sql >> Base de Datos >  >> RDS >> PostgreSQL

PostgreSQL, disparadores y concurrencia para hacer cumplir una clave temporal

Una solución es tener una segunda tabla para detectar conflictos y llenarla con un disparador. Usando el esquema que agregó en la pregunta:

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

(nota:este es el segundo intento debido a una lectura incorrecta de su requisito la primera vez. Espero que sea correcto esta vez).

Algunas funciones para mantener esta tabla:

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

Y llene la tabla por primera vez con:

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

Ahora cree activadores para completar el mapa de fechas después de los cambios en medicamentos_productos:después de insertar llama a add_, después de actualizar llama a clr_ (valores antiguos) y add_ (valores nuevos), después de eliminar llama a 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();

La restricción de unicidad en medicamento_product_date_map capturará cualquier producto que se agregue con el mismo código el mismo día:

[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

Esto depende de los valores que se verifican para tener un espacio discreto, razón por la cual pregunté acerca de las fechas frente a las marcas de tiempo. Aunque las marcas de tiempo son, técnicamente, discretas, ya que Postgresql solo almacena una resolución de microsegundos, no es práctico agregar una entrada a la tabla del mapa para cada microsegundo para el que se aplica el producto.

Habiendo dicho eso, probablemente también podría salirse con la suya con algo mejor que un escaneo de tabla completa para verificar intervalos de marcas de tiempo superpuestos, con algunos trucos para buscar solo el primer intervalo, ni después ni antes ... sin embargo, para espacios discretos fáciles Prefiero este enfoque, ya que IME también puede ser útil para otras cosas (por ejemplo, informes que necesitan encontrar rápidamente qué productos son aplicables en un día determinado).

También me gusta este enfoque porque se siente bien aprovechar el mecanismo de restricción de unicidad de la base de datos de esta manera. Además, creo que será más confiable en el contexto de actualizaciones simultáneas de la tabla maestra:sin bloquear la tabla contra actualizaciones simultáneas, sería posible que un disparador de validación no viera ningún conflicto y permitiera inserciones en dos sesiones simultáneas, que son luego se ve en conflicto cuando los efectos de ambas transacciones son visibles.