Unione efficiente (rimozione di duplicati) di array


10

Ho due tavoli left2e right2. Entrambe le tabelle saranno grandi (1-10 M righe).

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);

Eseguirò questo tipo di query:

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;

Dove per l'aggregazione di array utilizzo la funzione:

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

Dopo aver concatenato gli array, utilizzo la UNIQfunzione del intarraymodulo. Esiste un modo più efficiente per farlo? Esiste un indice sul arrcampo per accelerare l'unione (con la rimozione dei duplicati)? La funzione di aggregazione può rimuovere direttamente i duplicati? Le matrici originali possono essere considerate ordinate (e sono uniche) se ciò aiuta.

SQL Fiddle è qui :


Hai intenzione di interrogare milioni di righe contemporaneamente? Cosa stai facendo con il risultato? O ci saranno predicati per selezionarne alcuni? Può right2.arr essere NULL come suggerisce lo schema demo? Hai bisogno di array ordinati come risultato?
Erwin Brandstetter,

Risposte:


9

Risultati corretti?

Prima di tutto: correttezza. Vuoi produrre una serie di elementi unici? La tua query attuale non lo fa. La funzione uniq()dal modulo intarray promette solo di:

rimuovere i duplicati adiacenti

Come indicato nel manuale , è necessario:

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

Ti dà anche array ordinati - supponendo che lo desideri, non hai chiarito.

Vedo che hai sort() nel violino , quindi questo potrebbe essere solo un refuso nella tua domanda.

Postgres 9.5

In entrambi i casi, sarà l'amore il nuovo Postgres 9.5 (attualmente in beta). Offre le funzionalità di array_agg_mult()pronto all'uso e molto più veloce:

Ci sono stati anche altri miglioramenti delle prestazioni per la gestione dell'array.

domanda

Lo scopo principale di array_agg_mult()è aggregare array multidimensionali, ma si producono comunque solo array monodimensionali. Quindi almeno proverei questa query alternativa:

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;

Che affronta anche la tua domanda:

La funzione di aggregazione può rimuovere direttamente i duplicati?

Sì, può, con DISTINCT. Ma questo non è più veloce rispetto uniq()agli array di numeri interi, che è stato ottimizzato per gli array di numeri interi, mentre DISTINCTè generico per tutti i tipi di dati qualificanti.

Non richiede il intarraymodulo. Tuttavia , il risultato non è necessariamente ordinato. Postgres utilizza algoritmi variabili per DISTINCT(IIRC), i set di grandi dimensioni sono generalmente sottoposti a hash, quindi il risultato non viene ordinato a meno che non venga aggiunto esplicitamente ORDER BY. Se sono necessari array ordinati, è possibile aggiungere ORDER BYdirettamente alla funzione aggregata:

array_agg(DISTINCT elem ORDER BY elem)

Ma in genere è più lento del fornire dati pre-ordinati a array_agg()(un grande ordinamento rispetto a molti piccoli ordinamenti). Quindi vorrei ordinare in una sottoquery e quindi aggregare:

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;

Questa è stata la variante più veloce nel mio test rapido su Postgres 9.4.

SQL Fiddle basato su quello che hai fornito.

Indice

Non vedo molto potenziale per nessun indice qui. L'unica opzione sarebbe:

CREATE INDEX ON right2 (t1, arr);

Ha senso solo se si ottengono scansioni solo indice da questo, cosa che succederà se la tabella sottostante right2è sostanzialmente più ampia di queste sole due colonne e la propria configurazione si qualifica per scansioni solo indice. Dettagli nel Wiki di Postgres.


Grazie +1. Dovrò comunque UNNEST più tardi, ma voglio verificare se la rimozione dei duplicati negli array e UNNEST è più veloce.
Alexandros,

0

Sono davvero deluso, questa è una cosa facile da fare in Microsoft Access. Puoi creare una query "rimuovi duplicati", quindi guarda l'SQL per vedere come sta andando. Dovrò accendere un computer Windows per guardare. Variano, lo fa la procedura guidata per le query.

Una cosa che funziona penso che sia caricare tutti i tuoi dati in una tabella e poi SELEZIONARE DISTINCT in una nuova tabella. Puoi anche attenerci a una clausola order by mentre ci sei. L'ho fatto in qualche modo un anno fa, deve essere così.

Sto combinando 2 anni di dati di temperatura, il sensore invia 2 copie dello stesso punto dati ogni minuto come protezione ridondante. A volte uno viene spazzato via, ma voglio solo tenerne uno. Ho anche delle sovrapposizioni tra i file.

Se i dati hanno esattamente lo stesso formato per tutta la corsa, su una macchina unix puoi fare qualcosa del genere

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

Ma uniq confronta le righe come stringhe e, ad esempio, 18.7000 non è uguale a 18.7. Ho cambiato il mio software durante i 2 anni, quindi ho entrambi i formati.


Deluso da Postgres? Access ha persino array?
ypercubeᵀᴹ

Non lo so, ma può rimuovere i duplicati, è un problema abbastanza comune nella pulizia dei dati. Seleziona distinto è abbastanza vicino. Non hai sempre il controllo sui tuoi dati grezzi dal mondo reale.
Alan Corey,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.