Прочная конструкция базы данных

Прочная конструкция базы данных

Алекс С.*

 

«Я считаю, что самая сложная часть построения программного обеспечения - это спецификация, дизайн и тестирование этой концептуальной конструкции, а не процесс ее презентации и тестирование точности воспроизведения презентации.» - Фредерик П. Брукс мл.

В этом пособии будут описаны концепции, лежащие в разработке практических схем баз данных. Это наиболее применимо к реляционным базам данных, но может так же легко быть применено к объекту базы данных.

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

Имейте в виду, что время от времени, этот учебник «обманывает», насколько осведомлена правильная нормализация базы данных (если есть такая вещь). Другими словами, теоретик реляционной базы данных надуется на вас за следование этому руководству.

1 Почему это так сложно?

Базы данных организовывают и хранят информацию. Тем не менее, прежде чем они могут сделать это, мы, как люди, должны предоставить им структуры для этой информации. То есть, мы, как люди, должны выяснить сами структуру - и это то, где мы, как люди, очень часто застреваем и / или делаем много неправильных решений.

Вы, наверное, заметили мой акцент на людей в последних абзацах. У большинства людей возникает проблема, когда дело доходит до организации их собственных мыслей! Не говоря уже об организации своих мыслей о том, как должна быть организована некоторая абстрактная информация. Как вы можете себе представить, процесс полон ловушек (и обычно множеством разочарований).

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

Сделать хороший концептуальный дизайн трудно. Еще труднее сделать так, чтоб этот дизайн работал с реальностью.

2 Где все это начинается?

Проектирование баз данных (а также любой вид дизайна) начинается с требований. Мы должны знать, что мы строим, с какой информацией мы имеем дело, и как мы с этим справляемся.

*alex@theparticle.com

Это не является документом по сбору требований (что есть удивительно трудной активностью самой по себе), так что мы просто пропустим это и предположим, что вы знаете, что вы строите и что это за информация, с которой вы имеете дело (и то, что вам нужно сделать с этой информацией). Во всяком случае, процесс определения схемы базы данных начинается с объектов.

2.1 Objects

Зная информацию, которой ваша система будет заниматься, вы можете начать процесс идентификации объектов. Они похожи, но не совсем те же объекты, что и в «объектно-ориентированном программировании». Эти объекты являются отдельными концептуальными объектами, которые существуют сами по себе. Обычно, если вы можете коснуться его, это объект. Если вы можете описать что-то, не обращаясь ни к чему другому, то, это, как правило, объект тоже. Объекты зачастую существуют в течение длительного периода времени.

Составьте список всех объектов, о которых вы можете думать, из требований. Будьте осторожны, чтобы не включать такие вещи, которые не могут существовать сами по себе.

Так, начиная с дизайна нашей онлайновой базы данных магазина, «КЛИЕНТ» может быть объектом; «ПРОДУКТ» также может быть объектом. Но «ЦЕНА» не является объектом, потому что она не может существовать сама по себе, она должна быть «ЦЕНА» чего-то (и это что-то весьма вероятно, будет объектом).

Таким образом, предварительный список объектов для интернет-магазина может быть:

• ПОКУПАТЕЛЬ - лицо, которое покупает что-то.

• ПРОДУКТ - продукт, который клиент может хотеть купить.

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

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

2.2 События

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

Событие – это взаимодействие между одним или несколькими объектами в определенное время. События не могут существовать сами по себе (в противном случае они были бы объектами). События должны записывать время (или какую-либо другую подобную информацию) их возникновения, чтобы дифференцировать себя от других подобных событий, но происходящих в разное время. Конечно, они могут также записывать другую информацию о собственном возникновении.

Расширяя наш предыдущий пример, онлайн-магазин, «ПОКУПКА» является очевидным событием. Там могут быть и другие, и, если мы думаем о чем-то, мы добавим его позже.

Если мы не уверены, является ли нечто предметом или событием, вы можете посмотреть, требует ли это временной метки. Если что-то происходит в какой-то временной отрезок, то очень вероятно, что это событие. Это не означает, что вы не можете иметь метку на объекте – объекты могут иметь время создания.

Теперь у вас есть список всех объектов и событий, давайте перейдем к свойствам.

2.3 Свойства

Свойства - это детали или характеристики наших объектов и событий. А объекты покупателя могут иметь «Имя» и «Фамилию», к примеру. События также имеют свойства: время, когда они произошли!

Давайте рассмотрим наши объекты и события и добавим детали к ним:

