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

PostgreSQL, triggere og samtidighed for at håndhæve en tidsmæssig nøgle

En løsning er at have en anden tabel til at bruge til at detektere sammenstød og udfylde den med en trigger. Ved at bruge det skema, du tilføjede til spørgsmålet:

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

(bemærk:dette er andet forsøg på grund af fejllæsning af dit krav første gang. håber det er rigtigt denne gang).

Nogle funktioner til at vedligeholde denne tabel:

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
$$;

Og udfyld tabellen første gang med:

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

Opret nu triggere for at udfylde datokortet efter ændringer af medicinal_produkter:efter indsæt kalder add_, efter opdatering kalder clr_ (gamle værdier) og add_ (nye værdier), efter sletning kalder 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();

Entydighedsbegrænsningen på medicinal_product_date_map vil fange alle produkter, der tilføjes med den samme kode på samme dag:

[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

Dette afhænger af de værdier, der kontrolleres for at have et diskret mellemrum - derfor spurgte jeg om datoer vs. tidsstempler. Selvom tidsstempler teknisk set er diskrete, da Postgresql kun gemmer mikrosekundsopløsning, er det ikke praktisk at tilføje en indgang til korttabellen for hvert mikrosekund, som produktet kan anvendes til.

Når det er sagt, kan du sikkert også slippe afsted med noget bedre end en fuld-tabel scanning for at kontrollere for overlappende tidsstempelintervaller, med nogle tricks ved kun at lede efter det første interval ikke efter eller ikke før... dog for nemme diskrete mellemrum Jeg foretrækker denne tilgang, som IME også kan være praktisk til andre ting (f.eks. rapporter, der hurtigt skal finde ud af, hvilke produkter der er gældende på en bestemt dag).

Jeg kan også godt lide denne tilgang, fordi det føles rigtigt at udnytte databasens unikke-begrænsningsmekanisme på denne måde. Jeg føler også, at det vil være mere pålideligt i forbindelse med samtidige opdateringer til mastertabellen:uden at låse tabellen mod samtidige opdateringer, ville det være muligt for en valideringsudløser at se ingen konflikt og tillade indsættelser i to samtidige sessioner, dvs. derefter set i konflikt, når begge transaktions effekter er synlige.



  1. SQL - ydeevne i varchar vs. int

  2. PDO indsæt matrixværdier

  3. Indsættelse af heltalsværdi i mysql int ved hjælp af INSERT

  4. Hvordan kan jeg indsætte kolonnekommentarer i PostgreSQL via Python?