Чи розумно позначати всі стовпці, окрім одного, як основний ключ?


9

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

Мою базу даних не можна забруднювати дублюючими рядками, тому я хочу надати унікальність. Проблема полягає в тому, що різні фільми можуть мати однакову назву, або навіть однакові поля, за винятком tagsі downloads. Як нав'язати унікальність?

Я думав про два способи:

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

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

На даний момент у мене трохи менше 20 000 записів, але я очікую, що кількість зросте. Я не знаю, чи це дещо стосується цього питання.

EDIT: Я змінив схему, і ось як я створив таблицю:

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


Питання, що мають тісний зв’язок (з відповіддю) на ТА: Чи потрібен первинний ключ для моєї таблиці, який містить УНІКАЛЬНИЙ (складений 4-стовпець), один з яких може бути NULL? . Якщо будь-який із стовпців може бути 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 як ПК, але я все одно використовую serialстовпчик з його 4-х байтами, що є простим і дешевим для посилань FK та інших цілей. UUID був би чудовим варіантом для розподілених систем, яким потрібно генерувати значення PK самостійно. Або для дуже величезних столів, але в нашій Сонячній системі майже не вистачає фільмів.

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

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

Є й інші конкретні переваги, ось список:

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

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

Унікальність та масиви

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

У будь-якому випадку, робота з масивами безпосередньо або з нормалізованою схемою та матеріалізованим поданням пошук може бути дуже ефективним з правильним індексом та операторами:

Убік

Якщо ви використовуєте Postgres 9.4 або новішої версії, розгляньте jsonbзамістьjson .


6

Уявіть, що ви поза групою друзів, і розмова перетворюється на кіно. Хтось запитує: "Що ти думав про" Три мушкетери "?" Ви відповідаєте: "Який?"

Яка додаткова інформація вам потрібна, щоб бути абсолютно впевненим, що ви обидва думаєте про той самий фільм? Ім’я режисера? Виробнича студія? Рік, коли його випустили? Одне з імен зірки? Якась комбінація двох чи більше?

Відповідь на моє запитання і ваша однакова.

Однак я б не думав, що жанр буде хорошим кандидатом. Одна з причин, жанр є занадто суб'єктивним критерієм. Акція "Три мушкетери"? драма? пригода? комедія? екшн-пригода? романтична комедія? Я часто бачу один і той же фільм, перелічений у різних жанрах. Навіть якщо ви надаєте декілька жанрів, ваш користувач може вибрати зовсім інший, не перелічений із фактичним фільмом, який шукає.

Навіть тривалість виконання може відрізнятися, особливо між театральною та відеомагнітофоною / DVD / b-ray версіями.

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

Що з датою випуску? Театральний реліз 1993 року? Випуск відеомагнітофона 1999 року? DVD-реліз 2004 року? Ви отримуєте ідею.

Подумайте над тим, що з усіх тих фільмів режисера Алана Сміті? Чи справді колись нарешті виступив справжній режисер, щоб поставити своє ім’я на проект після факту? Не знаю.

Хм, я б краще зупинився, поки ще залишилися деякі критерії.

Деякі додаткові моменти:

  • Так, збережіть сурогатний ключ і створіть унікальний індекс на полях природних ключів (якщо ви, нарешті, зможете їх прибити). Сурогатний ключ найкращий для іноземних посилань. Ви не хочете дублювати всі поля природних ключів у кожній таблиці, що містить посилання на фільм.
  • Відкиньте поля масиву (жанри, теги, джерела). Вперед і належним чином нормалізуйте ці атрибути. Я ніколи не бачив поля масиву, який не мав би більше клопоту, ніж це коштувало, особливо якщо ви хочете, щоб їх можна було шукати ("... де жанр = 'жах" ... "). Зауважте, це не автоматично усуне жодних проблем із відмінностями у регістрі та написанні ("Наукова фантастика" проти "SciFi") - якщо ви належним чином не підтримуєте таблиці пошуку . Але набагато простіше перевірити наявність таких різниць в одному полі невеликої таблиці, ніж кожна комірка масиву кожного ряду великої таблиці.

4

Стовпчик ідентифікатора взагалі не має переваги, коли мова йде про унікальність, яку потрібно / потрібно застосувати. Унікальність будь-якої комбінації атрибутів ніколи не буде застосована шляхом додавання безглуздого ідентифікатора. Його «перевага» виявляється лише тоді, коли ви коли-небудь досягнете того пункту, коли вам знадобиться нова таблиця, для якої потрібен зовнішній ключ до цієї. У такому випадку, і якщо ви включили ідентифікатор, то ви можете використовувати його як FK у вашій новій таблиці. (Але не думайте, що це буде безкоштовний обід. Недоліком такого підходу є те, що ви, ймовірно, зможете написати більше приєднань лише для того, щоб отримати інформацію, яка цілком могла бути частиною тієї нової таблиці, яку ви створили. )


1
Якщо ділові правила говорять, що поєднання значень в атрибутах FOO і BAR повинно бути унікальним, то додавання ідентифікатора цього не збирається досягти. Додавання ідентифікатора просто полегшує уникнення необхідності включати FOO і BAR як такі у посилання на таблиці. Що, у свою чергу, потребує більше приєднань, оскільки атрибути FOO і BAR (які мають ідентифікатори БІЗНЕС) не є там, де вони могли бути (і там, де вони, ймовірно, ОЧАКУЮТЬсь, принаймні з бізнес-точки зору).
Ервін Смоут

1
НЕ "рядки" повинні бути унікальними, це те, про що говорить бізнес - це їхні ідентифікатори. Якщо це комбінація атрибутів FOO і BAR, то це комбінація атрибутів FOO і BAR.
Erwin Smout

2
Ідентифікатор чи ні не вирішує жодних проблем із застосуванням унікальності стовпців "бізнес" у вашій таблиці. Забезпечення унікальності повинно бути виконано, оголосивши відповідні клавіші (що ви робите - той факт, що ви використовували синтаксичне слово "CONSTRAINT" замість "KEY", не означає, що це не ключ).
Erwin Smout
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.