ПОКУПАТЕЛЬ

 

ПРОДУКТ

 

ПОКУПКА

ИМЯ

 

ОПИСАНИЕ

 

ПОКУПАТЕЛЬ

ФАМИЛИЯ

 

ЦЕНА

 

 

ПРОДУКТЫ

АДРЕС

 

ВРЕМЕННАЯ МЕТКА

ТЕЛЕФОНЫ

 

 

Очевидно, что мы можем добавить больше деталей (например, продукт «цвет» и т.д.), но давайте пока оставим его в этом состоянии (нет необходимости усложнять пример). Обратите внимание, что ПОКУПКА имеет время покупки, и того, кто сделал покупку (ПОКУПАТЕЛЬ) и то, что они купили (кучу ПРОДУКТОВ).

Хорошо, теперь, когда с этим разобрались, вот несколько «предложений»: Каждый объект и событие должно иметь уникальный идентификатор (это одна из первых вещей, которой теоретик будет расстроен). Всякий раз, когда мы говорим, что событие ПОКУПКИ "имеет" ПОКУПАТЕЛЯ, то мы имеем в виду, что ПОКУПКА имеет идентификатор клиента, который однозначно идентифицирует конкретного клиента.

Более детальный вид наших таблиц (угу, я употребил слово на букву Т):

ПОКУПАТЕЛЬ

 

ПРОДУКТ

 

ПОКУПКА

ПОКУПАТЕЛЬ ID

 

ПРОДУКТ ID

 

ПОКУПКА ID

ИМЯ

 

ОПИСАНИЕ

 

ПОКУПАТЕЛЬ

ФАМИЛИЯ

 

ЦЕНА

 

ПРОДУКТЫ

АДРЕС

 

ВРЕМЕННАЯ МЕТКА

ТЕЛЕФОНЫ

 

 

 

Это больше походит на вот это. Теперь становится яснее, что ПОКУПКА имеет только идентификатор ПОКУПАТЕЛЯ, а не самого ПОКУПАТЕЛЯ.

 

2.4    Повторяющиеся Свойства

Вас, вероятно, смутило (или должно было смутить) из предыдущего примера то, что у нас есть это свойство «ПРОДУКТЫ» в таблице ПОКУПКА. Нет, мы не можем оставить количество продуктов просто так. Мы должны распределить его на отдельные таблицы.

Ключ к концепции заключается в том, что мы вводим таблицу ПОКУПКА_ДЕТАЛИ, которая будет содержать подробную информацию об отдельных продуктах. Таблица будет выглядеть следующим образом; наряду с новой таблицей ПОКУПКА:

 

ПОКУПКА_ДЕТАЛИ

 

ПОКУПКА

ПОКУПКА_ДЕТАЛИ ID

 

ПОКУПКА ID

ПОКУПКА ID

 

ПОКУПАТЕЛЬ ID

ПРОДУКТ ID

 

ВРЕМЕННАЯ МЕТКА

 

В случае, если нам надо знать, какие продукты приобрели, мы просто смотрим это в таблице ПРОДУКТ_ДЕТАЛИ, используя ПОКУПКА ID или ПОКУПКА. Четко, не так ли?

Теперь, не все повторяющиеся свойства заслуживают такого пристального внимания. Например, некоторые из них могут быть установлены просто путем перечисления нескольких копий определенной области. Обратите внимание, что в нашей таблице ПОКУПАТЕЛЬ у нас есть ТЕЛЕФОНЫ (во множественном числе). Как мы можем исправить это без введения другой таблицы? Я думаю, что вы уже получили представление - мы просто перечислим различные телефоны, какие ПОКУПАТЕЛЬ может иметь:

ПОКУПАТЕЛЬ

ПОКУПАТЕЛЬ ID

ИМЯ

ФАМИЛИЯ

АДРЕС

ДТЕЛЕФОН

РТЕЛЕФОН

МТЕЛЕФОН

ФТЕЛЕФОН

 

 

Мы просто заменяем «ТЕЛЕФОНЫ» на ДТЕЛЕФОН (для домашнего телефона), РТЕЛЕФОН (для рабочего телефона), МТЕЛЕФОН (для мобильных телефонов) и ФТЕЛЕФОН (для факса). Если у клиента нет ни одного из них, мы просто ставим его на НОЛЬ. Ничего страшного. С этим немного проще иметь дело, чем с отдельной таблицей - к сожалению, мы не всегда можем сделать так, как это показано в примере выше ПРОДУКТ_ДЕТАЛИ.

 

