Kasus uji
Pertama, cara yang lebih berguna untuk menyajikan data Anda - atau bahkan lebih baik, dalam sqlfiddle , siap bermain dengan:
CREATE TEMP TABLE data(
system_measured int
, time_of_measurement int
, measurement int
);
INSERT INTO data VALUES
(1, 1, 5)
,(1, 2, 150)
,(1, 3, 5)
,(1, 4, 5)
,(2, 1, 5)
,(2, 2, 5)
,(2, 3, 5)
,(2, 4, 5)
,(2, 5, 150)
,(2, 6, 5)
,(2, 7, 5)
,(2, 8, 5);
Kueri yang disederhanakan
Karena masih belum jelas, saya hanya mengasumsikan hal di atas seperti yang diberikan.
Selanjutnya, saya menyederhanakan kueri Anda untuk sampai pada:
WITH x AS (
SELECT *, CASE WHEN lag(measurement) OVER (PARTITION BY system_measured
ORDER BY time_of_measurement) = measurement
THEN 0 ELSE 1 END AS step
FROM data
)
, y AS (
SELECT *, sum(step) OVER(PARTITION BY system_measured
ORDER BY time_of_measurement) AS grp
FROM x
)
SELECT * ,row_number() OVER (PARTITION BY system_measured, grp
ORDER BY time_of_measurement) - 1 AS repeat_ct
FROM y
ORDER BY system_measured, time_of_measurement;
Sekarang, meskipun semuanya bagus dan berkilau untuk menggunakan SQL murni, ini akan menjadi banyak lebih cepat dengan fungsi plpgsql, karena dapat melakukannya dalam satu pemindaian tabel di mana kueri ini membutuhkan setidaknya tiga pemindaian.
Lebih cepat dengan fungsi plpgsql:
CREATE OR REPLACE FUNCTION x.f_repeat_ct()
RETURNS TABLE (
system_measured int
, time_of_measurement int
, measurement int, repeat_ct int
) LANGUAGE plpgsql AS
$func$
DECLARE
r data; -- table name serves as record type
r0 data;
BEGIN
-- SET LOCAL work_mem = '1000 MB'; -- uncomment an adapt if needed, see below!
repeat_ct := 0; -- init
FOR r IN
SELECT * FROM data d ORDER BY d.system_measured, d.time_of_measurement
LOOP
IF r.system_measured = r0.system_measured
AND r.measurement = r0.measurement THEN
repeat_ct := repeat_ct + 1; -- start new array
ELSE
repeat_ct := 0; -- start new count
END IF;
RETURN QUERY SELECT r.*, repeat_ct;
r0 := r; -- remember last row
END LOOP;
END
$func$;
Telepon:
SELECT * FROM x.f_repeat_ct();
Pastikan untuk mengkualifikasi tabel nama kolom Anda setiap saat dalam fungsi plpgsql semacam ini, karena kami menggunakan nama yang sama sebagai parameter keluaran yang akan didahulukan jika tidak memenuhi syarat.
Milyaran baris
Jika Anda memiliki miliar baris , Anda mungkin ingin membagi operasi ini. Saya mengutip manualnya di sini:
Catatan:Implementasi
RETURN NEXT
saat ini danRETURN QUERY
menyimpan seluruh hasil yang ditetapkan sebelum kembali dari fungsi, seperti yang dibahas di atas. Itu berarti bahwa jika fungsi PL/pgSQL menghasilkan kumpulan hasil yang sangat besar, kinerjanya mungkin buruk:data akan ditulis ke disk untuk menghindari kehabisan memori, tetapi fungsi itu sendiri tidak akan kembali sampai seluruh kumpulan hasil dihasilkan. Versi PL/pgSQL yang akan datang mungkin memungkinkan pengguna untuk menentukan fungsi pengembalian-set yang tidak memiliki batasan ini. Saat ini, titik di mana data mulai ditulis ke disk dikendalikan oleh variabel work_memconfiguration. Administrator yang memiliki memori yang cukup untuk menyimpan kumpulan hasil yang lebih besar dalam memori harus mempertimbangkan untuk meningkatkan parameter ini.
Pertimbangkan untuk menghitung baris untuk satu sistem pada satu waktu atau tetapkan nilai yang cukup tinggi untuk work_mem
untuk mengatasi beban. Ikuti tautan yang disediakan dalam kutipan tentang lebih lanjut tentang work_mem.
Salah satu caranya adalah dengan menetapkan nilai yang sangat tinggi untuk work_mem
dengan SET LOCAL
di fungsi Anda, yang hanya berlaku untuk untuk transaksi saat ini. Saya menambahkan baris komentar di function. Jangan jangan atur sangat tinggi secara global, karena ini dapat mematikan server Anda. Baca manualnya.