PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Existe-t-il une approche standard pour traiter les tableaux (ensembles) non ordonnés dans PostgreSQL ?

Il n'y a pas de méthode intégrée pour le moment.

En tableaux

Si vous les normalisez systématiquement lors de l'enregistrement, vous pouvez traiter les tableaux comme des ensembles, en les stockant toujours triés et dédupliqués. Ce serait formidable si PostgreSQL avait une fonction C intégrée pour le faire, mais ce n'est pas le cas. J'ai jeté un œil à l'écriture d'un mais l'API du tableau C est horrible , donc même si j'ai écrit un tas d'extensions, je me suis juste éloigné de celle-ci.

Si cela ne vous dérange pas d'avoir des performances médiocres, vous pouvez le faire en 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;

puis encapsulez toutes les sauvegardes dans les appels à array_uniq_sort ou l'appliquer avec un déclencheur. Vous pouvez alors simplement comparer vos tableaux pour l'égalité. Vous pourriez éviter le array_uniq_sort appelle les données de l'application si vous avez simplement fait le tri/unique du côté de l'application à la place.

Si vous faites cela, s'il vous plaît stockez vos "ensembles" sous forme de colonnes de tableau, comme text[] , pas de texte délimité par des virgules ou des espaces. Voir cette question pour certaines des raisons.

Vous devez faire attention à certaines choses, comme le fait que les transtypages entre les tableaux sont plus stricts que les transtypages entre leurs types de base. 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)

Ces colonnes sont indexables par GiST pour des opérations telles que array-contains ou array-overlaps ; voir la documentation PostgreSQL sur l'indexation des tableaux.

Comme lignes normalisées

L'autre option consiste simplement à stocker des lignes normalisées avec une clé appropriée. J'utiliserais toujours array_agg pour les trier et les comparer, car les opérations d'ensemble SQL peuvent être maladroites à utiliser pour cela (en particulier compte tenu de l'absence d'opération de différence d'ensemble XOR / double face).

Ceci est généralement connu sous le nom d'EAV (entité-attribut-valeur). Je ne suis pas fan moi-même, mais il a sa place de temps en temps. Sauf que vous l'utiliseriez sans la value composant.

Vous créez un tableau :

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

et insérez une ligne pour chaque entrée d'ensemble pour chaque élément, au lieu d'avoir chaque élément ayant une colonne de valeur de tableau. La contrainte d'unicité imposée par la clé primaire garantit qu'aucun élément ne peut avoir de doublons d'un attribut donné. L'ordre des attributs n'est pas pertinent/indéfini.

Les comparaisons peuvent être faites avec des opérateurs d'ensemble SQL comme EXCEPT , ou en utilisant array_agg(attribute_name ORDER BY attribute_name) pour former des tableaux triés de manière cohérente à des fins de comparaison.

L'indexation se limite à déterminer si un élément donné a/n'a pas un attribut donné.

Personnellement, j'utiliserais des tableaux plutôt que cette approche.

hstore

Vous pouvez également utiliser hstores avec des valeurs vides pour stocker des ensembles, car hstore dédoublonne les clés. jsonb de 9.4 fonctionnera également pour cela.

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

Cependant, ce n'est vraiment utile que pour les types de texte. par exemple :

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

et je pense que c'est moche. Encore une fois, je privilégierais les tableaux.

Pour les tableaux d'entiers uniquement

Le intarray fournit des fonctions utiles et rapides pour traiter les tableaux comme des ensembles. Ils ne sont disponibles que pour les tableaux d'entiers mais ils sont vraiment utiles.