3 Почти готово!

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

3.1 Свойство История Значений

Несколько редким, но очень интересным требованием является необходимость поддержания истории значений определенного свойства. Например, наша таблица ПРОДУКТ имеет ЦЕНУ. Что делать, если продукт поступит в распродажу? Покупки, сделанные до распродажи должна быть оформлены по неотпускной цене, так же, цена распродажи должна быть применена к покупкам, сделанным во время распродажи.

Есть несколько способов обработки этого требования. Мы можем сохранить копию цены продажи вместе с ПОКУПКА_ДЕТАЛЬ для этого ПРОДУКТА. Тогда вы просто изменяете ПРОДУКТ.ЦЕНА по желанию, и увидите клиентов, приобретающим по текущим ценам. Эта конструкция, в то время как она работоспособна, все же немного слишком утомительна, если вам нужно проанализировать ценовые истории. Для того, чтобы наблюдать ценовую историю, нужно просмотреть ПОКУПКи.

Что делать, если у вас какой-то пункт на распродаже, и никто его не купил; как вы узнаете, что цена вообще была снижена? (и на сколько?).

Когда сталкиваетесь с такой ситуацией, хороший вариант поддерживать цену на ПРОДУКТ вместе с ПРОДУКТОМ. Таким образом, мы настраиваем ПРОДУКТ, чтобы он выглядел вот так:

 

ПРОДУКТ

ПРОДУКТ ID

ОПИСАНИЕ

ЦЕНЫ

 

И двигаемся дальше, применяя нашу технику Повторяющихся свойств, из этого получается:

 

ПРОДУКТ

 

ЦЕНА_ИСТОРИЯ

ПРОДУКТ ID

ОПИСАНИЕ

 

ЦЕНА_ИСТОРИЯ ID

ПРОДУКТ ID

ЦЕНА

СТАРТ_ВРЕМЯ

 

 

 

Обратите внимание, что в дополнение ко всем штукам Повторяющихся Свойств, мы также добавили "СТАРТ_ВРЕМЯ". Это означает начало этой цены. Таким образом, чтобы найти текущую цену, все, что нужно сделать, это посмотреть на последний пункт ЦЕНА_ИСТОРИЯ.

(Обратите внимание, что вы можете рассмотреть вопрос об изменении цен в качестве события, которое может произойти в ПРОДУКТЕ, и в конце концов, вы закончите на более или менее том же интерфейсе таблицы).

3.2 Отношения объектов

Другой относительно распространенной является ситуация, когда объекты имеют отношения к другим объектам. Рассмотрим базу данных с объектами-людьми, где ЧЕЛОВЕК-объект может иметь семейные отношения с другим (и) ЧЕЛОВЕКОМ-объектом.

Эти типы отношений очень похожи на события - за исключением того, что они являются долгосрочными. События происходят в какой-то момент времени, и заканчиваются. Объектные отношения формируются также в какой-то момент (так что у вас может быть начальная дата), и могут быть уничтожены в какой-то момент в будущем (так что вы можете иметь конечную дату).

Например, “ЖЕНАТ” or “РОДИТЕЛЬ” и т.д.

Вы также должны быть осторожны, чтобы не дублировать информацию. Например, если вы определяете “ЖЕНАТ” как:

 

ЖЕНАТ

ЖЕНАТ ID

ЧЕЛОВЕК1_ID

ЧЕЛОВЕК2_ID

СТАРТ_ДАТА

КОНЕЦ ДАТА

 

Затем, когда вы хотите найти информацию о том, состоит ли "Джон" в браке с "Джейн", у вас "Джон" является ЧЕЛОВЕК1_ID или ЧЕЛОВЕК2_ID? У вас есть двусторонние отношения - есть две записи ЖЕНАТ, в сущности говоря Джейн вышла замуж за Джона, а Джон женат на Джейн). В любом случае, у вас компромисс. Если только односторонние отношения, то вам нужно подключать логику приложения, чтобы справиться с оборотным делом.

Другой вариант, который позволяет избежать этой проблемы, заключается в реализации этих Повторяющихся свойств в отдельной таблице (в отличие от простого перечисления двух функций ЧЕЛОВЕК ID в таблице ЖЕНАТ). В любом случае, у вас получается компромисс (в данном случае, скорость доступа к дополнительной таблице).

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

