Решение между хранением идентификаторов таблицы поиска или чистых данных - PullRequest
14 голосов
/ 20 декабря 2008

Я считаю, что это очень важно, и я не уверен, что лучший способ подойти к этому.

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

  • Со вторым методом вы бы нужно делать массовые обновления для всех записи, ссылающиеся на данные, если это изменяется в справочной таблице.

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

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

Есть ли здесь лучшая практика или какие-то ключевые моменты для рассмотрения?

Ответы [ 7 ]

27 голосов
/ 20 декабря 2008

Вы можете использовать справочную таблицу с первичным ключом VARCHAR, а ваша основная таблица данных использует FOREIGN KEY в своем столбце с каскадными обновлениями.

CREATE TABLE ColorLookup (
  color VARCHAR(20) PRIMARY KEY
);

CREATE TABLE ItemsWithColors (
  ...other columns...,
  color VARCHAR(20),
  FOREIGN KEY (color) REFERENCES ColorLookup(color)
    ON UPDATE CASCADE ON DELETE SET NULL
);

Это решение имеет следующие преимущества:

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

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


Комментарий от @MacGruber:

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

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

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

4 голосов
/ 20 декабря 2008

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

CREATE TABLE Hat (
  hat_id INT NOT NULL PRIMARY KEY,
  brand VARCHAR(255) NOT NULL,
  size INT NOT NULL,
  color VARCHAR(30) NOT NULL /* color is a string, like "Red", "Blue" */
)

Или вы можете нормализовать его, сделав таблицу "color":

CREATE TABLE Color (
  color_id INT NOT NULL PRIMARY KEY,
  color_name VARCHAR(30) NOT NULL
)

CREATE TABLE Hat (
  hat_id INT NOT NULL PRIMARY KEY,
  brand VARCHAR(255) NOT NULL,
  size INT NOT NULL,
  color_id INT NOT NULL REFERENCES Color(color_id)
)

Конечным результатом последнего является то, что вы добавили некоторую сложность - вместо:

SELECT * FROM Hat

Теперь вы должны сказать:

SELECT * FROM Hat H INNER JOIN Color C ON H.color_id = C.color_id

Это дополнительное присоединение к огромной сделке? Нет, на самом деле это основа реляционной модели проектирования - нормализация позволяет предотвратить возможные несоответствия в данных. Но каждая такая ситуация добавляет немного сложности, и если нет веских причин, стоит спросить, почему вы это делаете. Я считаю возможные "веские причины" включать:

  • Существуют ли другие атрибуты, которые "зависают" от этого атрибута? Вы захватываете, скажем, и "имя цвета", и "шестнадцатеричное значение", так что шестнадцатеричное значение всегда зависит от имени цвета? Если это так, то вам определенно нужна отдельная таблица цветов, чтобы предотвратить ситуации, когда одна строка имеет («Красный», «# FF0000»), а другая - («Красный», «# FF3333»). Множественные коррелированные атрибуты являются сигналом № 1 о том, что объект должен быть нормализован.
  • Будет ли набор возможных значений часто меняться? Использование нормализованной таблицы поиска облегчит будущие изменения элементов набора, поскольку вы просто обновляете одну строку. Однако, если это нечасто, не отказывайтесь от операторов, которые вместо этого должны обновлять множество строк в основной таблице; базы данных довольно хороши в этом. Если вы не уверены, проведите несколько тестов скорости.
  • Будет ли пользователь напрямую управлять набором возможных значений? Т.е. Есть ли экран, где они могут добавить / удалить / изменить порядок элементов в списке? Если так, то отдельная таблица обязательна.
  • Будет ли список различных значений влиять на некоторый элемент пользовательского интерфейса? Например. такое «цвет» в выпадающем списке? Тогда вам лучше иметь его в отдельной таблице, а не делать SELECT DISTINCT для таблицы каждый раз, когда вам нужно отобразить выпадающий список.

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

3 голосов
/ 23 декабря 2008

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

2 голосов
/ 20 декабря 2008

rauhr.myopenid.com писал :

Мы решили решить эту проблему с помощью 4-й нормальной формы. ...

Это не 4-я нормальная форма. Это распространенная ошибка, которая называется One True Lookup: http://www.dbazine.com/ofinterest/oi-articles/celko22

4-я нормальная форма: http://en.wikipedia.org/wiki/Fourth_normal_form

1 голос
/ 20 декабря 2008

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

Это позволяет оптимизировать представление и сделать ваш код устойчивым к изменениям в таблицах.

В Oracle вы можете даже преобразовать представление в материализованное представление, если вам когда-либо понадобится.

1 голос
/ 20 декабря 2008

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

0 голосов
/ 20 декабря 2008

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

...