Uma solução é ter uma segunda tabela a ser usada para detectar conflitos e preenchê-la com um gatilho. Usando o esquema que você adicionou à pergunta:
CREATE TABLE medicinal_product_date_map(
aic_code char(9) NOT NULL,
applicable_date date NOT NULL,
UNIQUE(aic_code, applicable_date));
(nota:esta é a segunda tentativa devido a uma leitura errada do seu requisito na primeira vez. Espero que esteja certo desta vez).
Algumas funções para manter esta tabela:
CREATE FUNCTION add_medicinal_product_date_range(aic_code_in char(9), start_date date, end_date date)
RETURNS void STRICT VOLATILE LANGUAGE sql AS $$
INSERT INTO medicinal_product_date_map
SELECT $1, $2 + offset
FROM generate_series(0, $3 - $2)
$$;
CREATE FUNCTION clr_medicinal_product_date_range(aic_code_in char(9), start_date date, end_date date)
RETURNS void STRICT VOLATILE LANGUAGE sql AS $$
DELETE FROM medicinal_product_date_map
WHERE aic_code = $1 AND applicable_date BETWEEN $2 AND $3
$$;
E preencha a tabela pela primeira vez com:
SELECT count(add_medicinal_product_date_range(aic_code, vs, ve))
FROM medicinal_products;
Agora crie gatilhos para preencher o mapa de data após alterações em medicamentos_products:após inserir chamadas add_, após atualização chamar clr_ (valores antigos) e add_ (novos valores), após excluir chamadas clr_.
CREATE FUNCTION sync_medicinal_product_date_map()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
IF TG_OP = 'UPDATE' OR TG_OP = 'DELETE' THEN
PERFORM clr_medicinal_product_date_range(OLD.aic_code, OLD.vs, OLD.ve);
END IF;
IF TG_OP = 'UPDATE' OR TG_OP = 'INSERT' THEN
PERFORM add_medicinal_product_date_range(NEW.aic_code, NEW.vs, NEW.ve);
END IF;
RETURN NULL;
END;
$$;
CREATE TRIGGER sync_date_map
AFTER INSERT OR UPDATE OR DELETE ON medicinal_products
FOR EACH ROW EXECUTE PROCEDURE sync_medicinal_product_date_map();
A restrição de exclusividade em medicinal_product_date_map interceptará quaisquer produtos adicionados com o mesmo código no mesmo dia:
[email protected]@[local] =# INSERT INTO medicinal_products VALUES ('1','A','2010-01-01','2010-04-01');
INSERT 0 1
[email protected]@[local] =# INSERT INTO medicinal_products VALUES ('1','A','2010-03-01','2010-06-01');
ERROR: duplicate key value violates unique constraint "medicinal_product_date_map_aic_code_applicable_date_key"
DETAIL: Key (aic_code, applicable_date)=(1 , 2010-03-01) already exists.
CONTEXT: SQL function "add_medicinal_product_date_range" statement 1
SQL statement "SELECT add_medicinal_product_date_range(NEW.aic_code, NEW.vs, NEW.ve)"
PL/pgSQL function "sync_medicinal_product_date_map" line 6 at PERFORM
Isso depende dos valores que estão sendo verificados por terem um espaço discreto - e é por isso que perguntei sobre datas versus carimbos de data/hora. Embora os timestamps sejam, tecnicamente, discretos, já que o Postgresql armazena apenas resolução de microssegundos, adicionar uma entrada à tabela de mapas para cada microssegundo ao qual o produto é aplicável não é prático.
Dito isto, você provavelmente também poderia se safar com algo melhor do que uma varredura de tabela completa para verificar intervalos de carimbo de data e hora sobrepostos, com alguns truques para procurar apenas o primeiro intervalo não depois ou não antes ... no entanto, para espaços discretos fáceis Eu prefiro essa abordagem que o IME também pode ser útil para outras coisas (por exemplo, relatórios que precisam encontrar rapidamente quais produtos são aplicáveis em um determinado dia).
Também gosto dessa abordagem porque parece certo aproveitar o mecanismo de restrição de exclusividade do banco de dados dessa maneira. Além disso, acho que será mais confiável no contexto de atualizações simultâneas na tabela mestre:sem bloquear a tabela contra atualizações simultâneas, seria possível que um gatilho de validação não visse nenhum conflito e permitisse inserções em duas sessões simultâneas, que são então visto como conflitante quando os efeitos de ambas as transações são visíveis.