Obecnie nie ma wbudowanego sposobu.
Jako tablice
Jeśli konsekwentnie normalizujesz je podczas zapisywania, możesz traktować tablice jako zestawy, zawsze przechowując je posortowane i pozbawione duplikatów. Byłoby wspaniale, gdyby PostgreSQL miał do tego wbudowaną funkcję C, ale tak nie jest. Rzuciłem okiem na pisanie jednego, ale interfejs API tablicy C jest straszny , więc chociaż napisałem kilka rozszerzeń, po prostu ostrożnie wycofałem się z tego.
Jeśli nie masz nic przeciwko umiarkowanie złej wydajności, możesz to zrobić w 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;
następnie zawijaj wszystkie zapisy w wywołaniach do array_uniq_sort
lub wyegzekwuj go za pomocą wyzwalacza. Następnie możesz po prostu porównać swoje tablice pod kątem równości. Możesz uniknąć array_uniq_sort
woła o dane z aplikacji, jeśli zamiast tego zrobiłeś sortowanie/unikatowe po stronie aplikacji.
Jeśli to zrobisz proszę przechowuj swoje "zestawy" jako kolumny tablicowe, takie jak text[]
, a nie tekst oddzielony przecinkami lub spacjami. Zobacz to pytanie
z niektórych powodów.
Musisz uważać na kilka rzeczy, takich jak fakt, że rzutowanie między tablicami jest bardziej rygorystyczne niż rzutowanie między ich typami bazowymi. Np.:
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)
Takie kolumny są indeksowalne GiST dla operacji takich jak tablica-zawiera lub tablica-nakładanie; zobacz dokumentację PostgreSQL na temat indeksowania tablic.
Jako znormalizowane wiersze
Inną opcją jest po prostu przechowywanie znormalizowanych wierszy z odpowiednim kluczem. Nadal używałbym array_agg
do sortowania i porównywania ich, ponieważ operacje zestawów SQL mogą być do tego niezręczne (szczególnie biorąc pod uwagę brak operacji XOR / dwustronnej operacji zestawów różnicowych).
Jest to ogólnie znane jako EAV (wartość atrybutu encji). Sam nie jestem fanem, ale czasami ma to swoje miejsce. Tyle że używasz go bez value
komponent.
Tworzysz tabelę:
CREATE TABLE item_attributes (
item_id integer references items(id),
attribute_name text,
primary key(item_id, attribute_name)
);
i wstaw wiersz dla każdego wpisu zestawu dla każdego elementu, zamiast umieszczania każdego elementu w kolumnie o wartościach tablicowych. Unikalne ograniczenie wymuszone przez klucz podstawowy zapewnia, że żaden element nie może mieć duplikatów danego atrybutu. Kolejność atrybutów jest nieistotna/niezdefiniowana.
Porównania można wykonywać za pomocą operatorów zestawów SQL, takich jak EXCEPT
lub używając array_agg(attribute_name ORDER BY attribute_name)
aby utworzyć konsekwentnie posortowane tablice do porównania.
Indeksowanie ogranicza się do określenia, czy dany element ma/nie ma danego atrybutu.
Osobiście używałbym tablic zamiast tego podejścia.
sklep
Możesz również używać hstores z pustymi wartościami do przechowywania zestawów, ponieważ hstore deduplikuje klucze. jsonb
9.4 będzie również działać na to.
regress=# create extension hstore;
CREATE EXTENSION
regress=# SELECT hstore('a => 1, b => 1') = hstore('b => 1, a => 1, b => 1');
?column?
----------
t
(1 row)
Jednak jest to naprawdę przydatne tylko w przypadku typów tekstowych. np.:
regress=# SELECT hstore('"1.0" => 1, "2.0" => 1') = hstore('"1.00" => 1, "1.000" => 1, "2.0" => 1');
?column?
----------
f
(1 row)
i myślę, że jest brzydki. Więc ponownie preferuję tablice.
Tylko dla tablic liczb całkowitych
intarray
rozszerzenie zapewnia przydatne, szybkie funkcje do traktowania tablic jako zestawów. Są dostępne tylko dla tablic liczb całkowitych, ale są naprawdę przydatne.