Еще одна важная вещь, которую стоит понять, это что отношения Объект-Объект не могут быть затребованы. Объект может существовать сам по себе! Если вы окажетесь перед необходимостью затребовать отношения, то вам нужно пересмотреть то, что вы считаете в качестве объекта, и что в качестве свойств этого объекта.

4 Оптимизация

НЕ НАЧИНАЙТЕ ОПТИМИЗАЦИЮ, ПОКА ВЫ ПОЛНОСТЬЮ НЕ УДОВЛЕТВОРЕНЫ ДИЗАЙНОМ!

Реляционные базы данных разработаны красиво. Ваш дизайн должен работать, и быть очень красивым (по крайней мере для вас). Большинство оптимизаций имеет тенденцию к увеличению скорости, но за счет красоты дизайна. Если вы преждевременно начнете оптимизацию, в конечном итоге вы будете просто с плохо спроектированным беспорядком.

Некоторые методы: добавление индексов в соответствующих местах, уменьшение количества таблиц, введение избыточности (есть несколько других, но в основном все они вращаются вокруг этих трех).

4.1 Добавление индексов

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

(Индексы – это специальные файлы базы данных, которые содержат значения столбцов в определенном порядке (B-деревья), что позволяет легко найти запись в логарифмическом времени).

Только представьте, сколько бы вам понадобилось, чтобы найти имя в несортированной телефонной книге? Много раз, базы данных сталкиваются с таким вот вызовом. Индекс обеспечивает отсортированное представление данных делая данных просмотр довольно тривиальным доступом к диску (в отличие от, поиска сквозь миллионы записей, база данных может только глянуть на несколько, чтобы найти запись).

Если ваша база данных вялая, добавление индексов в соответствующих полях будет, скорее всего, признаком повышения производительности.

Теперь, что это за соответствующие поля (или колонки)? Если в любое время вы делаете поиск по значению некоторой области, вы определили хорошего "кандидата" в индексы.

Большинство первичных и внешних ключей также должны быть проиндексированы (и большинство баз данных сделает это по умолчанию). Если вы когда-нибудь делали поиск по датам, то индексируйте и их тоже.

Добавление индекса, который вам не нужен, не уменьшит ВЫБОРа показатели - скорость, с которой вы извлекаете записи, но может серьезно повлиять на ВВОД, ОБНОВЛЕНИЕ или УДАЛЕНИЕ показателей, так как индексы должны быть вставлены и удалены вместе с записью. Следите за этими компромиссами.

Имея это в виду, вы не должны сходить с ума и добавлять индексы на все без тщательного понимания того, почему вам они нужно там. В то время как индексы повышают производительность извлечения, они также тратят пространство. Индексы могут легко занять от 10% до 50% от общего пространства, используемого базой данных.

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

4.2 Снижение количества таблиц

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

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

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

Это говорит (мы надеемся, это звучало достаточно предостерегающе), вот некоторые вещи, касательно рассмотрения вопроса о сокращении:

Отношения Объект-Объект, как описано выше, реализованы через отдельную таблицу (часто называемую «соединяющая» таблица). Если отношения многие-ко-многим, то это подходящий способ сделать это. Если отношения один-к-многим, то вы можете устранить таблицу, и просто получить идентификационное поле в одной таблице.

Едем дальше, если отношения один-к-одному, вы могли бы полностью поглотить одним объектом другой. Это, как правило, происходит в случае с Обобщением (или Специализацией) отношений.

Например, база данных, которая имеет ЖИВОТНОЕ и МЛЕКОПИТАЮЩЕЕ (что является специализацией животного) и может объединить все поля с МЛЕКОПИТАЮЩИМИ в таблицу ЖИВОТНЫХ.

Кроме того, если две таблицы достаточно похожи, вы можете объединить их в одну таблицу: таблицу СОБАКА и таблицу КОТ можно объединить в таблицу ДОМАШНИЙ ЛЮБИМЕЦ, которая будет иметь все поля для СОБАК и КОШЕК и поле ТИП, которое позволит приложению знать то, с чем мы имеем дело.

Возможности бесконечны - просто помните, не переусердствуйте, и по-прежнему поддерживайте хороший дизайн.

4.3 Избыточность данных

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

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

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

Резервные данные должны быть строго только для чтения. Обновления должны происходить только от исходных данных. Источник Редактирования должен обновлять избыточные данные.

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

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

Позвольте мне повторить (и подчеркнуть этот момент): Избыточные данные должны быть доступны только для чтения! Резервные данные должны быть задокументированы! Порядок обновления должен быть задокументирован! Процедуры восстановления также должны быть задокументированы!

