sql >> Database >  >> RDS >> PostgreSQL

Is er een standaardaanpak voor het omgaan met ongeordende arrays (sets) in PostgreSQL?

Er is op dit moment geen ingebouwde manier.

Als arrays

Als u ze consequent normaliseert bij het opslaan, kunt u arrays als sets behandelen door ze altijd gesorteerd en gededupliceerd op te slaan. Het zou geweldig zijn als PostgreSQL een ingebouwde C-functie had om dit te doen, maar dat doet het niet. Ik heb een kijkje genomen om er een te schrijven, maar de C-array API is verschrikkelijk , dus hoewel ik een heleboel extensies heb geschreven, heb ik me voorzichtig verwijderd van deze.

Als je het niet erg vindt om matige prestaties te leveren, kun je het in SQL doen:

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;

verpak vervolgens alle opgeslagen items in aanroepen naar array_uniq_sort of afdwingen met een trigger. U kunt dan gewoon uw arrays vergelijken voor gelijkheid. Je zou de array_uniq_sort . kunnen vermijden vraagt ​​om gegevens van de app als je in plaats daarvan alleen de sortering/uniek aan de app-kant deed.

Als je dit doet, alsjeblieft sla uw "sets" op als matrixkolommen, zoals text[] , geen door komma's of spaties gescheiden tekst. Zie deze vraag om een ​​aantal redenen.

Je moet op een paar dingen letten, zoals het feit dat casts tussen arrays strenger zijn dan casts tussen hun basistypen. Bijv.:

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)

Dergelijke kolommen zijn GiST-indexeerbaar voor bewerkingen zoals array-contains of array-overlaps; zie de PostgreSQL-documentatie over array-indexering.

Als genormaliseerde rijen

De andere optie is om gewoon genormaliseerde rijen op te slaan met een geschikte sleutel. Ik zou nog steeds array_agg gebruiken om ze te sorteren en te vergelijken, aangezien SQL-setbewerkingen onhandig kunnen zijn om hiervoor te gebruiken (vooral gezien het ontbreken van een XOR / dubbelzijdige setverschilbewerking).

Dit staat algemeen bekend als EAV (entity-attribute-value). Ik ben zelf geen fan, maar het heeft wel af en toe een plekje. Behalve dat je het zou gebruiken zonder de value onderdeel.

U maakt een tabel:

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

en voeg een rij in voor elk set-item voor elk item, in plaats van dat elk item een ​​kolom met matrixwaarde heeft. De unieke beperking die door de primaire sleutel wordt afgedwongen, zorgt ervoor dat geen enkel item duplicaten van een bepaald kenmerk mag hebben. Attribuutvolgorde is niet relevant/ongedefinieerd.

Vergelijkingen kunnen worden gemaakt met SQL-setoperators zoals EXCEPT , of gebruik array_agg(attribute_name ORDER BY attribute_name) om consistent gesorteerde arrays te vormen voor vergelijking.

Indexering is beperkt tot het bepalen of een bepaald item een ​​bepaald kenmerk wel/niet heeft.

Persoonlijk zou ik arrays gebruiken in plaats van deze benadering.

hstore

U kunt ook hstores met lege waarden gebruiken om sets op te slaan, aangezien hstore sleutels dedupliceert. 9.4's jsonb zal hier ook voor werken.

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

Het is echter alleen echt handig voor teksttypen. bijv.:

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

en ik vind het lelijk. Dus nogmaals, ik geef de voorkeur aan arrays.

Alleen voor integer-arrays

De intarray extensie biedt handige, snelle functies voor het behandelen van arrays als sets. Ze zijn alleen beschikbaar voor integer-arrays, maar ze zijn erg handig.




  1. Hoe kan ik gegevens in CLOB-velden bijwerken met een>> voorbereide query <<met ODP (Oracle.DataAccess)?

  2. Gebruik Oracle om drie tabellen te combineren tot één met PIVOT

  3. NHibernate - kon geen (orakel) dialect vinden in configuratie

  4. SQL-query om alle producten, categorieën en metagegevens woocommerce/wordpress te krijgen