Операторы 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
.Подробности:
Прочитайте главу по индексам в руководстве .