Разумно ли отмечать все столбцы, кроме одного, как первичный ключ?

9

У меня есть таблица с фильмами. Поля:
id (PK), title, genre, runtime, released_in, tags, origin, downloads.

Моя база данных не может быть загрязнена дублированными строками, поэтому я хочу обеспечить уникальность. Проблема в том, что разные фильмы могут иметь одинаковое название или даже одинаковые поля, кроме tagsи downloads. Как обеспечить уникальность?

Я думал о двух способах:

  • сделать все поля кроме downloadsпервичного ключа. Я избегаю downloads, потому что это JSON, и это, вероятно, повлияет на производительность.
  • оставьте только idпервичный ключ, но добавьте уникальное ограничение ко всем остальным столбцам (кроме, опять же downloads).

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

На данный момент у меня чуть меньше 20 000 записей, но я ожидаю, что их число будет расти. Я не знаю, имеет ли это какое-то отношение к проблеме.

РЕДАКТИРОВАТЬ: я изменил схему, и вот как я хотел бы создать таблицу:

CREATE TABLE movies (
    id          serial PRIMARY KEY,
    title       text NOT NULL,
    runtime     smallint NOT NULL CHECK (runtime >= 0),
    released_in smallint NOT NULL CHECK (released_in > 0),
    genres      text[] NOT NULL default ARRAY[]::text[],
    tags        text[] NOT NULL default ARRAY[]::text[],
    origin      text[] NOT NULL default ARRAY[]::text[],
    downloads   json NOT NULL,
    inserted_at timestamp NOT NULL default current_timestamp,
    CONSTRAINT must_be_unique UNIQUE(title,runtime,released_in,genres,tags,origin)
);

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

рубик
источник
Тесно связанный вопрос (с ответом) на SO: нужен ли мне первичный ключ для моей таблицы, который имеет УНИКАЛЬНЫЙ (составной 4-столбец), один из которых может быть NULL? , Если какой-либо из столбцов может быть НЕДЕЙСТВИТЕЛЕН, срочно рассмотрите это: dba.stackexchange.com/q/9759/3684 .
Эрвин Брандштеттер

Ответы:

4

Ваше определение таблицы выглядит разумным повсюду. Со всеми столбцами ограничение будет работать , как и ожидалось - за опечатки и незначительные различия в правописании, которые могут быть довольно часто я боюсь за исключением. Посмотрите на комментарий @ a_horse .NOT NULLUNIQUE

Альтернатива с функциональным уникальным индексом

Другой вариант - это функциональный уникальный индекс (аналогичный тому, что прокомментировал @Dave ). Но я бы использовал uuidтип данных для оптимизации размера и производительности индекса.

Преобразование из массива в текст не выполняется IMMUTABLE(из-за его общей реализации):

Следовательно, вам нужна небольшая вспомогательная функция, чтобы объявить ее неизменной:

CREATE OR REPLACE FUNCTION f_movie_uuid(_title text
                                      , _runtime int2
                                      , _released_in int2
                                      , _genres text[]
                                      , _tags text[]
                                      , _origin text[])
  RETURNS uuid LANGUAGE sql IMMUTABLE AS  -- faking IMMUTABLE
'SELECT md5(_title || _runtime::text || _released_in::text
         || _genres::text || _tags::text || _origin::text)::uuid';

Используйте его для определения индекса:

CREATE UNIQUE INDEX movies_uni_idx
ON movies (f_movie_uuid(title,runtime,released_in,genres,tags,origin));

SQL Fiddle.

Больше деталей:

Вы можете использовать сгенерированный UUID в качестве PK, но я все равно буду использовать serialстолбец с его 4 байтами, что просто и дешево для ссылок FK и других целей. UUID будет отличным вариантом для распределенных систем, которым необходимо генерировать значения PK независимо. Или для очень больших столов, но для этого в нашей солнечной системе недостаточно фильмов.

Плюсы и минусы

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

Есть и другие конкретные преимущества, вот список:

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

