Решение между хранением идентификаторов таблиц поиска или чистыми данными

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

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

Что следует иметь в виду:

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

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

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

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


person cweston    schedule 20.12.2008    source источник
comment
Я не совсем понимаю вопрос. Возможно, вы могли бы перефразировать или уточнить?   -  person cletus    schedule 20.12.2008
comment
Это не нормализация. (Но есть распространенное заблуждение, что это так.)   -  person philipxy    schedule 11.07.2019


Ответы (7)


Вы можете использовать таблицу поиска с первичным ключом 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 строк не имеет значения. Но на эту таблицу поиска могут ссылаться миллионы или миллиарды строк в других таблицах!

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

person Bill Karwin    schedule 20.12.2008
comment
Если бы не было дополнительной таблицы для цветов, какой уровень нормальной формы она нарушила бы? (3-й, 4-й и т. Д.) - person MobileMon; 02.10.2015
comment
@MobileMon, Вы неправильно понимаете цель нормализации. Дело не в том, чтобы делать больше столов. Нормальные формы представляют данные без избыточности. Таблица поиска цветов в моем примере не имеет ничего общего с обычными формами таблицы ItemsWithColors. Речь идет о создании ограничения, чтобы varchar ограничивался конечным списком определенных строк (названий цветов). Это помогает определить домен столбца color, но на самом деле не помогает и не мешает достижению цели устранения избыточности. - person Bill Karwin; 02.10.2015
comment
Я считаю, что это нарушает шестую нормальную форму en.wikipedia.org/wiki/Sixth_normal_form - person MobileMon; 05.10.2015
comment
@MobileMon, для этого потребуется, чтобы в приведенной выше таблице ItemsWith Colors была хотя бы одна нетривиальная зависимость соединения. Это означало бы, что помимо color есть еще один столбец, который не является частью ключа-кандидата. В примере указаны только ... другие столбцы ... в качестве заполнителя, которые могут быть только столбцами ключа-кандидата. Так что информации, чтобы судить, находится ли таблица в 6NF, недостаточно. - person Bill Karwin; 05.10.2015
comment
Влияет ли размер столбца подстановки на решение использовать столбец идентификатора или нет? Если у вас есть столбец varchar (64) или varchar (256) по сравнению с целочисленным идентификатором, не займет ли это намного больше места для хранения? Я полагаю, что если пространство не проблема или разница тривиальна, то пропустить столбец идентификатора не составит труда. Для записи, мне нравится идея не использовать столбец идентификатора. Я создал таблицу поиска без нее, а затем повторно угадал то решение, которое привело меня к этой теме. Рад, что моя интуиция подтвердилась! - person MacGruber; 08.02.2017
comment
@BillKarwin, если вам когда-нибудь понадобится изменить строку в таблице поиска, как бы вы это сделали? учитывая тот факт, что это будет первичный ключ в таблице. - person WAQ; 29.08.2018
comment
@WAQ Чаще всего вставляют новое значение в таблицу поиска. Но если вам нужно ОБНОВИТЬ значение, вы можете использовать каскадное ограничение внешнего ключа для автоматического обновления зависимых строк. В приведенном выше примере показан синтаксис ON UPDATE CASCADE. Попробуйте! - person Bill Karwin; 29.08.2018
comment
@BillKarwin, это правда, и это работает. Но большинство БД не позволяют (или поощряют) изменять значения первичного ключа, скажем, я использую Entity Framework, что не позволит вам изменить первичный ключ таблицы, если я не использую хранимую процедуру и не обновляю ее. - person WAQ; 29.08.2018
comment
@WAQ Очень жаль, потому что именно по этой причине существуют каскадные ограничения внешнего ключа. Это нормальная операция в SQL. Но каскадные обновления должны быть атомарными, поэтому они должны выполняться внутри СУБД, их нельзя моделировать с помощью фреймворка. Вот почему разработчики фреймворка считают это плохой практикой. В этом случае вы не можете использовать решение, которое я показываю выше, в EF. - person Bill Karwin; 29.08.2018

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

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" */
)

Или можно еще нормализовать, составив "цветную" таблицу:

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 в таблице каждый раз, когда вам нужно отобразить дроплист.

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

person Ian Varley    schedule 20.12.2008
comment
ммммм, в этом случае у вас может быть hat.color с внешним ключом на color.color. Нормализация! = Создание поддельных ключей. Кроме того, вы в основном сказали, что нет необходимости переносить цвет в его собственную таблицу, если он не имеет значения; это просто произвольная строка .... ну да. Значит, это не отношение. - person derobert; 21.12.2008
comment
@derobert - Согласен, это не отношение, если это просто произвольные строки. Я понял, что это суть его вопроса: когда вы устанавливаете связь, а когда нет? - person Ian Varley; 22.12.2008

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

person HLGEM    schedule 22.12.2008

rauhr.myopenid.com написал:

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

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

Четвертая нормальная форма: http://en.wikipedia.org/wiki/Fourth_normal_form

person Ben Dempsey    schedule 20.12.2008
comment
Статьи Джо Селко больше нет. Wayback machine предлагает, чтобы groups.google.com/d/ msg / microsoft.public.sqlserver.programming / содержит его текст, но не такой красивый. - person James Skemp; 08.07.2013
comment
Замена значений другими значениями, являющимися идентификаторами, не является нормализацией. - person philipxy; 11.07.2019

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

person chaos    schedule 20.12.2008
comment
Только до тех пор, пока в этом не пропадет смысл. Нормой также является денормализация, когда производительность снижается из-за слишком нормализованных данных. Иногда логическая структура оказывается слишком медленной. - person Lasse V. Karlsen; 21.12.2008
comment
Я не согласен. OP спрашивает в довольно общем виде, поэтому я бы сказал нормализовать, пока не увижу, что нормализация слишком велика для ситуации. - person chaos; 22.12.2008
comment
Замена значений другими значениями, являющимися идентификаторами, не является нормализацией. - person philipxy; 11.07.2019

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

person derobert    schedule 20.12.2008
comment
Я всегда предостерегаю от использования представлений - они сохраняют удобочитаемость, но могут иметь значительную потерю производительности. - person Jeremiah Peschka; 21.12.2008
comment
Представления довольно дешевы для большинства баз данных, поскольку обычно они функционируют путем переписывания запросов. Конечно, проверьте вывод "объяснения" (или аналогичный) вашей БД. - person derobert; 21.12.2008

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

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

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

person Osama Al-Maadeed    schedule 20.12.2008
comment
Материализованные представления не зависят от Oracle. SQL Server поддерживает индексированные представления, которые в основном являются одним и тем же, хотя синтаксис отличается. - person mmx; 20.12.2008