PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Gibt es einen Standardansatz für den Umgang mit ungeordneten Arrays (Sets) in PostgreSQL?

Derzeit gibt es keine eingebaute Methode.

Als Arrays

Wenn Sie sie beim Speichern konsequent normalisieren, können Sie Arrays als Sätze behandeln, indem Sie sie immer sortiert und dedupliziert speichern. Es wäre toll, wenn PostgreSQL dafür eine eingebaute C-Funktion hätte, aber das tut es nicht. Ich habe versucht, eine zu schreiben, aber die C-Array-API ist schrecklich , also habe ich mich, obwohl ich eine Menge Erweiterungen geschrieben habe, vorsichtig von dieser entfernt.

Wenn Sie etwas eklige Leistung nicht stört, können Sie es in SQL tun:

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;

packen Sie dann alle Speicherungen in Aufrufe von array_uniq_sort ein oder erzwingen Sie es mit einem Trigger. Sie können dann einfach Ihre Arrays auf Gleichheit vergleichen. Sie könnten array_uniq_sort vermeiden fordert Daten von der App an, wenn Sie stattdessen nur sortieren/eindeutig auf der App-Seite durchgeführt haben.

Wenn Sie dies tun, bitte Speichern Sie Ihre "Sätze" als Array-Spalten, wie text[] , kein durch Kommas oder Leerzeichen getrennter Text. Siehe diese Frage aus einigen Gründen.

Sie müssen auf einige Dinge achten, wie die Tatsache, dass Umwandlungen zwischen Arrays strenger sind als Umwandlungen zwischen ihren Basistypen. Beispiel:

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)

Solche Spalten sind für Operationen wie Array-Contains oder Array-Overlaps GiST-indizierbar; siehe die PostgreSQL-Dokumentation zur Array-Indizierung.

Als normalisierte Zeilen

Die andere Möglichkeit besteht darin, normalisierte Zeilen einfach mit einem geeigneten Schlüssel zu speichern. Ich würde immer noch array_agg verwenden zum Sortieren und Vergleichen, da SQL-Mengenoperationen dafür umständlich zu verwenden sein können (insbesondere angesichts des Fehlens einer XOR / doppelseitigen Mengendifferenzoperation).

Dies wird allgemein als EAV (Entity-Attribute-Value) bezeichnet. Ich bin selbst kein Fan, aber es hat gelegentlich seine Berechtigung. Außer Sie würden es ohne den value verwenden Komponente.

Sie erstellen eine Tabelle:

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

und fügen Sie eine Zeile für jeden Satzeintrag für jedes Element ein, anstatt dass jedes Element eine Spalte mit Array-Werten hat. Die durch den Primärschlüssel erzwungene Eindeutigkeitsbeschränkung stellt sicher, dass kein Element Duplikate eines bestimmten Attributs haben darf. Die Reihenfolge der Attribute ist irrelevant/undefiniert.

Vergleiche können mit SQL-Set-Operatoren wie EXCEPT durchgeführt werden , oder verwenden Sie array_agg(attribute_name ORDER BY attribute_name) um konsistent sortierte Arrays zum Vergleich zu bilden.

Die Indizierung beschränkt sich auf die Bestimmung, ob ein bestimmtes Element ein bestimmtes Attribut hat oder nicht.

Persönlich würde ich Arrays gegenüber diesem Ansatz verwenden.

hstore

Sie können hstores auch mit leeren Werten verwenden, um Sätze zu speichern, da hstore Schlüssel dedupliziert. 9.4's jsonb wird auch dafür funktionieren.

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

Es ist jedoch nur für Texttypen wirklich nützlich. zB:

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

und ich finde es hässlich. Also wieder würde ich Arrays bevorzugen.

Nur für Integer-Arrays

Das intarray Die Erweiterung bietet nützliche, schnelle Funktionen zum Behandeln von Arrays als Mengen. Sie sind nur für Integer-Arrays verfügbar, aber sie sind wirklich nützlich.