Saya memiliki tabel yang berisi dua kolom permutasi / kombinasi array integer, dan kolom ketiga berisi nilai, seperti:
CREATE TABLE foo
(
perm integer[] NOT NULL,
combo integer[] NOT NULL,
value numeric NOT NULL DEFAULT 0
);
INSERT INTO foo
VALUES
( '{3,1,2}', '{1,2,3}', '1.1400' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0.9280' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,2,1}', '{1,2,3}', '0' ),
( '{3,2,1}', '{1,2,3}', '0.8000' )
Saya ingin mengetahui rata-rata dan standar deviasi untuk setiap permutasi, serta untuk setiap kombinasi. Saya bisa melakukannya dengan pertanyaan ini:
SELECT
f1.perm,
f2.combo,
f1.perm_average_value,
f2.combo_average_value,
f1.perm_stddev,
f2.combo_stddev,
f1.perm_count,
f2.combo_count
FROM
(
SELECT
perm,
combo,
avg( value ) AS perm_average_value,
stddev_pop( value ) AS perm_stddev,
count( * ) AS perm_count
FROM foo
GROUP BY perm, combo
) AS f1
JOIN
(
SELECT
combo,
avg( value ) AS combo_average_value,
stddev_pop( value ) AS combo_stddev,
count( * ) AS combo_count
FROM foo
GROUP BY combo
) AS f2 ON ( f1.combo = f2.combo );
Namun, kueri itu bisa sangat lambat ketika saya memiliki banyak data, karena tabel "foo" (yang pada kenyataannya, terdiri dari 14 partisi masing-masing dengan sekitar 4 juta baris) perlu dipindai dua kali.
Baru-baru ini, saya mengetahui bahwa Postgres mendukung "Fungsi Jendela", yang pada dasarnya seperti GROUP BY untuk kolom tertentu. Saya memodifikasi permintaan saya untuk menggunakan ini seperti ini:
SELECT
perm,
combo,
avg( value ) as perm_average_value,
avg( avg( value ) ) over w_combo AS combo_average_value,
stddev_pop( value ) as perm_stddev,
stddev_pop( avg( value ) ) over w_combo as combo_stddev,
count( * ) as perm_count,
sum( count( * ) ) over w_combo AS combo_count
FROM foo
GROUP BY perm, combo
WINDOW w_combo AS ( PARTITION BY combo );
Meskipun ini berfungsi untuk kolom "combo_count", kolom "combo_average_value" dan "combo_stddev" tidak lagi akurat. Tampaknya rata-rata diambil untuk setiap permutasi, dan kemudian dirata-rata untuk kedua kalinya untuk setiap kombinasi, yang tidak benar.
Bagaimana saya bisa memperbaikinya? Dapatkah fungsi jendela bahkan digunakan sebagai pengoptimalan di sini?
sumber
Jawaban:
Anda dapat memiliki fungsi jendela pada hasil fungsi agregat dalam tingkat kueri tunggal.
Ini semua akan bekerja dengan baik setelah beberapa modifikasi - kecuali bahwa gagal untuk standar deviasi pada prinsip matematika . Perhitungan yang terlibat tidak linier, jadi Anda tidak bisa begitu saja menggabungkan standar deviasi sub-populasi.
Untuk
combo_average_value
Anda perlu ungkapan iniKarena Anda perlu rata-rata tertimbang . (Rata-rata grup dengan 10 anggota memiliki berat lebih dari rata-rata grup dengan hanya 2 anggota!)
Ini bekerja :
Saya menggunakan dua jendela berbeda di sini, dan mengurangi baris
DISTINCT
yang diterapkan bahkan setelah fungsi jendela.Tapi saya sangat ragu itu akan lebih cepat dari permintaan awal Anda. Saya cukup yakin tidak.
Performa yang lebih baik dengan tata letak tabel yang diubah
Array memiliki overhead 24 byte (sedikit variasi tergantung pada jenisnya). Juga, Anda tampaknya memiliki beberapa item per array dan banyak pengulangan. Untuk meja besar seperti milik Anda, akan lebih baik untuk menormalkan skema. Contoh tata letak:
Jika Anda tidak memerlukan integritas referensial, Anda dapat menghilangkan batasan kunci asing.
Koneksi ke
combo_id
juga dapat ditempatkan di tabelperm
, tetapi dalam skenario ini saya akan menyimpannya (sedikit dinormalisasi)value
untuk kinerja yang lebih baik.Ini akan menghasilkan ukuran baris 32 byte (tuple header + padding: 24 byte, 2 x int (8 byte), tanpa bantalan), ditambah ukuran
numeric
kolom Anda yang tidak diketahui . (Jika Anda tidak membutuhkan ketepatan yang ekstrem, kolomdouble precision
atau bahkanreal
mungkin juga akan melakukannya.)Lebih lanjut tentang penyimpanan fisik dalam jawaban terkait ini pada SO atau di sini:
Mengkonfigurasi PostgreSQL untuk kinerja baca
Bagaimanapun, itu hanya sebagian kecil dari apa yang Anda miliki sekarang dan akan membuat kueri Anda jauh lebih cepat berdasarkan ukuran saja. Pengelompokan dan pengurutan pada bilangan bulat sederhana juga jauh lebih cepat.
Pertama - tama Anda akan mengumpulkan dalam subquery dan kemudian bergabung ke
perm
dancombo
untuk kinerja terbaik.sumber
foo
tabel yang tidak relevan. Pada kenyataannya, ada beberapa kolom lagi yang tidak digunakan oleh permintaan ini, jadi saya tidak yakin bahwa normalisasi permutasi dan kombinasi akan memberikan peningkatan kecepatan yang signifikan, untuk kasus penggunaan khusus ini.