PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

PostgreSQL, trigger e concorrenza per imporre una chiave temporale

Una soluzione consiste nell'avere una seconda tabella da utilizzare per rilevare le interferenze e popolarla con un trigger. Utilizzando lo schema che hai aggiunto nella domanda:

CREATE TABLE medicinal_product_date_map(
   aic_code char(9) NOT NULL,
   applicable_date date NOT NULL,
   UNIQUE(aic_code, applicable_date));

(nota:questo è il secondo tentativo a causa di una lettura errata del tuo requisito la prima volta. spero che questa volta sia giusto).

Alcune funzioni per mantenere questa tabella:

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 popola la tabella per la prima volta con:

SELECT count(add_medicinal_product_date_range(aic_code, vs, ve))
FROM medicinal_products;

Ora crea i trigger per popolare la mappa delle date dopo le modifiche a medicinali_prodotti:dopo l'inserimento delle chiamate add_, dopo l'aggiornamento delle chiamate clr_ (vecchi valori) e add_ (nuovi valori), dopo l'eliminazione delle chiamate 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();

Il vincolo di unicità su medical_product_date_map intrappolerà tutti i prodotti aggiunti con lo stesso codice nello stesso giorno:

[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

Questo dipende dai valori controllati per avere uno spazio discreto, motivo per cui ho chiesto informazioni su date e timestamp. Sebbene i timestamp siano, tecnicamente, discreti poiché Postgresql memorizza solo la risoluzione di microsecondi, aggiungere una voce alla tabella della mappa per ogni microsecondo per cui il prodotto è applicabile non è pratico.

Detto questo, probabilmente potresti anche cavartela con qualcosa di meglio di una scansione dell'intera tabella per verificare la presenza di intervalli di timestamp sovrapposti, con qualche trucco nel cercare solo il primo intervallo non dopo o non prima... tuttavia, per spazi discreti facili Preferisco questo approccio che IME può essere utile anche per altre cose (ad es. report che devono trovare rapidamente quali prodotti sono applicabili in un determinato giorno).

Mi piace anche questo approccio perché mi sembra giusto sfruttare il meccanismo di vincolo di unicità del database in questo modo. Inoltre, ritengo che sarà più affidabile nel contesto degli aggiornamenti simultanei alla tabella principale:senza bloccare la tabella per gli aggiornamenti simultanei, sarebbe possibile che un trigger di convalida non veda alcun conflitto e consenta inserimenti in due sessioni simultanee, che sono quindi visto in conflitto quando gli effetti di entrambe le transazioni sono visibili.