понедельник, 28 июля 2008 г.

Некоторые правила построения баз данных

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

Нормализация

Итак, нормализация нам нужна для повышения еффективности нашей базы. Сразу следует оговоириться - говоря "база" я подразумеваю совокупность таблиц. Нормализуется именно вся база, при этом вносятся изменения в отдельные таблицы, при необходимости создаются новые, устанавливаются связи между ними и т.д.
Так вот , нормализация - это проце приведения нашей базы к нормальным формам. Их несколько, приведение происходит последовательно. При этом нужно знать, когда остановиться. Об этом чуть позже. А пока знайте, что нормальных форм существует 6 (известных, никто не мешает вам придумать седьмую), но обычно останавливаются на третьей, иногда на второй. Впрочем, при определенном опыте вы с ходу будете разрабатывать базы такими, что они сразу будут удовлетворять третьей нормальной форме. Теперь подробнее.

Первая нормальная форма

Первая нормальная форма требует, чтобы каждое поле таблицы было неделимым и не содержало повторяющихся групп.
Неделимость означает, что ваше поле не должно делиться на более мелкие. Например, речь идет о базе сотрудников для отдела кадров. Будет ли поле "Фамилия" неделимым? Ясно, что будет, куда же его еще делить. А поле "ФИО"? Тут все зависит от контекста - если вы не предполагаете использовать потом экзотические запросы типа "Выбрать всех Александровичей", то тоже можно считать его неделимым. А вот адрес лучше разделить на улицу, дом и квартиру, т.е. на 2 или 3 поля, тогда вам будет проще выбрать всех живущих на одной улице. Хотя, можете и его считать неделимым, если адрес у вас - второстепенная информация. А вот с телефоном его объединять не стоит. Короче, как у Энштейна, все относительно ;)
Непосторяемость означает, что мы не повторяем значение полей от одного поля к другому. Имеется ввиду не та ситуация, когда мы тупо указали несколько полей с одинаковыми значениями. Например вы делаете записную книжку. Какие поля вы туда внесете? Фамилия, имя, телефон, e-mail... А может несколько телефонов? В наше время почти у каждого есть и рабочий, и домашний, и мобильный... А у некоторых может быть несколько рабочих или несколько мобильных номеров. А сколько e-mail адресов можно себе завести я думаю каждый знает... Так сколько таких полей ввести? Правильнее всего будет создать еще одну таблицу с телефонами, а в нашей только поместить ссылку на нее. Тогда можно будет для каждого указать произвольное число телефонов.

Код Фамилия Адрес....
1 Иванов
2 Петров
Чей Какой Номер
1 Рабочий 1 23-45-67
1 Рабочий 2 23-45-98
1 Домашний 11-34-98
2 Домашний 45-09-87
... ... ...

Этот процесс называется разбиением таблицы на главную и подчиненную. Эти две таблицы аходятся в отношении "многие-к-одному". То есть многим телефонам соответствует только один человек. Еще бывают отношения "один-к-одному", "один-ко-многим" и "многие-ко-многим". По названиям должно быть понятно, что имеется ввиду.
В данном случае главная - таблица с телефонами. Хотя тут все зависит от контекста - в одних ситуациях одна главная, в других - другая. Каждая из подчиненных таблиц сама может иметь подчиненные. Как и главная может сама подчиняться какой-нибудь. В реляционных база данных все это может меняться в зависимости от ситуации как угодно.

Вторая нормальная форма

Вторая нормальная форма требует, во-первых соответствия первой нормальной форме. А во-вторых, чтобы каждая строка таблицы однозначно и неизбыточно определялась первичным ключем. Здесь два варианта - простой и очень простой.
Первый - если у нас накладные нумеруются каждый день начиная с первой, то номер не может однозначно определить накладную, и не может быть первичным ключем. А номер вместе с датой - может. Можно поискать первичные ключи среди имеющихся полей (номер пасспорта в базе сотрудников), а можно попытаться объединить несколько, пытаясь обеспечить уникальность.
Очень простой вариант - создаем поле-счетчик и указываем его в качестве первичного ключа. Все, требование выполнено.

Третья нормальная форма

