sql >> Databáze >  >> RDS >> PostgreSQL

PostgreSQL, spouštěče a souběžnost k vynucení časového klíče

Jedním z řešení je mít druhou tabulku pro detekci střetů a naplnit ji spouštěčem. Pomocí schématu, které jste přidali do otázky:

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

(poznámka:toto je druhý pokus kvůli nesprávnému přečtení vašeho požadavku při prvním kole. doufám, že tentokrát je to správně).

Některé funkce pro údržbu této tabulky:

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

A vyplňte tabulku poprvé:

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

Nyní vytvořte spouštěče pro naplnění mapy data po změnách v léčivých_produktech:po vložení volání add_, po aktualizaci volání clr_ (staré hodnoty) a add_ (nové hodnoty), po odstranění volání 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();

Omezení jedinečnosti na mapě medical_product_date_map zachytí všechny produkty přidávané se stejným kódem ve stejný den:

[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

To závisí na hodnotách, které jsou kontrolovány, zda mají diskrétní prostor - proto jsem se zeptal na data vs časová razítka. Ačkoli jsou časová razítka, technicky vzato, diskrétní, protože Postgresql ukládá pouze mikrosekundové rozlišení, přidání záznamu do mapové tabulky pro každou mikrosekundu, pro kterou je produkt použitelný, není praktické.

Když už to bylo řečeno, pravděpodobně vám také projde něco lepšího než skenování celé tabulky, abyste zkontrolovali překrývající se intervaly časových razítek, s trochou triku při hledání pouze prvního intervalu ne po nebo ne před... nicméně pro snadné diskrétní mezery Dávám přednost tomuto přístupu, který IME může být užitečný i pro jiné věci (např. pro přehledy, které potřebují rychle zjistit, které produkty jsou použitelné v určitý den).

Tento přístup se mi také líbí, protože mi přijde správné využít mechanismus omezení jedinečnosti databáze tímto způsobem. Také se domnívám, že to bude spolehlivější v kontextu souběžných aktualizací hlavní tabulky:bez zamykání tabulky proti souběžným aktualizacím by bylo možné, aby ověřovací spouštěč neviděl žádný konflikt a povolil vložení ve dvou souběžných relacích, tj. poté je vidět konflikt, když jsou viditelné oba efekty transakce.



  1. Jak simulovat DEADLOCK na SQL Server?

  2. Jak funguje SQLite Upper()

  3. Chyba syntaxe uložené procedury serveru MySQL

  4. Neplatný typ parametru (numpy.int64) při vkládání řádků pomocí executemany()