Как реализовать отношение "многие ко многим" в PostgreSQL?

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

мой пример:

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

1 ответов


операторы 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 as коды. Хотя это возможно, это плохой стиль и приводит к путанице ошибок и сообщений об ошибках. Использовать юридические, строчные, без кавычек идентификаторы. Никогда не используйте зарезервированные слова и избегайте двойных кавычек идентификаторов смешанных случаев, если можете.

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

  • 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: если вы удалите счет, детали будут удалены вместе с ним.
    Не так для продуктов: вы не хотите удалять продукт, который используется в счете. Сервер выдаст ошибку, если попробуй это. Вы добавите еще один столбец в product пометить вместо устаревших строк.

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

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

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

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