Penggabungan array yang efisien (menghapus duplikat)

10

Saya punya dua meja, left2dan right2. Kedua tabel akan berukuran besar (baris 1-10M).

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

Saya akan melakukan jenis pertanyaan ini:

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;

Di mana untuk agregasi array saya menggunakan fungsi:

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

Setelah menyatukan array, saya menggunakan UNIQfungsi intarraymodul. Apakah ada cara yang lebih efisien untuk melakukan ini? Apakah ada indeks di arrlapangan untuk mempercepat penggabungan (dengan menghapus duplikat)? Bisakah fungsi agregat menghapus duplikat secara langsung? Array asli dapat dianggap diurutkan (dan unik) jika itu membantu.

SQL Fiddle ada di sini :

Alexandros
sumber
Apakah Anda akan menanyakan jutaan baris sekaligus? Apa yang kamu lakukan dengan hasilnya? Atau akan ada predikat untuk memilih beberapa? Bisakah right2.arr NULL seperti yang disarankan skema demo Anda? Apakah Anda memerlukan array yang diurutkan sebagai hasilnya?
Erwin Brandstetter

Jawaban:

9

Hasil yang benar?

Pertama: benar. Anda ingin menghasilkan berbagai elemen unik? Permintaan Anda saat ini tidak melakukan itu. Fungsi uniq()dari modul intarray hanya menjanjikan untuk:

hapus duplikat yang berdekatan

Seperti yang diinstruksikan dalam manual , Anda perlu:

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

Juga memberi Anda array yang diurutkan - dengan asumsi Anda menginginkannya, Anda tidak mengklarifikasi.

Saya melihat Anda ada sort() di biola Anda , jadi ini mungkin hanya salah ketik dalam pertanyaan Anda.

Postgres 9.5

Either way, Anda akan menyukai Postgres 9.5 baru (saat ini beta). Ini memberikan kemampuan di array_agg_mult()luar kotak dan jauh lebih cepat:

Ada juga peningkatan kinerja lainnya untuk penanganan array.

Pertanyaan

Tujuan utamanya array_agg_mult()adalah untuk mengumpulkan array multi dimensi, tetapi Anda hanya menghasilkan array 1 dimensi. Jadi saya setidaknya akan mencoba kueri alternatif ini:

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;

Yang juga menjawab pertanyaan Anda:

Bisakah fungsi agregat menghapus duplikat secara langsung?

Ya, bisa, dengan DISTINCT. Tapi itu tidak lebih cepat daripada uniq()untuk array integer, yang telah dioptimalkan untuk array integer, sementara DISTINCTgenerik untuk semua tipe data yang memenuhi syarat.

Tidak memerlukan intarraymodul. Namun , hasilnya belum tentu diurutkan. Postgres menggunakan algoritme yang bervariasi untuk DISTINCT(IIRC), set besar biasanya hash, maka hasilnya tidak diurutkan kecuali Anda menambahkan secara eksplisit ORDER BY. Jika Anda membutuhkan array yang diurutkan, Anda dapat menambahkan ORDER BYke fungsi agregat secara langsung:

array_agg(DISTINCT elem ORDER BY elem)

Tapi itu biasanya lebih lambat daripada memasukkan data pra- array_agg()sortir ke (satu jenis besar versus banyak jenis kecil). Jadi saya akan mengurutkan dalam subquery dan kemudian mengumpulkan:

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;

Ini adalah varian tercepat dalam tes sepintas saya di Postgres 9.4.

SQL Fiddle berdasarkan yang Anda berikan.

Indeks

Saya tidak melihat banyak potensi untuk indeks apa pun di sini. Satu-satunya pilihan adalah:

CREATE INDEX ON right2 (t1, arr);

Masuk akal jika Anda mendapatkan hanya pindaian indeks - ini akan terjadi jika tabel yang mendasari right2jauh lebih luas dari hanya dua kolom ini dan pengaturan Anda memenuhi syarat untuk pindaian hanya indeks. Detail dalam Postgres Wiki.

Erwin Brandstetter
sumber
Terima kasih +1. Saya harus UNNEST nanti, tetapi ingin memeriksa apakah menghapus duplikat dalam array dan kemudian UNNEST lebih cepat.
Alexandros
0

Saya sangat kecewa, ini hal yang mudah dilakukan di Microsoft Access. Anda dapat membuat kueri "hapus duplikat" lalu lihat SQL untuk melihat bagaimana melakukannya. Saya harus menyalakan mesin Windows untuk melihat. Mereka berbeda-beda, wisaya kueri melakukannya.

Satu hal yang berfungsi menurut saya adalah memuat semua data Anda ke dalam satu tabel lalu melakukan SELECT DISTINCT ke dalam tabel baru. Anda juga dapat menempel pada perintah dengan klausa saat Anda melakukannya. Saya melakukannya entah bagaimana setahun yang lalu, itu pasti.

Saya menggabungkan data suhu selama 2 tahun, sensor mengirimkan 2 salinan dari titik data yang sama setiap menit sebagai perlindungan berlebihan. Kadang-kadang seseorang menjadi sampah, tetapi saya hanya ingin menyimpannya. Saya juga memiliki tumpang tindih antara file.

Jika data format yang sama persis selama seluruh proses, pada mesin unix Anda dapat melakukan sesuatu seperti

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

Tetapi uniq membandingkan garis sebagai string dan misalnya 18.7000 tidak sama dengan 18.7. Saya telah mengubah perangkat lunak saya selama 2 tahun sehingga saya memiliki kedua format.

Alan Corey
sumber
Kecewa karena Postgres? Apakah Access bahkan memiliki array?
ypercubeᵀᴹ
Saya tidak tahu tetapi bisa menghapus duplikat, itu masalah yang cukup umum dalam pembersihan data. Pilih yang berbeda cukup dekat. Anda tidak selalu memiliki kendali atas data mentah Anda dari dunia nyata.
Alan Corey