Restrição de exclusividade com período

15

Considere uma pricestabela com estas colunas:

id         integer primary key
product_id integer -- foreign key
start_date date not null
end_date   date not null
quantity   integer
price      numeric

Gostaria que o banco de dados aplicasse a regra de que um produto pode ter apenas um preço em uma quantidade específica em um período (via where <date> BETWEEN start_date AND end_date).

Esse tipo de restrição baseada em intervalo é possível?

Espinho
fonte

Respostas:

23

Sim, você pode usar uma EXCLUDErestrição, que é uma generalização de UNIQUErestrições:

ALTER TABLE prices 
  ADD CONSTRAINT unique_price_per_product_quantity_daterange
    EXCLUDE  USING gist
    ( product_id WITH =, 
      quantity WITH =, 
      daterange(start_date, end_date, '[]') WITH &&   -- this is the crucial
    );

A restrição pode ser interpretada como dizendo:

Não permita duas linhas com períodos iguais product_id, iguais quantitye sobrepostos ( &&).

O '[]'é para o período desejado com tudo incluído (o padrão é [)para tipos de intervalo).

Consulte a documentação sobre restrições nos tipos de intervalo . Você provavelmente também precisará adicionar a extensão executando (uma vez, para cada banco de dados em que você deseja que este esteja instalado):

CREATE EXTENSION btree_gist;
ypercubeᵀᴹ
fonte
Isso é incrível. Eu não acho que daterangeseja exatamente o mesmo, pois é um limite inferior exclusivo, mas é fácil de corrigir. Eu realmente deveria estar migrando meus dados para usar um daterangetipo de coluna (isso pode ser uma pergunta separada, se for melhor) ou essa coisa de duas colunas é razoável?
spike
O padrão no limite inferior inclusivo e no limite superior exclusivo, se bem me lembro. Vou editar para o plano tudo incluído. Normalmente, prefiro o padrão, pois é comum em aplicativos semelhantes a hotéis. (Eu chegar no hotel no 2º, eu saia na 8ª, ficamos 6 dias O próximo ocupante pode entrar no 8º.)
ypercubeᵀᴹ
Na verdade, eu posso me perguntar qual é qual ... acabei de descobrir sobre os tipos de intervalo hoje e estou lendo os documentos!
spike
Não tenho certeza sobre o que é preferível, 2 colunas ou uma com daterange. Você pode fazer uma pergunta separada. Provavelmente dependerá do uso que você deseja, consultas, facilidade de uso (e necessidades de indexação). Se houver colunas separadas, seria mais fácil, por exemplo, ter um índice (product_id, start_date). Com uma fonte de dados, isso teria que ser um índice em(product_id, lower(range_column))
ypercubeᵀᴹ