Теперь, после выполнения всех рекомендаций, вы все равно должны быть готовы к непоследовательности. Это произойдет не смотря ни на что (в соответствии с Мерфи). Кто-то обновляет значение, но все еще видит старое значение на экране. Убедитесь, что избыточные данные не являются критическим, и могут оставаться несоответствующими в течение некоторых периодов времени.

Хорошими кандидатами для избыточности являются: итоги, суммы, среднее или любое значение базы данных (или ваше приложение) вычисляемое из данных уже в базе данных.

В качестве примера, давайте рассмотрим нашу дилемму цены ТОВАРОВ. Большую часть времени мы просто хотим увидеть цену, но также мы все еще хотим сохранить историю изменения цен. Ранее мы разбили эту идею на две таблицы, говоря, что одна из них будет поддерживать историю области ЦЕНА. Это все хорошо, за исключением того, что требуется дополнительный подзапрос (и более сложная логика приложения), чтобы получить текущую цену.

Нам потребуется ценовая история, возможно, один раз в неделю, но текущая цена нам, вероятно, понадобится каждые несколько минут. Что мы делаем? Ну ... Мы определяем:

 

ПРОДУКТ

 

ЦЕНА_ИСТОРИЯ

ПРОДУКТ ID

 

ЦЕНА_ИСТОРИЯ ID

ОПИСАНИЕ

 

ПРОДУКТ ID

ЦЕНА

 

ЦЕНА

 

СТАРТ_ВРЕМЯ

 

где ПРОДУКТ.ЦЕНА является излишним полем последней цены, полученной из нахождения последней записи в таблице ЦЕНА_ИСТОРИЯ для этого ПРОДУКТа ID. Теперь, при среднем использовании, мы не должны даже предполагать, там ли еще таблица ЦЕНА_ИСТОРИЯ. Мы имеем дело с ПРОДУКТами и их ЦЕНами.

Однако, когда нам необходимо изменить цену, мы должны знать (и это должно быть четко задокументировано), что мы должны добавить цену в таблицу ЦЕНА_ИСТОРИЯ. В тот момент, либо периодический процесс, либо хранимая процедура (или просто наша логика приложения) повторно вычислит цену для таблицы ПРОДУКТ.

4.3.1 Денормализованные либо Соединенные Таблицы

Другой способ приобретения полезной избыточности возможен посредством сохранения таблиц в уже соединенной форме. Например, предположим, что у вас есть две таблицы, «заказы» и «сделки». Предположим, что каждый раз, когда вы посмотрите на «сделки», вы почти всегда должны присоединить их к «заказам». Предположим также, что эти две таблицы огромные (миллиарды записей). Так же ужасно, как это может показаться, лучший способ для хранения таких данных может быть предварительное соединение таблиц: используйте таблицу (не вид), которая будет естественным соединением между «заказами» и «сделками». Таким образом, вы можете добраться к заказам с незначительной фильтрацией, и можете добраться к торгам (вместе с информацией заказа).

Этой методикой не следует злоупотреблять!

 

5 Формальные методы

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

Просто некоторые термины, которые могут не показаться очевидными сразу же: «отношение» в «реляционной базе данных» относится к взаимосвязи между атрибутами в одном ряду. Например, в таблице «человек» столбец «имя» относится к колонке «фамилия» и т.д. Реляционная не относится ко множеству таблиц (распространенное заблуждение).

5.1 Ключи, Суперключи, Первичные ключи

Суперключ для отношения к R представляет собой набор атрибутов S ≤ R такое, что нет двух различные кортежей t1 и t2 будут иметь t1 [S] = t2 [S]. По сути суперключ это то, что делает кортеж отличным от любого другого кортежа, то есть: суперключ может быть всеми столбцами.

Ключ - это минимальный суперключ, в смысле, что удаление любого атрибута приведет к тому, что он перестанет быть суперключом.

Любой набор атрибутов, которые включает в себя ключ, также является суперключом.

Отношение может иметь более одного ключа. Они тогда называются возможные ключи.

Один из ключей-кандидатов выбирается, чтобы быть первичным ключом. Каждое отношение должно иметь первичный ключ.

5.2 Функциональные зависимости

Функциональная зависимость является ограничением на значения атрибутов по всем кортежам. Если S → Т будем говорить, что Т функционально зависит от S. Это означает, что для любых двух кортежей t1 и t2, если у нас есть t1 [S] = t2 [S], то мы также должны иметь t1 [T] = t2 [Т].

