PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

PostgreSQL, Trigger und Parallelität, um einen zeitlichen Schlüssel zu erzwingen

Eine Lösung besteht darin, eine zweite Tabelle zum Erkennen von Konflikten zu verwenden und diese mit einem Trigger zu füllen. Verwenden Sie das Schema, das Sie der Frage hinzugefügt haben:

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

(Hinweis:Dies ist der zweite Versuch, da Sie Ihre Anforderung beim ersten Mal falsch gelesen haben. Hoffentlich ist es diesmal richtig).

Einige Funktionen zum Pflegen dieser Tabelle:

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

Und füllen Sie die Tabelle zum ersten Mal mit:

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

Erstellen Sie nun Trigger, um die Datumszuordnung nach Änderungen an medical_products zu füllen:nach insert-Aufrufen add_, nach update-Aufrufen clr_ (alte Werte) und add_ (neue Werte), nach delete-Aufrufen 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();

Die Eindeutigkeitsbeschränkung für medical_product_date_map fängt alle Produkte auf, die mit demselben Code am selben Tag hinzugefügt werden:

[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

Dies hängt davon ab, ob die Werte auf einen diskreten Abstand überprüft werden. Deshalb habe ich nach Daten und Zeitstempeln gefragt. Obwohl Zeitstempel technisch gesehen diskret sind, da Postgresql nur Mikrosekundenauflösung speichert, ist es nicht praktikabel, der Zuordnungstabelle für jede Mikrosekunde, für die das Produkt anwendbar ist, einen Eintrag hinzuzufügen.

Abgesehen davon könnten Sie wahrscheinlich auch mit etwas Besserem als einem vollständigen Tabellenscan davonkommen, um nach überlappenden Zeitstempelintervallen zu suchen, mit einigen Tricks, wenn Sie nur nach dem ersten Intervall suchen, nicht nach oder nicht vor ... jedoch nach einfachen diskreten Leerzeichen Ich bevorzuge diesen Ansatz, da IME auch für andere Dinge nützlich sein kann (z. B. Berichte, die schnell herausfinden müssen, welche Produkte an einem bestimmten Tag anwendbar sind).

Ich mag diesen Ansatz auch, weil es sich richtig anfühlt, den Eindeutigkeitsbeschränkungsmechanismus der Datenbank auf diese Weise zu nutzen. Außerdem glaube ich, dass es im Zusammenhang mit gleichzeitigen Aktualisierungen der Master-Tabelle zuverlässiger sein wird:Ohne die Tabelle gegen gleichzeitige Aktualisierungen zu sperren, wäre es möglich, dass ein Validierungsauslöser keinen Konflikt erkennt und Einfügungen in zwei gleichzeitigen Sitzungen zulässt wird dann als Konflikt angezeigt, wenn die Auswirkungen beider Transaktionen sichtbar sind.