Как реализовать отношение многие ко многим в PostgreSQL? - PullRequest
60 голосов
/ 20 марта 2012

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

Мой пример:

Product(name, price);
Bill(name, date, Products);

1 Ответ

230 голосов
/ 20 марта 2012

Операторы SQL DDL (язык определения данных) могут выглядеть следующим образом:

CREATE TABLE product (
  product_id serial PRIMARY KEY  -- implicit primary key constraint
, product    text NOT NULL
, price      numeric NOT NULL DEFAULT 0
);

CREATE TABLE bill (
  bill_id  serial PRIMARY KEY
, bill     text NOT NULL
, billdate date NOT NULL DEFAULT CURRENT_DATE
);

CREATE TABLE bill_product (
  bill_id    int REFERENCES bill (bill_id) ON UPDATE CASCADE ON DELETE CASCADE
, product_id int REFERENCES product (product_id) ON UPDATE CASCADE
, amount     numeric NOT NULL DEFAULT 1
, CONSTRAINT bill_product_pkey PRIMARY KEY (bill_id, product_id)  -- explicit pk
);

Я внес несколько изменений:

  • n:m отношение обычно реализуется отдельной таблицей - в данном случае bill_product.

  • Я добавил serial столбцыкак суррогатные первичные ключи .Я настоятельно рекомендую это, потому что название продукта вряд ли уникально.Кроме того, обеспечение уникальности и ссылки на столбец во внешних ключах намного дешевле с 4-байтовым integer, чем со строкой, хранящейся как text или varchar.
    В Postgres 10 или более поздней версии рассмотрим IDENTITY столбец вместо.Подробности:

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

  • name не является хорошим именем.Я переименовал столбец name таблицы product в product.Это лучшее соглашение об именах .В противном случае, когда вы объединяете пару таблиц в запросе - который вы делаете много в реляционной базе данных - вы получаете несколько столбцов с именем name и должны использовать псевдонимы столбцов, чтобы разобраться в беспорядке,Это не полезно.Другим распространенным анти-паттерном будет просто id в качестве имени столбца.
    Я не уверен, каким будет имя bill.Может быть bill_id может быть именем в этом случае.

  • price имеет тип данных numeric для хранения дробных чисел точно как введено (произвольный тип точности вместо типа с плавающей запятой).Если вы имеете дело исключительно с целыми числами, сделайте это integer.Например, вы можете сохранить цены в виде центов .

  • . amount ("Products" в вашем вопросе) переходит в таблицу ссылок bill_product итипа numeric.Опять же, integer, если вы имеете дело исключительно с целыми числами.

  • Вы видите внешние ключи в bill_product?Я создал оба для каскадных изменений (ON UPDATE CASCADE): если product_id или bill_id должны измениться, изменение каскадно ко всем зависимым записям в bill_product и ничего не нарушается.
    Я также использовал ON DELETE CASCADE дляbill_id: Если вы удалите счет, вместе с ним будут удалены детали.
    Не для товаров: Вы не хотите удалять продукт, который используется в счете.Postgres выдаст ошибку, если вы попытаетесь это сделать.Вместо этого вы должны добавить еще один столбец к product, чтобы пометить устаревшие строки.

  • Все столбцы в этом базовом примере в итоге получат NOT NULL, поэтому NULL значения не допускаются.(Да, все столбцы - столбцы, используемые в первичном ключе, определяются UNIQUE NOT NULL автоматически.) Это потому, что значения NULL не имеют смысла ни в одном из столбцов.Это облегчает жизнь новичка.Но вам не так легко уйти, вам все равно нужно понимать NULL обработку .Дополнительные столбцы могут разрешать значения NULL, функции и объединения могут вводить значения NULL в запросах и т. Д.

  • Прочтите главу CREATE TABLE в руководстве .

  • Первичные ключи реализованы с уникальным index для ключевых столбцов, что позволяет быстро выполнять запросы с условиями для столбцов PK.Однако последовательность ключевых столбцов важна для ключей с несколькими столбцами.Поскольку в моем примере PK на bill_product включен на (bill_id, product_id), вы можете добавить еще один индекс на product_id или (product_id, bill_id), если у вас есть запросы, ищущие данные с product_id и без bill_id.Подробности:

  • Прочитайте главу по индексам в руководстве .

...