Конкатенация всех столбцов можно ввести ложные срабатывания ( 'foo ' || 'bar' = 'foob ' || 'ar', но кажется , что очень маловероятно , для этого случая. Опечатки настолько гораздо более вероятно , что вы можете спокойно игнорировать его здесь.

Уникальность и массивы

Массивы должны быть отсортированы последовательно, чтобы иметь смысл в любом уникальном расположении, полагаясь на =оператора, потому что '{1,2}' <> '{2,1}'. Я предлагаю просмотровые таблицы для genre, tagи originс serialПК и уникальными записями, которые позволяют нечеткий поиск элементов массива. Затем:

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

В стороне

Если вы используете Postgres 9.4 или новее, рассмотрите jsonbвместоjson .

Эрвин Брандштеттер
источник
6

Представьте, что вы с группой друзей, и разговор переходит в кино. Кто-то спрашивает: «Что вы думаете о« Трех мушкетерах »?» Вы отвечаете: "Какой?"

Какая дополнительная информация вам нужна, чтобы быть абсолютно уверенным, что вы оба думаете об одном и том же фильме? Имя директора? Производственная студия? Год, когда он был выпущен? Одно из звёздных имен? Какая-то комбинация из двух или более?

Ответ на мой и ваш вопрос один и тот же.

Однако я не думаю, что жанр будет хорошим кандидатом. Одна из причин, жанр слишком субъективный критерий. Акция «Три мушкетера»? драма? приключение? комедии? приключенческий? романтическая комедия? Я часто вижу один и тот же фильм в разных жанрах. Даже если вы разрешите использовать несколько жанров, ваш пользователь может выбрать совершенно другой, не указанный в списке фильмов, которые он ищет.

Даже время выполнения может отличаться, особенно в кинотеатрах и версиях VCR / DVD / b-ray.

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

Как насчет даты выпуска? Театральный выпуск 1993 года? Видеомагнитофон выпуска 1999 года? Выпуск DVD 2004 года? Вы поняли идею.

Если подумать, что за фильмы, снятые Аланом Смити? Неужели настоящий режиссер когда-нибудь вышел вперед, чтобы поставить свое имя в проекте после свершившегося факта? Я не знаю.

Хм, я бы лучше остановился, пока еще есть некоторые критерии.

Некоторые дополнительные пункты:

  • Да, сохраните суррогатный ключ и создайте уникальный индекс для полей естественного ключа (если вы, наконец, сможете их зафиксировать). Суррогатный ключ лучше всего подходит для ссылок на внешние ключи. Вы не хотите дублировать все поля естественных ключей в каждой таблице, которая содержит ссылку на фильм.
  • Удалите поля массива (жанры, теги, происхождение). Идите вперед и правильно нормализуйте эти атрибуты. Я никогда не видел поле массива, которое не доставляло бы больше хлопот, чем оно того стоило, особенно если вы хотите, чтобы они были доступны для поиска ("... где жанр = 'ужас' ..."). Обратите внимание, что это не устранит автоматически проблемы различий и орфографии («Научная фантастика» и «SciFi») - если вы не будете правильно вести таблицы поиска . Но намного проще проверить такие различия в одном поле маленькой таблицы, чем в каждой ячейке массива каждой строки большой таблицы.
TommCatt
источник
4

Столбец ID не имеет никакого преимущества, если речь идет об уникальности, которую вы хотите / должны применять. Уникальность любой комбинации атрибутов никогда не будет реализована путем добавления бессмысленного идентификатора. Его «преимущество» проявляется только в том случае, если вам когда-нибудь понадобится новая таблица, для которой нужен внешний ключ. В этом случае, и если вы включили Id, вы можете использовать его в качестве FK в новой таблице. (Но не думайте, что это будет бесплатный обед. Недостатком такого подхода является то, что вы, скорее всего, будете писать больше соединений для простой цели получения информации, которая вполне могла бы быть частью этой новой таблицы, которую вы создали. )

Эрвин Смут
источник
1
Если бизнес-правила говорят, что комбинация значений в атрибутах FOO и BAR должна быть уникальной, то добавление идентификатора не приведет к этому. Добавление идентификатора просто позволяет избежать необходимости включать FOO и BAR как таковые в ссылочные таблицы. Что, в свою очередь, требует большего количества объединений, потому что атрибуты FOO и BAR (которые содержат идентификаторы BUSINESS) находятся не там, где они могли бы быть (и где они, скорее всего, ожидаются, по крайней мере, с точки зрения бизнеса).
Эрвин Смут
1
Это НЕ «строки», которые должны быть уникальными, это то, что бизнес говорит, что их идентификаторы должны быть. Если это комбинация атрибутов FOO и BAR, то это комбинация атрибутов FOO и BAR.
Эрвин Смут
2
Наличие идентификатора или нет не решает проблему обеспечения уникальности столбцов «бизнес» в вашей таблице. Обеспечение уникальности должно быть сделано путем объявления соответствующих ключей (что вы делаете - тот факт, что вы использовали синтаксическое слово «CONSTRAINT» вместо «KEY», не означает, что это не ключ).
Эрвин Смут