PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

PostgreSQL, gatilhos e simultaneidade para impor uma chave temporal


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.