As instruções SQL DDL (linguagem de definição de dados) podem ser assim:
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
);
Fiz alguns ajustes:
-
A relação n:m é normalmente implementado por uma tabela separada -bill_productnesse caso.
-
Eu adicioneiserialcolunas como chaves primárias substitutas . No Postgres 10 ou posterior, considere umaIDENTITYcoluna em vez disso. Ver:
- Renomeie tabelas com segurança usando colunas de chave primária serial
- Coluna da tabela de incremento automático
- https://www.2ndquadrant.com/en/blog/postgresql-10-identity-columns/
Eu recomendo isso, porque o nome de um produto dificilmente é único (não é uma boa "chave natural"). Além disso, impor a exclusividade e fazer referência à coluna em chaves estrangeiras geralmente é mais barato com umintegerde 4 bytes (ou mesmo umbigintde 8 bytes ) do que com uma string armazenada comotextouvarchar.
-
Não use nomes de tipos de dados básicos comodatecomo identificadores . Embora isso seja possível, é um estilo ruim e leva a erros e mensagens de erro confusos. Use identificadores legais, minúsculos e sem aspas. Nunca use palavras reservadas e evite identificadores de maiúsculas e minúsculas entre aspas duplas, se puder.
-
"nome" não é um bom nome. Renomeei a coluna da tabelaproductpara serproduct(ouproduct_nameou similar). Essa é uma convenção de nomenclatura melhor . Caso contrário, quando você junta algumas tabelas em uma consulta - o que você faz muito em um banco de dados relacional - você acaba com várias colunas chamadas "nome" e precisa usar aliases de coluna para resolver a bagunça. Isso não é útil. Outro antipadrão difundido seria apenas "id" como nome de coluna.
Não tenho certeza de qual é o nome de umabillseria.bill_idprovavelmente será suficiente neste caso.
-
priceé de tipo de dadosnumericpara armazenar números fracionários precisamente como digitados (tipo de precisão arbitrária em vez de tipo de ponto flutuante). Se você lida exclusivamente com números inteiros, torne esseinteger. Por exemplo, você pode economizar preços como centavos .
-
Aamount("Products"na sua pergunta) vai para a tabela de vinculaçãobill_producte é do tiponumerictambém. Novamente,integerse você lida exclusivamente com números inteiros.
-
Você vê as chaves estrangeiras embill_product? Eu criei ambos para cascata de mudanças:ON UPDATE CASCADE. Se umproduct_idoubill_iddeve mudar, a mudança é em cascata para todas as entradas dependentes embill_producte nada quebra. Essas são apenas referências sem significado próprio.
Eu também useiON DELETE CASCADEparabill_id:Se uma fatura for excluída, seus detalhes morrerão com ela.
Não é assim para produtos:você não deseja excluir um produto que é usado em uma fatura. O Postgres lançará um erro se você tentar isso. Você adicionaria outra coluna aoproductpara marcar linhas obsoletas ("exclusão reversível").
-
Todas as colunas neste exemplo básico acabam sendoNOT NULL, entãoNULLvalores não são permitidos. (Sim, todos colunas - as colunas de chave primária são definidasUNIQUE NOT NULLautomaticamente.) Isso porqueNULLvalores não fariam sentido em nenhuma das colunas. Facilita a vida de um iniciante. Mas você não vai escapar tão facilmente, você precisa entenderNULLmanipulação de qualquer maneira. Colunas adicionais podem permitirNULLvalores, funções e junções podem introduzirNULLvalores em consultas etc.
-
Leia o capítulo sobreCREATE TABLEno manual.
-
As chaves primárias são implementadas com um índice exclusivo nas colunas-chave, o que torna rápidas as consultas com condições na(s) coluna(s) PK. No entanto, a sequência de colunas de chave é relevante em chaves de várias colunas. Desde o PK embill_productestá em(bill_id, product_id)no meu exemplo, você pode querer adicionar outro índice apenas emproduct_idou(product_id, bill_id)se você tiver dúvidas procurando por um determinadoproduct_ide sembill_id. Ver:
- Chave primária composta PostgreSQL
- Um índice composto também é bom para consultas no primeiro campo?
- Trabalho de índices no PostgreSQL
-
Leia o capítulo sobre índices no manual.