Я считаю, что титул не требует объяснений. Как создать структуру таблиц в PostgreSQL для создания отношения "многие ко многим".
Мой пример:
Product(name, price);
Bill(name, date, Products);
Я считаю, что титул не требует объяснений. Как создать структуру таблиц в PostgreSQL для создания отношения "многие ко многим".
Мой пример:
Product(name, price);
Bill(name, date, Products);
Операторы 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
): Если a 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
в руководстве.
Первичные ключи реализуются с уникальным индексом в ключевых столбцах, что делает запросы с условиями на столбцах PK быстрыми. Однако последовательность ключевых столбцов имеет значение в многоколоночных ключах. Поскольку PK на bill_product
находится на (bill_id, product_id)
в моем примере, вы можете захотеть добавить еще один индекс только для product_id
или (product_id, bill_id)
, если у вас есть запросы, которые ищут a product_id
и no bill_id
. Подробности:
Прочитайте главу о указателях в руководстве.