MySQL Design Patterns?

Wicked

Новичок
MySQL Design Patterns?

После прочтения книжки Guide to PHP design patterns задумался, нет ли чего похожего для баз данных (в частности, для MySQL). Я имею в виду именно шаблоны организации структуры базы данных, а не шаблоны для программ для работы с бд (в число которых входят всякие DataMapper, ActiveRecord, etc).

А то на данный момент мне известны только всякие пэттерины (пэттерны ли?) типа "отношение многие ко многим".
 

HEm

Сетевой бобер
Паттерн
Систематически повторяющийся устойчивый элемент (фрагмент) или последовательность элементов (фрагментов).

(цитата правда не из ресурсов посвященных паттернам проектирования программ, но, имхо, самое точное определение самого понятия.)

В этом смысле наверное можно считать нормальные формы использованием паттернов проектирования (заезжаем в колею правильного проектирования и не выкатываемся из нее)
 

svetasmirnova

маленький монстрик
>В этом смысле наверное можно считать нормальные формы использованием паттернов проектирования
Тогда наследование тоже паттерн ;)
 

Wicked

Новичок
Если рассматривать пэттерны с точки зрения, что они состоят из:
* Имени пэттерна
* Описания частой проблемы, которую он решает
* Собственно решения
то может стоит отталкиваться как раз от существующих распространенных проблем, возникающих при дизайне бд...
 

vovik

Новичок
Тема интересная. Я не встречал нигде именно списка паттернов проектирования БД. Хотя они, несомненно, существуют :)

Например:
1. Nested Sets - для хранения деревьев.
2. EAV - для изменяющихся атрибутов сущностей.
3. Наследование сущностей (супертип-подтип). Тут все совсем просто. В таблицах-наследниках FK на родительскую таблицу совпадает с PK. Как ни странно, мало кто так делает.

Продолжайте список :)
 

Wicked

Новичок
Таблица-итератор.
Нигде ничего подобного не видел, так что считаю это своим изобретением.

Объясню на более простом примере.
Допустим, у нас есть таблица с интервалами дат `range`.
id | date_start | date_end
1 | 2006-01-01 | 2006-01-19 (длина 19 дней)
2 | 2006-01-06 | 2006-01-20 (длина 15 дней)

А нам из нее надо получить все 19+15 промежуточных дат. Тогда мы создаем таблицу-итератор `iterator` с единственным полем `i` типа int. И заполняем ее 19-ю (max(15, 19) в данном случае) значениями от 0 до 18.
Тогда чтобы сгененировать интересующие нас даты, делаем следующий запрос:
[sql]SELECT
range.id, range.date_start + INTERVAL iterator.i DAY as `date`
FROM
range
INNER JOIN iterator ON (range.date_start + INTERVAL iterator.i DAY <= range.date_end)
order by id, `date`[/sql]
"И вуаля" &copy; я.
id | `date`
1 | 2006-01-01
1 | 2006-01-02
...
1 | 2006-01-18
1 | 2006-01-19
2 | 2006-01-06
2 | 2006-01-07
...
2 | 2006-01-19
2 | 2006-01-20
 

vovik

Новичок
Автор оригинала: Wicked
Можно обобщить паттерн. Частенько возникают подобного рода задачи. Проблема заключается в том, что из БД нельзя выбрать данных, которых там нет. Решение - иметь под рукой таблицу с номерами от 1 до N. N зависит от потребностей. И джойнить ее.

Можно решать кучу задач. Например, вывод непрерывных всяких последовательностей. Или вывести пробелы в auto_increment.

Или, скажем, есть у нас таблица а-ля лог какой-нибудь. Нужно вывести кол-во с группировкой по месяцам. Вот если за какой-то месяц нужно вывести ноль (не было записей), то просто группировка не выведет ничего. А нужен ноль. Поможет опять же волшебная табличка с номерами :)


Паттерн хороший и красивый. Обычно те, кто с ним не знакомы - делают циклы на клиенте.

