sql >> Databasteknik >  >> RDS >> PostgreSQL

Finns det en standardmetod för att hantera oordnade arrayer (uppsättningar) i PostgreSQL?

Det finns inget inbyggt sätt just nu.

Som matriser

Om du konsekvent normaliserar dem vid spara kan du behandla arrayer som uppsättningar genom att alltid lagra dem sorterade och de-duplicerade. Det skulle vara bra om PostgreSQL hade en inbyggd C-funktion för att göra detta, men det gör den inte. Jag tittade på att skriva en men C array API är hemskt , så även om jag har skrivit ett gäng tillägg så backade jag bara försiktigt från den här.

Om du inte har något emot måttligt dålig prestanda kan du göra 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;

linda sedan alla sparade anrop till array_uniq_sort eller genomdriva det med en utlösare. Du kan sedan bara jämföra dina arrayer för jämlikhet. Du kan undvika array_uniq_sort efterlyser data från appen om du istället bara gjorde sorteringen/unika på appsidan istället.

Om du gör detta snälla lagra dina "uppsättningar" som matriskolumner, som text[] , inte komma- eller mellanslagsavgränsad text. Se denna fråga av några av anledningarna.

Du måste se upp med några saker, som det faktum att casts mellan arrayer är strängare än casts mellan deras bastyper. T.ex.:

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ådana kolumner är GiST-indexerbara för operationer som array-contains eller array-overlaps; se PostgreSQL-dokumentationen om arrayindexering.

Som normaliserade rader

Det andra alternativet är att bara lagra normaliserade rader med en lämplig nyckel. Jag skulle fortfarande använda array_agg för att sortera och jämföra dem, eftersom SQL-uppsättningsoperationer kan vara klumpiga att använda för detta (särskilt med tanke på avsaknaden av en XOR / dubbelsidig uppsättningsskillnadsoperation).

Detta är allmänt känt som EAV (entity-attribute-value). Jag är själv inget fan, men det har sin plats ibland. Förutom att du skulle använda det utan value komponent.

Du skapar en tabell:

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

och infoga en rad för varje uppsättningspost för varje objekt, istället för att varje objekt har en kolumn med arrayvärde. Den unika begränsningen som upprätthålls av primärnyckeln säkerställer att ingen artikel får ha dubbletter av ett givet attribut. Attributordning är irrelevant/odefinierad.

Jämförelser kan göras med SQL-uppsättningsoperatorer som EXCEPT , eller med array_agg(attribute_name ORDER BY attribute_name) för att bilda konsekvent sorterade arrayer för jämförelse.

Indexering är begränsad till att avgöra om en given vara har/inte har ett givet attribut.

Personligen skulle jag använda arrayer över detta tillvägagångssätt.

hstore

Du kan också använda hstores med tomma värden för att lagra set, eftersom hstore de-duplicerar nycklar. 9.4:s jsonb kommer också att fungera för detta.

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 är dock bara riktigt användbart för texttyper. t.ex.:

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

och jag tycker det är fult. Så återigen, jag skulle föredra arrayer.

Endast för heltalsmatriser

intarray extension ger användbara, snabba funktioner för att behandla arrayer som uppsättningar. De är bara tillgängliga för heltalsmatriser men de är verkligen användbara.




  1. Hur man ansluter Grails 3.0 till min lokala Mysql-databas

  2. När jag använder Docker får jag felet:SQLSTATE[HY000] [2002] Ingen sådan fil eller katalog

  3. Fråga efter inlägg och taggar i samma fråga

  4. Finns det någon gräns för den kommaavgränsade strängen i find_in_set i mysql-frågan