sql >> Databáze >  >> RDS >> PostgreSQL

Existuje standardní přístup pro práci s neuspořádanými poli (množinami) v PostgreSQL?

V tuto chvíli neexistuje žádný vestavěný způsob.

Jako pole

Pokud je při ukládání důsledně normalizujete, můžete s poli zacházet jako s množinami, a to tak, že je vždy uložíte setříděná a deduplikovaná. Bylo by skvělé, kdyby měl PostgreSQL vestavěnou funkci C, která to dělá, ale nemá. Podíval jsem se na psaní, ale C array API je příšerné , takže i když jsem napsal spoustu rozšíření, od tohoto jsem opatrně ustoupil.

Pokud vám nevadí středně špatný výkon, můžete to udělat v 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;

pak zabalte všechna uložení do volání array_uniq_sort nebo to vynutit spouštěčem. Pak můžete jen porovnat vaše pole pro rovnost. Můžete se vyhnout array_uniq_sort vyžaduje data z aplikace, pokud jste místo toho provedli řazení/jedinečnost na straně aplikace.

Pokud to uděláte, prosím uložte své "sady" jako sloupce pole, například text[] , nikoli text oddělený čárkou nebo mezerou. Viz tuto otázku z některých důvodů.

Musíte si dát pozor na několik věcí, jako je skutečnost, že přetypování mezi poli je přísnější než přetypování mezi jejich základními typy. Např.:

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)

Takové sloupce lze indexovat GiST pro operace, jako jsou pole-obsahuje nebo pole-překrývání; viz dokumentaci PostgreSQL o indexování polí.

Jako normalizované řádky

Druhou možností je pouze uložit normalizované řádky pomocí vhodného klíče. Stále bych použil array_agg pro jejich třídění a porovnávání, protože operace množin SQL mohou být k tomuto účelu neohrabané (zejména vzhledem k tomu, že neexistuje operace XOR / oboustranný rozdíl množin).

To je obecně známé jako EAV (entity-attribute-value). Sám nejsem fanoušek, ale občas má své místo. Až na to, že byste jej používali bez value komponenta.

Vytvoříte tabulku:

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

a vložte řádek pro každou položku sady pro každou položku, místo aby každá položka měla sloupec s hodnotou pole. Jedinečné omezení vynucené primárním klíčem zajišťuje, že žádná položka nesmí mít duplikáty daného atributu. Pořadí atributů je irelevantní/nedefinované.

Porovnání lze provést pomocí operátorů sady SQL, jako je EXCEPT , nebo pomocí array_agg(attribute_name ORDER BY attribute_name) vytvořit konzistentně seřazená pole pro porovnání.

Indexování je omezeno na určení, zda daná položka má/nemá daný atribut.

Osobně bych na tento přístup použil pole.

hstore

K ukládání sad můžete také použít hstores s prázdnými hodnotami, protože hstore deduplikuje klíče. 9.4 jsonb bude fungovat i pro toto.

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

Je to však užitečné pouze pro textové typy. např.:

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

a podle mě je to ošklivé. Znovu bych tedy upřednostnil pole.

Pouze pro celočíselná pole

intarray rozšíření poskytuje užitečné a rychlé funkce pro zacházení s poli jako s množinami. Jsou k dispozici pouze pro celočíselná pole, ale jsou opravdu užitečné.




  1. bbPress:Jak najít mapování příloh k jejich příslušným příspěvkům

  2. Aktualizujte každý řádek náhodným datem mezi dvěma daty

  3. MySQL dotaz – najděte nové uživatele za den

  4. Automatický přírůstek Heroku MySQL