Есть еще альтернатива джойнить что-то вида (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 ........). Если эту штуку пару раз заCROSSJOIN-ить на себя, можно получить довольно большой набор номеров :)

-~{}~ 24.11.06 13:00:

Автор оригинала: vovik
Поясняю последнюю мысль, может и на паттерн потянет :)

Вот такой имелся в виду запрос. Копирайт не мой, взято где-то на форумах sql.ru.

select a*1000+b*100+c*10+d num from
(select 0 a union select 1 union select 2 union select 3 union select 4
union select 5 union select 6 union select 7 union select 8 union select 9) a
cross join
(select 0 b union select 1 union select 2 union select 3 union select 4
union select 5 union select 6 union select 7 union select 8 union select 9) b
cross join
(select 0 c union select 1 union select 2 union select 3 union select 4
union select 5 union select 6 union select 7 union select 8 union select 9) c
cross join
(select 0 d union select 1 union select 2 union select 3 union select 4
union select 5 union select 6 union select 7 union select 8 union select 9) d
 

Popoff

popoff.donetsk.ua
Паттерн "прослешь всех" для защиты от sql-инъекций любого порядка: все данные перед вставкой в запрос прослешивать, даже если они пришли из надёжного источника и проверены. Например, так:

mysql_query("update t_table set i_integer='".mysql_real_escape_string($i_integer)."'");

svetasmirnova
По такой же схеме, паттерн "выборка". Проблема: часто нужно что-то выбирать из базы данных. Решение проблемы: нужно писать select.

Думаю, к паттернам нужно относить способ решения задачи в случае, если есть альтернативы и мы выбираем одну из альтернатив как наиболее оптимальную для решения какой-то задачи. Например, наследование и выборка - нет вариантов, значит это не паттерны, а стандартные конструкции языка. Хранение деревьев (есть много разных способов), таблицы-итераторы (можно реализовать то же на РНР) и прослешивание (можно не прослешивать, надеясь на что-нибудь) - есть разные варианты. Значит, это паттерны.

Хотя.. Можно написать не ОО-программу без всякого наследования, которая будет в точности по поведению соответствовать ОО-программе с наследованием. С этой точки зрения, получается, наследование - это патерн. Сам по себе ООПодход - это паттерн.
 

Wicked

Новичок
vovik
Да, у меня тоже были мысли насчет a*1000+b*100+c*10+d, тока я это нигде это не использовал, т.к. числа обычно ограничивались :)

Проблема заключается в том, что из БД нельзя выбрать данных, которых там нет.
Золотые слова :)

Можно обобщить паттерн.
Это был всего лишь пример. И так понятно, что применений у него гораздо больше. Например, я использовал его в своем поисковике при генерации всех возможных смещений совпадения по тексту. А также для генерации событий, происходящих раз в неделю (уже в реальном проекте).

-~{}~ 24.11.06 16:32:

О версионности записей.
Когда доводилось делать версионность записей, пришлось использовать 2 таблицы:
1) items с полем:
`item_id` int auto_increment

2) items_rev с полями:
`rev_id` int auto_increment,
`item_fk` int,
`is_last_ver` bool, ... (или `ver_reverse_order` int, который равен 0 у последних записей, N - у первых, если у данного айтема N+1 версий)

Таблица items нужна была только для генерации auto_increment-а. А дополнительное поле `is_last_ver` / `ver_reverse_order` - для оптимизации выборки последних версий (ведь они используются чаще всего) без особых извращений, к тому же, используя индекс.

Не знаю, тянет ли это на пэттерн :)
 

svetasmirnova

маленький монстрик
>Проблема заключается в том, что из БД нельзя выбрать данных, которых там нет.

Как нельзя? А [sql]SELECT 'NOT EXISTENT' FROM some_table[/sql] ?
 

vovik

Новичок
Автор оригинала: svetasmirnova
Как нельзя? А [sql]SELECT 'NOT EXISTENT' FROM some_table[/sql] ?
Это придирка к словам или серьезный вопрос ? :)

Конечно, если в запросе что-то явно указать - это выбрать можно. Собственно, для этого и делается SELECT 1 UNION SELECT 2 .. как замена таблички с номерами.

