Menggunakan beberapa fungsi jendela yang berbeda dan dua subkueri, ini akan bekerja dengan cepat:
WITH events(id, event, ts) AS (
VALUES
(1, 12, '2014-03-19 08:00:00'::timestamp)
,(2, 12, '2014-03-19 08:30:00')
,(3, 13, '2014-03-19 09:00:00')
,(4, 13, '2014-03-19 09:30:00')
,(5, 12, '2014-03-19 10:00:00')
)
SELECT first_value(pre_id) OVER (PARTITION BY grp ORDER BY ts) AS pre_id
, id, ts
, first_value(post_id) OVER (PARTITION BY grp ORDER BY ts DESC) AS post_id
FROM (
SELECT *, count(step) OVER w AS grp
FROM (
SELECT id, ts
, NULLIF(lag(event) OVER w, event) AS step
, lag(id) OVER w AS pre_id
, lead(id) OVER w AS post_id
FROM events
WINDOW w AS (ORDER BY ts)
) sub1
WINDOW w AS (ORDER BY ts)
) sub2
ORDER BY ts;
Menggunakan ts sebagai nama untuk kolom stempel waktu.
Dengan asumsi ts menjadi unik - dan diindeks (batasan unik melakukannya secara otomatis).
Dalam pengujian dengan tabel kehidupan nyata dengan 50 ribu baris, hanya diperlukan pemindaian indeks tunggal . Jadi, harus cukup cepat bahkan dengan meja besar. Sebagai perbandingan, kueri Anda dengan bergabung / berbeda tidak selesai setelah satu menit (seperti yang diharapkan).
Bahkan versi yang dioptimalkan, menangani satu gabungan silang pada satu waktu (penggabungan kiri dengan kondisi yang hampir tidak membatasi secara efektif terbatas cross join) tidak selesai setelah satu menit.
Untuk performa terbaik dengan meja besar, sesuaikan setelan memori Anda, khususnya untuk work_mem (untuk operasi sortir besar). Pertimbangkan untuk mengaturnya (jauh) lebih tinggi untuk sesi Anda sementara jika Anda dapat menghemat RAM. Baca selengkapnya di sini dan di sini.
Bagaimana?
-
Dalam subkueri
sub1lihat acara dari baris sebelumnya dan simpan hanya jika itu telah berubah, dengan demikian menandai elemen pertama dari grup baru. Pada saat yang sama, dapatkaniddari baris sebelumnya dan berikutnya (pre_id,post_id). -
Dalam subkueri
sub2,count()hanya menghitung nilai bukan nol.grp. yang dihasilkan menandai rekan-rekan di blok peristiwa yang sama berturut-turut. -
Di bagian terakhir
SELECT, ambilpre_idpertama danpost_idterakhir per grup untuk setiap baris untuk mencapai hasil yang diinginkan.
Sebenarnya, ini harus lebih cepat diSELECTluar :last_value(post_id) OVER (PARTITION BY grp ORDER BY ts RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS post_id... karena urutan jendela sesuai dengan jendela untuk
pre_id, jadi hanya satu jenis yang diperlukan. Tes cepat tampaknya mengkonfirmasinya. Selengkapnya tentang definisi bingkai ini.
SQL Fiddle.