Fusion efficace (suppression des doublons) de tableaux

10

J'ai deux tables, left2et 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 UNIQfonction du intarraymodule. Existe-t-il un moyen plus efficace de procéder? Existe-t-il un index sur le arrterrain 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 est ici :

Alexandros
la source
Allez-vous interroger des millions de lignes à la fois? Que faites-vous avec le résultat? Ou y aura-t-il des prédicats pour en sélectionner quelques-uns? Peut right2.arr être NULL comme le suggère votre schéma de démonstration? Avez-vous besoin de tableaux triés en conséquence?
Erwin Brandstetter

Réponses:

9

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 aurez besoin de:

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

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

Je vois que vous avez sort() dans votre violon , donc ce n'est peut-être qu'une faute de frappe dans votre question.

Postgres 9.5

Quoi qu'il en soit, vous allez adorer le nouveau Postgres 9.5 (actuellement en version bêta). Il offre les capacités prêtes array_agg_mult()à l'emploi et beaucoup plus rapides:

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

Requete

L'objectif 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 qu'il DISTINCTest générique pour tous les types de données éligibles.

Ne nécessite pas le intarraymodule. 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 pouvez ajouter ORDER BYdirectement à 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 par rapport à de nombreux petits tri). Je trierais donc dans une sous-requête, puis j'agrégerais:

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 right2est nettement plus large que ces deux colonnes et que votre configuration est qualifiée pour les analyses d'index uniquement. Détails dans le wiki Postgres.

Erwin Brandstetter
la source
Merci +1. Je devrai UNNEST plus tard de toute façon, mais je veux vérifier si la suppression des doublons dans les tableaux et puis UNNEST est plus rapide.
Alexandros
0

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 se fait saccager, 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.

Alan Corey
la source
Déçu de Postgres? Access a-t-il même des tableaux?
ypercubeᵀᴹ
Je ne sais pas, mais il peut supprimer les doublons, c'est un problème assez courant dans le nettoyage des données. Sélectionnez distinct est suffisamment proche. Vous n'avez pas toujours le contrôle sur vos données brutes du monde réel.
Alan Corey