Примером могут быть города и почтовые индексы. Если две записи имеют одинаковый почтовый индекс, можно с уверенностью предположить, что они имеют один и тот же город. В этом случае, город функционально зависит от почтового индекса.

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

Существует понятие «нормальных форм», который часто1 используется, чтобы формально спроектировать и оценить качество дизайна баз данных.

Есть пять2 нормальных форм. Они пронумерованы - достаточно удобно - от одного до пяти. Есть также множество других промежуточных форм, названных в честь того или другого (обычно того, что пришло с ним).

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

Как правило, вы озабочены только первыми тремя нормальными формами - и в этом документе, мы также будем иметь дело с нормальной формой Бойс-Кодда, которая является своего рода «Нормальной формой 3.5».

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

Это самая основная нормальная форма, и единственным требованием является то, что данные хранятся в таблицах. Если ваши данные хранятся в таблицах, то вы достигли первой нормальной формы.

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

Если база данных имеет столбцы, содержащие значения, разделенные запятыми, это, как правило3, признак того, что база данных не нормализована.

Мы обсудили это в разделе 2.3 «Повторяющиеся Свойства» выше.

_____________________

Проще говоря никогда

Или более

Всегда есть исключения, когда воплощаешь такие вещи в жизнь.

 

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

Хорошо, вот оно:

«База данных находится на второй-нормальной форме, если она находится в первой нормальной форме, и каждый атрибут полностью функционально зависит от первичного ключа.»

А теперь, чтобы объяснить это: первичные ключи - это поля, которые уникально идентифицируют запись. Атрибуты являются всем остальным в записи.

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

Полная в функциональная зависимость в основном относится к композиционным первичным ключам. Это в основном означает, что атрибут должен быть функционально зависящим от всего первичного ключа (не одного из его частей). Например, некоторые приложения используют имя, фамилию и дату рождения как составное первичного ключа. Каждый атрибут в этой записи должен зависеть от всего -  фамилии, имени и даты рождения. Предположим, что база данных также же имела столбец «возраст». Этот столбец будет зависеть только от даты рождения (то есть: части ключа).

 

 

6.2.1 Дизайн

Очень легко достичь второй нормальной формы, просто выбирая уникальный абстрактный первичный ключ, который не зависит от данных, то есть: добавление автоинкрементного первичного ключа к вашим таблицам. Осторожно, этим можно (и часто так и делают) злоупотреблять. Если описанное выше не может быть сделано, обычный способ достичь 2NF - это разбить оригинальную связь на множество мелких взаимосвязей с меньшими ключами с соответствующими функциональными зависимостями.

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

«База данных находится в третьей нормальной форме, если она находится во второй нормальной форме и не содержит переходных атрибутных зависимостей.» Данная форма похожа на 2NF, за исключением того, что мы хотим избежать переходных функциональных зависимостей. Например, в то время как Х → Y не может присутствовать (т.е. 2NF), у нас могли бы быть еще ситуации, подобные X → Z → Z и Y, которые могли бы привести к тому, что связь не будет в 3NF.

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

6.4 Нормальная форма Бойс-Кодда

«Отношение R находится в нормальной форме Бойс-Кодда, если его первичный ключ, K, влечет за собой все не ключевые атрибуты - а, в, с ...- и К является суперключом.»

Вот еще один способ сказать то же самое:

«Отношение R находится в BCNF если всякий раз, когда нетривиальная функциональная зависимость X → Л имеет место в R, то X является суперключом от R.» В основном, если мы выбираем новый уникальный идентификатор для каждой записи (автоинкремент, и т.д.) и выбираем поля "соответственно", то мы получим Бойс-Кодда нормальную форму.

Нормальная форма Бойс-Кодда не совсем вписывается в идею пронумерованных нормальных форм: она начинается с нормальной формы (предполагается, что данные в таблицах). В действительности, она была первоначально сформулирована как упрощение 3NF, а затем было обнаружено, что она немного жестче и было доказано4, что Бойс-Кодд также в третьей нормальной форме. Таким образом, мы можем рассматривать Бойс-Кодд как 3,5-нормальную форму.

7 Заключение

Ключ (хех) за всеми вышеперечисленными нормальными формами: избегать дублирования данных. Если есть способ, чтобы получить значение некоторого поля без явного хранения его, то не храните его. Вот так вот просто.

 

__________________________

4           Кем-то.

Оригинал статьи на английском http://www.theparticle.com/documents/database_design.html