Almacenar como matriz (desnormalizada)
Consideraría el módulo adicional intarray
que proporciona las funciones convenientes (y rápidas) uniq()
y sort()
. En una instalación moderna típica de Postgres es tan fácil como:
CREATE EXTENSION intarray;
Usando estos, un simple CHECK
la restricción puede imponer ascendente arreglos con distintas elementos.
CHECK (uniq(sort(cat_arr)) = cat_arr)
Puede además (opcionalmente) tener un activador que normalice los valores de matriz ON INSERT OR UPDATE
automáticamente. Entonces puedes pasar cualquiera array (posiblemente sin clasificar y con duplicados) y todo funciona. Me gusta:
CREATE OR REPLACE FUNCTION trg_search_insup_bef()
RETURNS trigger AS
$func$
BEGIN
NEW.cat_arr := uniq(sort(NEW.cat_arr);
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
CREATE TRIGGER insup_bef
BEFORE INSERT OR UPDATE OF cat_arr ON search
FOR EACH ROW
EXECUTE PROCEDURE trg_search_insup_bef();
El módulo adicional intraray es opcional, hay otras formas:
Pero las funciones intraray ofrecen un rendimiento superior.
Entonces puedes simplemente crear un UNIQUE
restricción en la columna de la matriz para hacer cumplir la unicidad de toda la matriz.
UNIQUE (cat_arr)
Escribí más sobre las ventajas de combinar restricciones (muy estrictas y confiables) con disparadores (menos confiables pero más convenientes) en esta respuesta relacionada hace solo dos días:
Si, para cada combinación, todo lo que necesita almacenar por categoría es el ID (y ninguna información adicional), esto debería ser suficiente.
Sin embargo , la integridad referencial no se asegura fácilmente de esta manera. No hay restricciones de clave externa para elementos de matriz (todavía), como documentado en su enlace
:Si se elimina una de las categorías o se cambia de ID, las referencias se rompen...
Esquema normalizado
Si necesita almacenar más o prefiere ir con un esquema normalizado para hacer cumplir la integridad referencial o por alguna razón, también puede hacerlo y agregar un activador para completar una vista materializada hecha a mano (una tabla redundante) y hacer cumplir la singularidad de una manera similar:
CREATE TABLE search (
search_id serial PRIMARY KEY
, ... more columns
);
CREATE TABLE cat (
cat_id serial PRIMARY KEY
, cat text NOT NULL
);
CREATE TABLE search_cat (
search_id int REFERENCES search ON DELETE CASCADE
, cat_id int REFERENCES cat
, PRIMARY KEY (search_id, cat_id)
);
Respuesta relacionada (no para combinaciones únicas, sino para elementos únicos) que demuestra el disparador: