PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Esiste un approccio standard per gestire gli array (set) non ordinati in PostgreSQL?

Non esiste un modo integrato in questo momento.

Come array

Se li normalizzi costantemente al salvataggio, puoi trattare gli array come insiemi, archiviandoli sempre ordinati e deduplicati. Sarebbe fantastico se PostgreSQL avesse una funzione C integrata per farlo, ma non è così. Ho dato un'occhiata a scriverne uno ma l'API dell'array C è orribile , quindi, anche se ho scritto un sacco di estensioni, mi sono semplicemente allontanato con cautela da questa.

Se non ti dispiace prestazioni moderatamente icky puoi farlo in 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;

quindi avvolgi tutti i salvataggi nelle chiamate a array_uniq_sort o applicarlo con un grilletto. Puoi quindi confrontare i tuoi array per l'uguaglianza. Potresti evitare array_uniq_sort richiede i dati dall'app se invece hai semplicemente eseguito l'ordinamento/univoca sul lato app.

Se lo fai per favore memorizza i tuoi "set" come colonne di array, come text[] , testo non delimitato da virgole o spazi. Vedi questa domanda per alcuni dei motivi.

Devi fare attenzione ad alcune cose, come il fatto che i cast tra gli array sono più rigidi dei cast tra i loro tipi di base. Es.:

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)

Tali colonne sono indicizzabili GiST per operazioni come array-contain o array-overlaps; vedere la documentazione di PostgreSQL sull'indicizzazione degli array.

Come righe normalizzate

L'altra opzione è semplicemente memorizzare le righe normalizzate con una chiave adatta. Userei ancora array_agg per ordinarli e confrontarli, poiché le operazioni sugli insiemi SQL possono essere difficili da usare per questo (soprattutto data la mancanza di un'operazione XOR / differenza di insieme a doppia faccia).

Questo è generalmente noto come EAV (entità-attributo-valore). Io stesso non sono un fan, ma ogni tanto ha il suo posto. A parte il fatto che lo useresti senza il value componente.

Crei una tabella:

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

e inserire una riga per ogni voce del set per ogni elemento, invece di avere ogni elemento con una colonna con valori di matrice. Il vincolo univoco imposto dalla chiave primaria garantisce che nessun elemento possa avere duplicati di un determinato attributo. L'ordinamento degli attributi è irrilevante/non definito.

I confronti possono essere eseguiti con operatori di insiemi SQL come EXCEPT o utilizzando array_agg(attribute_name ORDER BY attribute_name) per formare array ordinati in modo coerente per il confronto.

L'indicizzazione si limita a determinare se un determinato articolo ha o meno un determinato attributo.

Personalmente userei gli array su questo approccio.

hstore

Puoi anche utilizzare hstore con valori vuoti per archiviare i set, poiché hstore deduplica le chiavi. jsonb di 9.4 funzionerà anche per questo.

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

Tuttavia, è davvero utile solo per i tipi di testo. es.:

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

e penso che sia brutto. Quindi, ancora una volta, preferirei gli array.

Solo per array di interi

L'intarray L'estensione fornisce funzioni utili e veloci per trattare gli array come insiemi. Sono disponibili solo per array di interi ma sono davvero utili.