web-dev-qa-db-fra.com

Fusion efficace (suppression des doublons) de tableaux

J'ai deux tables, left2 et right2. Les deux tables seront grandes (1 à 10 millions de lignes).

CREATE TABLE left2(id INTEGER, t1 INTEGER, d INTEGER);
ALTER TABLE left2 ADD PRIMARY KEY (id,t1);

CREATE TABLE right2( t1 INTEGER, d INTEGER, arr INTEGER[] );
ALTER TABLE right2 ADD PRIMARY KEY(t1,d);

Je vais effectuer ce type de requête:

SELECT l.d + r.d,
       UNIQ(SORT((array_agg_mult(r.arr)))
FROM left2 l,
     right2 r
WHERE l.t1 = r.t1
GROUP BY l.d + r.d
ORDER BY l.d + r.d;

Où pour l'agrégation de tableaux, j'utilise la fonction:

CREATE AGGREGATE array_agg_mult(anyarray) (
SFUNC=array_cat,
STYPE=anyarray,
INITCOND='{}');

Après avoir concaténé les tableaux, j'utilise la fonction UNIQ du module intarray. Existe-t-il un moyen plus efficace de procéder? Existe-t-il un index sur le champ arr pour accélérer la fusion (avec suppression des doublons)? La fonction d'agrégation peut-elle supprimer directement les doublons? Les tableaux originaux peuvent être considérés comme triés (et ils sont uniques) si cela aide.

Le SQL Fiddle is here :

10
Alexandros

Des résultats corrects?

Tout d'abord: l'exactitude. Vous souhaitez produire un tableau d'éléments uniques? Votre requête actuelle ne fait pas cela. La fonction uniq() du module intarray ne promet que:

supprimer les doublons adjacents

Comme indiqué dans le manuel , vous auriez besoin de:

SELECT l.d + r.d, uniq(sort(array_agg_mult(r.arr)))
FROM   ...

Vous donne également trié tableaux - en supposant que vous le vouliez, vous n'avez pas clarifié.

Je vous vois ayezsort() dans votre violon , donc cela peut être juste une faute de frappe dans votre question.

Postgres 9.5

Quoi qu'il en soit, vous allez - aimer le nouveau Postgres 9.5 (actuellement en version bêta). Il fournit les capacités de array_agg_mult() hors de la boîte et beaucoup plus rapide:

Il y a également eu d'autres améliorations de performances pour la gestion des baies.

Requete

Le but principal de array_agg_mult() est d'agréger des tableaux multidimensionnels, mais vous ne produisez de toute façon que des tableaux unidimensionnels. Je voudrais donc au moins essayer cette requête alternative:

SELECT l.d + r.d AS d_sum, array_agg(DISTINCT elem) AS result_arr
FROM   left2  l
JOIN   right2 r USING (t1)
     , unnest(r.arr) elem
GROUP  BY 1
ORDER  BY 1;

Ce qui répond également à votre question:

La fonction d'agrégation peut-elle supprimer directement les doublons?

Oui, c'est possible avec DISTINCT. Mais ce n'est pas plus rapide que uniq() pour les tableaux entiers, qui a été optimisé pour les tableaux entiers, tandis que DISTINCT est générique pour tous les types de données éligibles.

Ne nécessite pas le module intarray. Cependant , le résultat n'est pas nécessairement trié. Postgres utilise différents algorithmes pour DISTINCT (IIRC), les grands ensembles sont généralement hachés, puis le résultat n'est pas trié sauf si vous ajoutez explicitement ORDER BY. Si vous avez besoin de tableaux triés, vous pourriez ajouter directement ORDER BY À la fonction d'agrégation:

array_agg(DISTINCT elem ORDER BY elem)

Mais c'est généralement plus lent que de fournir des données pré-triées à array_agg() (un grand tri contre plusieurs petits). Donc je trierais dans une sous-requête et alors agrégerait:

SELECT d_sum, uniq(array_agg(elem)) AS result_arr
FROM  (
   SELECT l.d + r.d AS d_sum, elem
   FROM   left2  l
   JOIN   right2 r USING (t1)
        , unnest(r.arr) elem
   ORDER  BY 1, 2
   ) sub
GROUP  BY 1
ORDER  BY 1;

C'était la variante la plus rapide de mon test superficiel sur Postgres 9.4.

SQL Fiddle basé sur celui que vous avez fourni.

Indice

Je ne vois pas beaucoup de potentiel pour un indice ici. La seule option serait:

CREATE INDEX ON right2 (t1, arr);

Cela n'a de sens que si vous obtenez des analyses d'index uniquement - ce qui se produira si la table sous-jacente right2 Est nettement plus large que ces deux colonnes et que votre configuration est admissible à analyses d'index uniquement. Détails dans le wiki Postgres.

9
Erwin Brandstetter

Je suis vraiment déçu, c'est une chose facile à faire dans Microsoft Access. Vous pouvez créer une requête "supprimer les doublons" puis regarder le SQL pour voir comment il le fait. Je vais devoir allumer une machine Windows pour regarder. Ils varient, l'assistant de requête le fait.

Une chose qui fonctionne, je pense, est de charger toutes vos données dans une table, puis de faire SELECT DISTINCT dans une nouvelle table. Vous pouvez également coller une clause de commande par ordre pendant que vous y êtes. Je l'ai fait il y a un an, ça doit être ça.

Je combine 2 années de données de température, le capteur envoie chaque minute 2 copies du même point de données comme sauvegarde redondante. Parfois, on est saccagé, mais je veux seulement en garder un. J'ai également des chevauchements entre les fichiers.

Si les données sont exactement au même format sur l'ensemble du cycle, sur une machine Unix, vous pouvez faire quelque chose comme

cat *.tab > points.txt
sort -n < points.txt > sorted.txt
uniq -u sorted.txt unique.txt

Mais uniq compare les lignes comme des chaînes et, par exemple, 18.7000 n'est pas identique à 18.7. J'ai changé de logiciel pendant 2 ans donc j'ai les deux formats.

0
Alan Corey