Dalam basis data transaksi yang mencakup 1.000 entitas selama 18 bulan, saya ingin menjalankan kueri untuk mengelompokkan setiap periode 30 hari yang mungkin entity_id
dengan SUM dari jumlah transaksi mereka dan COUNT transaksi mereka dalam periode 30 hari itu, dan kembalikan data dengan cara yang kemudian bisa saya tanyakan. Setelah banyak pengujian, kode ini mencapai banyak hal yang saya inginkan:
SELECT id, trans_ref_no, amount, trans_date, entity_id,
SUM(amount) OVER(PARTITION BY entity_id, date_trunc('month',trans_date) ORDER BY entity_id, trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS trans_total,
COUNT(id) OVER(PARTITION BY entity_id, date_trunc('month',trans_date) ORDER BY entity_id, trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS trans_count
FROM transactiondb;
Dan saya akan menggunakan kueri terstruktur yang lebih besar seperti:
SELECT * FROM (
SELECT id, trans_ref_no, amount, trans_date, entity_id,
SUM(amount) OVER(PARTITION BY entity_id, date_trunc('month',trans_date) ORDER BY entity_id, trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS trans_total,
COUNT(id) OVER(PARTITION BY entity_id, date_trunc('month',trans_date) ORDER BY entity_id, trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS trans_count
FROM transactiondb ) q
WHERE trans_count >= 4
AND trans_total >= 50000;
Kasus yang tidak dicakup oleh kueri ini adalah saat jumlah transaksi akan berlangsung beberapa bulan, namun masih dalam 30 hari satu sama lain. Apakah jenis permintaan ini memungkinkan dengan Postgres? Jika demikian, saya menerima masukan apa pun. Banyak topik lain membahas agregat " berjalan ", bukan bergulir .
Memperbarui
The CREATE TABLE
Script:
CREATE TABLE transactiondb (
id integer NOT NULL,
trans_ref_no character varying(255),
amount numeric(18,2),
trans_date date,
entity_id integer
);
Sampel data dapat ditemukan di sini . Saya menjalankan PostgreSQL 9.1.16.
Output ideal akan mencakup SUM(amount)
dan COUNT()
dari semua transaksi selama periode 30 hari bergulir. Lihat gambar ini, misalnya:
Sorotan tanggal hijau menunjukkan apa yang disertakan oleh kueri saya. Sorotan baris kuning menunjukkan catatan apa yang saya ingin menjadi bagian dari set.
Bacaan sebelumnya:
sumber
every possible 30-day period by entity_id
Anda berarti periode dapat memulai setiap hari, sehingga 365 periode mungkin dalam (non-lompatan) tahun? Atau apakah Anda hanya ingin mempertimbangkan hari-hari dengan transaksi aktual sebagai permulaan periode secara individualentity_id
? Either way, tolong berikan definisi tabel Anda, versi Postgres, beberapa data sampel dan hasil yang diharapkan untuk sampel.entity_id
dalam 30 hari mulai dari setiap transaksi aktual. Mungkinkah ada beberapa transaksi untuk hal yang sama(trans_date, entity_id)
atau kombinasi itu unik? Definisi tabel Anda tidak memilikiUNIQUE
kendala PK atau apa pun, tetapi kendala tampaknya tidak ada ...id
kunci utama. Mungkin ada beberapa transaksi per entitas per hari.Jawaban:
Kueri yang Anda miliki
Anda bisa menyederhanakan kueri menggunakan
WINDOW
klausa, tapi itu hanya memperpendek sintaks, bukan mengubah rencana kueri.count(*)
, karenaid
sudah pasti ditentukanNOT NULL
?ORDER BY entity_id
sejak ituPARTITION BY entity_id
Anda dapat menyederhanakan lebih lanjut, meskipun:
Jangan menambahkan
ORDER BY
definisi jendela sama sekali, itu tidak relevan dengan permintaan Anda. Maka Anda tidak perlu mendefinisikan bingkai jendela khusus, baik:Lebih sederhana, lebih cepat, tetapi masih versi yang lebih baik dari apa yang Anda miliki , dengan bulan statis .
Permintaan yang Anda inginkan
... tidak didefinisikan dengan jelas, jadi saya akan membangun asumsi ini:
Hitung transaksi dan jumlah untuk setiap periode 30 hari dalam transaksi pertama dan terakhir dari setiap transaksi
entity_id
. Kecualikan periode awal dan akhir tanpa aktivitas, tetapi sertakan semua periode 30 hari yang mungkin dalam batas luar tersebut.Ini mencantumkan semua periode 30 hari untuk masing-masing
entity_id
dengan agregat Anda dan dengantrans_date
menjadi hari pertama (termasuk) periode tersebut. Untuk mendapatkan nilai untuk setiap baris, gabungkan ke tabel dasar sekali lagi ...Kesulitan dasarnya sama dengan yang dibahas di sini:
Definisi bingkai dari suatu jendela tidak dapat bergantung pada nilai dari baris saat ini.
Sebaliknya, panggil
generate_series()
dengantimestamp
input:Permintaan yang sebenarnya Anda inginkan
Setelah pembaruan dan diskusi pertanyaan:
Akumulasi baris yang sama
entity_id
dalam jendela 30 hari mulai dari setiap transaksi aktual.Karena data Anda didistribusikan secara jarang, seharusnya lebih efisien untuk menjalankan self-join dengan kondisi jangkauan , terlebih lagi karena Postgres 9.1 tidak memiliki
LATERAL
sambungan, namun:SQL Fiddle.
Jendela bergulir hanya bisa masuk akal (berkenaan dengan kinerja) dengan data untuk sebagian besar hari.
Ini tidak mengagregasi duplikat
(trans_date, entity_id)
per hari, tetapi semua baris pada hari yang sama selalu dimasukkan dalam jendela 30 hari.Untuk tabel besar, indeks penutup seperti ini bisa membantu sedikit:
Kolom terakhir
amount
hanya berguna jika Anda mendapatkan hanya pindaian indeks. Jatuhkan itu.Tapi itu tidak akan digunakan saat Anda memilih seluruh tabel. Itu akan mendukung permintaan untuk subset kecil.
sumber
column "t0.amount" must appear in the GROUP BY clause...