Но можно на это и так посмотреть, что если мы в запросе указываем какие-то данные, то они уже как бы и в БД, просто хранятся не на жестком диске и только в контексте текущего запроса.
 

Sender

Новичок
[/quote]Проблема заключается в том, что из БД нельзя выбрать данных, которых там нет.[/QUOTE]
интуитивно чувствую :) надо выбрать те которые есть, а уже метод вывода должен их вывести как надо и преобразовать
 

Wicked

Новичок
Sender
1) иногда на клиенте это получается делать намного более громоздко / медленно.
2) иногда промеждуточные данные на клиента вообще не должны попадать (в моем поисковике, например, последовательно выполняется 5..7 insert_into_select_from запросов, и данные вытягиваются на сторону клиента только из самой последней таблицы).
 

Sender

Новичок
Wicked
по второму:
возможно ты и прав, но можно ли в твоем случае обойтись запросами с использованием конструкций наподобии:
DISTINCT( date )


BETWEEN range1 AND range2


SELECT ..., DATE_FORMAT( date, '%Y-%m' ) as date_ym FROM ... GROUP BY date_ym


я не встречал еще задачи которая бы требовала выборки несуществующих данных из БД, поконкретнее можно по твоей выборке? может получится ее оформить без выборки несуществующих данных? :) тогда признаю что моя интуиция никудышная...
 

vovik

Новичок
Автор оригинала: Sender
я не встречал еще задачи которая бы требовала выборки несуществующих данных из БД
Речь ведется о паттерне. Т.е. о способе решения задачи наиболее быстро, оптимально и красиво. Да, это можно сделать на клиенте. Но будет медленнее и с большим количеством кода. Только и всего.

Например, у нас есть табличка в которой фиксируются некоторые события. Есть поле дата данного события и дополнительные атрибуты.
Требуется вывести даты за год, в которые данное событие не происходило.

Вариант 1 (по паттерну). Один скуэл запрос с джойном. Выведет только то, что нам надо.

Вариант 2 (на клиенте). Тащим _ВСЕ_ даты. А потом в цикле начинаем хождения и проверки.

Сравните сами.
 

Sender

Новичок
vovik
поспорю еще немного :)
За последний год. Событие происходило не каждый день это явно. То есть записей которые вы тащите на клиент по первому варианту гораздо больше чем вы их потащите по второму варианту.

ИМХО, опять же интуиция, всегда лучше знать то что есть, а не чего нету. Придумать информацию можно на любом этапе разработки/вывода, а вот узнать существующую более проблематично если она потерялась на этапе MySQL
 

vovik

Новичок
Автор оригинала: Sender
Событие происходило не каждый день это явно.
Явно.

То есть записей которые вы тащите на клиент по первому варианту гораздо больше чем вы их потащите по второму варианту.
А вот этот домысел откуда ? Хорошо, я скажу, что по второму варианту дат будет больше чем по первому. Чем этот домысел хуже ?

Суть в том, что даты, которых не было - выводить придется по-любому, это условие задачи. Предлагается задачу решить одним скуэл запросом.

ИМХО, опять же интуиция, всегда лучше знать то что есть, а не чего нету. Придумать информацию можно на любом этапе разработки/вывода, а вот узнать существующую более проблематично если она потерялась на этапе MySQL
По поводу этого даже не знаю что сказать. Не могу уловить никакого смысла. Что и куда теряется ?


Паттерн имеет право на жизнь. Если для какой-то конкретной задачи он не подходит - его не надо использовать. Только и всего.
Сформулируй пожалуйста, что ты пытаешься доказать или опровергнуть ? :)
 

Sender

Новичок
vovik
)) сформулирую
Подход: выборка несуществующих данных из MySQL на основе существующих неверна. Правильнее выбрать существующие и при конечной отдаче сформировать несуществующие :) вот :) именно при конечной отдаче

почти переубедил ты меня :) интуиция уже говорит что и так и так имеет право на жизнь :) только я бы все равно выбрал существующее
 
Сверху