Третья нормальная форма требует, во-первых, соответствия второй (а та - первой). А во-вторых - чтобы значение любого поля, не входящего впервичный ключ не зависело от других полей, не входящих в первичный ключ. Поясним.
Ну, от первичного ключа все зависят, потому такие оговорки. Что означает остальное? Если у нас есть ведомость начисления зарплаты:

Таб № Фамилия Должность Сумма
1 Иванов Директор 100
2 Петров его зам 90

то поля "Фамилия" и "Должность" - лишние, потому, что мы эти данные можем взять из таблицы для отдела кадров. То есть при печати эти поля будут, а вот в бази их хранить не следует.

Нормальная форма Бойса-Кодда, четвертая и пятая нормальные формы

Знаете как звучит определение нормальной формы Бойса-Кодда, приведенное у Дейта? Цитирую: "Отношение находится в нормальной форме Бойса-Кодда тогда и только тогда, когда каждая нетривиальная и неприводимая слева функциональная зависимость обладает потенциальным ключом в качестве детерминанта" или "Отношение находится в нормальной форме Бойса-Кодда тогда и только тогда, когда детерминанты являются потенциальными ключами". Не понятно? Объясняю.
Считается что первых трех нормальных форм достаточно для большинства практических применений. Помните, я говорил. что надо знать меру? Пора останавливаться. Если вы собрались писать диссертацию на эту тему, поищите серьезную литературу. Но если вы только осваиваете программирование баз данных - не забивайте себе голову.

Где пора остановиться?

Как мы рассматривали выше, при нормализации увеличивается количество таблиц и, соответственно, ссылок между таблицами. Пока у вас их немного - все в порядке. Но при усложнении базы может наступить момент, когда вы будете просто не в состоянии удержать в голове все взаимосвязи. Это значит, пора остановиться и заняться денормализацией.
Другая сторона - при каждом шаге по вашей таблице компьютер должен будет найти соответствующую запись в подчиненной таблице, затем в подчиненной более низкого уровня и т.д. Это несколько снижает производительность. При наличии правильно построенных индексов это не сильно заметно, но иногда может быть важно. Тут многое зависит от реализации самой СУБД. Например в 1С иногда здорово помогало пожертвовать третьей нормльной формой и ввести изыточную информацию в главную таблицу для того, чтобы ей не приходилось скакать по подчиненным таблицам - очень уж неэффективно она работает.

Индексирование

Сейчас я хочу обратить ваше внимание на правильную стратегию индексирования. Тема не очень сложная, ее мы рассмотрим в качестве отдыха после нормализации ;)

Индекс по первичному ключу позволяет быстро найти нужную запись. Это пожалуй самый быстрый способ перейти к нужной записи. Кроме того он незаменим при связывании таблиц. Есть и другие достоинства у индексов по первичным ключам. Так что возьмем за правило - каждой таблице по первичному ключу.

Delphi умеет использовать существующие индексы при поиске или сортировке. SQL Server тоже. А вот если приходится искать по неиндексированному полу, SQL Server переходит в режим сканирования. То есть просто пробегает по каждой записи. Попробуйте однажды поискать слово в словаре не по алфавиту, а просматривая все слова, пока не встретите нужное. Представили? А вот был бы индекс - все было бы в сотни или тысячи раз быстрее. Возьмем за правило - поля, по которым часто приходится искать, должны быть проиндексированы

При каждом обновлении таблицы индексы тоже модифицируются. Если их слишком много, это займет много времени. Понятие "слишком" сильно зависит от СУБД, но обычно считается, что 5 индексов на таблицу - нормально. Больше не стоит. Правило - не загромождайте таблицу лишними индексами

При связывании таблиц нам нужно, чтобы при перемещении по главной таблице записи в подчиненной искались как можно быстрее. Поле по которому мы связываем таблицы называется внешним ключем. Хотя оно и называется "ключем", индекс по нему не нужен - никто по нему не ищет. Итак следующее правило - индексы для внешних ключей как правило не нужны

Наблюдение последнее - все что описано выше, и о нормализации, и о правилах индескирования соблюдать совсем не сложно. В моем десятилетнем опыте работы с базами данных не было случая, чтобы мне не удалось их соблюсти. Чаще всего это происходит само собой. Так что, если кто успел испугаться - расслабьтесь ;)

Комментариев нет: