Trường hợp thử nghiệm
Đầu tiên, một cách hữu ích hơn để trình bày dữ liệu của bạn - hoặc thậm chí tốt hơn, trong sqlfiddle , sẵn sàng chơi với:
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);
Truy vấn đơn giản hóa
Vì nó vẫn chưa rõ ràng, tôi chỉ giả định ở trên như đã cho.
Tiếp theo, tôi đã đơn giản hóa truy vấn của bạn để đến:
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;
Bây giờ, mặc dù sử dụng SQL thuần túy là rất tốt và sáng bóng, nhưng điều này sẽ nhiều nhanh hơn với hàm plpgsql, bởi vì nó có thể thực hiện việc đó trong một lần quét bảng trong đó truy vấn này cần ít nhất ba lần quét.
Nhanh hơn với hàm 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$;
Gọi:
SELECT * FROM x.f_repeat_ct();
Hãy đảm bảo luôn đủ điều kiện cho bảng tên cột của bạn trong loại hàm plpgsql này, vì chúng tôi sử dụng các tên giống như các tham số đầu ra sẽ được ưu tiên hơn nếu không đủ điều kiện.
Hàng tỷ hàng
Nếu bạn có hàng tỷ hàng , bạn có thể muốn chia nhỏ hoạt động này. Tôi trích dẫn sách hướng dẫn ở đây:
Lưu ý:Việc triển khai
RETURN NEXT
hiện tại vàRETURN QUERY
lưu trữ toàn bộ tập kết quả trước khi trả về từ hàm, như đã đề cập ở trên. Điều đó có nghĩa là nếu một hàm PL / pgSQL tạo ra một tập kết quả rất lớn, hiệu suất có thể kém:dữ liệu sẽ được ghi vào đĩa để tránh cạn bộ nhớ, nhưng bản thân hàm sẽ không quay trở lại cho đến khi toàn bộ tập kết quả được tạo. Phiên bản tương lai của PL / pgSQL có thể cho phép người dùng xác định các hàm set-return không có giới hạn này. Hiện tại, điểm mà dữ liệu bắt đầu được ghi vào đĩa được điều khiển bởi biến work_memconfiguration. Quản trị viên có đủ bộ nhớ để lưu trữ các tập kết quả lớn hơn trong bộ nhớ nên xem xét việc tăng thông số này.
Cân nhắc tính toán các hàng cho một hệ thống tại một thời điểm hoặc đặt giá trị đủ cao cho work_mem
để đối phó với tải. Theo liên kết được cung cấp trong phần trích dẫn để biết thêm về work_mem.
Có một cách là đặt giá trị rất cao cho work_mem
với SET LOCAL
trong chức năng của bạn, điều này chỉ hiệu quả cho giao dịch hiện tại. Tôi đã thêm một dòng nhận xét trong hàm. Đừng không đặt nó rất cao trên toàn cầu, vì điều này có thể làm hỏng máy chủ của bạn. Đọc hướng dẫn.