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

¿Existe un enfoque estándar para tratar con matrices desordenadas (conjuntos) en PostgreSQL?

No hay una forma integrada en este momento.

Como arreglos

Si los normaliza constantemente al guardarlos, puede tratar las matrices como conjuntos, almacenándolos siempre ordenados y sin duplicar. Sería genial si PostgreSQL tuviera una función C integrada para hacer esto, pero no es así. Eché un vistazo a escribir uno, pero la API de matriz C es horrible , así que, aunque he escrito un montón de extensiones, me alejé cuidadosamente de esta.

Si no le importa un rendimiento moderadamente asqueroso, puede hacerlo en SQL:

CREATE OR REPLACE FUNCTION array_uniq_sort(anyarray) RETURNS anyarray AS $$
SELECT array_agg(DISTINCT f ORDER BY f) FROM unnest($1) f;
$$ LANGUAGE sql IMMUTABLE;

luego envuelva todos los guardados en llamadas a array_uniq_sort o aplicarlo con un gatillo. Luego puede comparar sus matrices para la igualdad. Podrías evitar el array_uniq_sort pide datos de la aplicación si, en cambio, solo hizo la ordenación/unique en el lado de la aplicación.

Si haces esto por favor almacene sus "conjuntos" como columnas de matriz, como text[] , no texto delimitado por comas o espacios. Consulte esta pregunta por algunas de las razones.

Debe tener cuidado con algunas cosas, como el hecho de que las conversiones entre matrices son más estrictas que las conversiones entre sus tipos base. Por ejemplo:

regress=> SELECT 'a' = 'a'::varchar, 'b' = 'b'::varchar;
 ?column? | ?column? 
----------+----------
 t        | t
(1 row)

regress=> SELECT ARRAY['a','b'] = ARRAY['a','b']::varchar[];
ERROR:  operator does not exist: text[] = character varying[]
LINE 1: SELECT ARRAY['a','b'] = ARRAY['a','b']::varchar[];
                              ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
regress=> SELECT ARRAY['a','b']::varchar[] = ARRAY['a','b']::varchar[];
 ?column? 
----------
 t
(1 row)

Dichas columnas son indexables a GiST para operaciones como array-contains o array-overlaps; consulte la documentación de PostgreSQL sobre la indexación de matrices.

Como filas normalizadas

La otra opción es simplemente almacenar filas normalizadas con una clave adecuada. Todavía usaría array_agg para clasificarlos y compararlos, ya que las operaciones de conjunto de SQL pueden ser complicadas de usar para esto (especialmente dada la falta de una operación de diferencia de conjunto de dos caras/XOR).

Esto se conoce generalmente como EAV (entidad-atributo-valor). Yo mismo no soy un fan, pero tiene su lugar de vez en cuando. Excepto que lo estarías usando sin el value componente.

Creas una tabla:

CREATE TABLE item_attributes (
    item_id integer references items(id),
    attribute_name text,
    primary key(item_id, attribute_name)
);

e inserte una fila para cada entrada de conjunto para cada elemento, en lugar de que cada elemento tenga una columna con valores de matriz. La restricción única impuesta por la clave principal garantiza que ningún elemento pueda tener duplicados de un atributo determinado. El orden de los atributos es irrelevante/indefinido.

Las comparaciones se pueden hacer con operadores de conjuntos de SQL como EXCEPT , o usando array_agg(attribute_name ORDER BY attribute_name) para formar matrices ordenadas consistentemente para comparar.

La indexación se limita a determinar si un elemento determinado tiene o no tiene un atributo determinado.

Personalmente, usaría matrices sobre este enfoque.

htienda

También puede usar hstores con valores vacíos para almacenar conjuntos, ya que hstore elimina los duplicados de claves. jsonb de 9.4 también funcionará para esto.

regress=# create extension hstore;
CREATE EXTENSION
regress=# SELECT hstore('a => 1, b => 1') = hstore('b => 1, a => 1, b => 1');
 ?column? 
----------
 t
(1 row)

Sin embargo, solo es realmente útil para los tipos de texto. por ejemplo:

regress=# SELECT hstore('"1.0" => 1, "2.0" => 1') = hstore('"1.00" => 1, "1.000" => 1, "2.0" => 1');
 ?column? 
----------
 f
(1 row)

y creo que es feo. De nuevo, preferiría las matrices.

Solo para matrices enteras

El intarray extension proporciona funciones útiles y rápidas para tratar matrices como conjuntos. Solo están disponibles para matrices de enteros, pero son realmente útiles.