Der er ingen indbygget måde på nuværende tidspunkt.
Som arrays
Hvis du konsekvent normaliserer dem ved gem, kan du behandle arrays som sæt ved altid at gemme dem sorteret og de-duplikeret. Det ville være fantastisk, hvis PostgreSQL havde en indbygget C-funktion til at gøre dette, men det gør den ikke. Jeg kiggede på at skrive en, men C array API er forfærdelig , så selvom jeg har skrevet en masse udvidelser, bakkede jeg bare forsigtigt tilbage fra denne.
Hvis du ikke har noget imod en moderat icky ydeevne, kan du gøre det i 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;
omslut derefter alle lagringer i opkald til array_uniq_sort
eller håndhæve det med en udløser. Du kan så bare sammenligne dine arrays for lighed. Du kan undgå array_uniq_sort
opfordrer til data fra app'en, hvis du i stedet bare lavede sorteringen/det unikke på app-siden.
Hvis du gør dette venligst gem dine "sæt" som matrixkolonner, såsom text[]
, ikke komma- eller mellemrumssepareret tekst. Se dette spørgsmål
af nogle af årsagerne.
Du skal passe på et par ting, såsom det faktum, at casts mellem arrays er strengere end casts mellem deres basistyper. F.eks.:
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)
Sådanne kolonner er GiST-indekserbare for operationer som array-contains eller array-overlaps; se PostgreSQL-dokumentationen om array-indeksering.
Som normaliserede rækker
Den anden mulighed er blot at gemme normaliserede rækker med en passende nøgle. Jeg ville stadig bruge array_agg
til at sortere og sammenligne dem, da SQL-sæt-operationer kan være klodsede at bruge til dette (især i betragtning af manglen på en XOR / dobbeltsidet sæt-forskel-operation).
Dette er generelt kendt som EAV (entity-attribute-value). Jeg er ikke selv fan, men det har sin plads indimellem. Medmindre du ville bruge det uden value
komponent.
Du opretter en tabel:
CREATE TABLE item_attributes (
item_id integer references items(id),
attribute_name text,
primary key(item_id, attribute_name)
);
og indsæt en række for hver sætindgang for hvert element, i stedet for at hvert element har en kolonne med matrixværdi. Den unikke begrænsning, der håndhæves af den primære nøgle, sikrer, at ingen vare må have dubletter af en given attribut. Attributrækkefølgen er irrelevant/udefineret.
Sammenligninger kan udføres med SQL-sætoperatorer som EXCEPT
, eller ved at bruge array_agg(attribute_name ORDER BY attribute_name)
at danne konsekvent sorterede arrays til sammenligning.
Indeksering er begrænset til at bestemme, om en given vare har/ikke har en given egenskab.
Personligt ville jeg bruge arrays over denne tilgang.
hstore
Du kan også bruge hstores med tomme værdier til at gemme sæt, da hstore de-duplikerer nøgler. 9.4's jsonb
vil også arbejde for dette.
regress=# create extension hstore;
CREATE EXTENSION
regress=# SELECT hstore('a => 1, b => 1') = hstore('b => 1, a => 1, b => 1');
?column?
----------
t
(1 row)
Det er dog kun rigtig nyttigt til teksttyper. f.eks.:
regress=# SELECT hstore('"1.0" => 1, "2.0" => 1') = hstore('"1.00" => 1, "1.000" => 1, "2.0" => 1');
?column?
----------
f
(1 row)
og jeg synes det er grimt. Så igen, jeg vil foretrække arrays.
Kun for heltalsarrays
intarray
udvidelse giver nyttige, hurtige funktioner til at behandle arrays som sæt. De er kun tilgængelige for heltalsarrays, men de er virkelig nyttige.