May mắn thay, bạn đang sử dụng PostgreSQL. Hàm cửa sổ generate_series()
là bạn của bạn.
Trường hợp thử nghiệm
Cho bảng kiểm tra sau (mà bạn lẽ ra phải cung cấp):
CREATE TABLE event(event_id serial, ts timestamp);
INSERT INTO event (ts)
SELECT generate_series(timestamp '2018-05-01'
, timestamp '2018-05-08'
, interval '7 min') + random() * interval '7 min';
Một sự kiện cứ sau 7 phút (ngẫu nhiên cộng với 0 đến 7 phút).
Giải pháp cơ bản
Truy vấn này đếm các sự kiện cho bất kỳ khoảng thời gian tùy ý nào. 17 phút trong ví dụ:
WITH grid AS (
SELECT start_time
, lead(start_time, 1, 'infinity') OVER (ORDER BY start_time) AS end_time
FROM (
SELECT generate_series(min(ts), max(ts), interval '17 min') AS start_time
FROM event
) sub
)
SELECT start_time, count(e.ts) AS events
FROM grid g
LEFT JOIN event e ON e.ts >= g.start_time
AND e.ts < g.end_time
GROUP BY start_time
ORDER BY start_time;
-
Truy vấn truy xuất
ts
tối thiểu và tối đa từ bảng cơ sở để bao gồm toàn bộ phạm vi thời gian. Thay vào đó, bạn có thể sử dụng một khoảng thời gian tùy ý. -
Cung cấp bất kỳ khoảng thời gian khi cần thiết.
-
Tạo một hàng cho mọi ca thời gian. Nếu không có sự kiện nào xảy ra trong khoảng thời gian đó, số lượng là
0
. -
Đảm bảo xử lý giới hạn trên và giới hạn dưới một cách chính xác:
- Kết quả không mong muốn từ truy vấn SQL với GIỮA các dấu thời gian
-
Hàm cửa sổ
lead()
có một tính năng thường bị bỏ qua:nó có thể cung cấp giá trị mặc định khi không tồn tại hàng đầu. Cung cấp'infinity'
trong ví dụ. Nếu không, khoảng thời gian cuối cùng sẽ bị cắt bằng một giới hạn trênNULL
.
Tương đương tối thiểu
Truy vấn trên sử dụng CTE và lead()
và cú pháp dài dòng. Thanh lịch và có thể dễ hiểu hơn, nhưng đắt hơn một chút. Đây là phiên bản ngắn hơn, nhanh hơn, tối thiểu:
SELECT start_time, count(e.ts) AS events
FROM (SELECT generate_series(min(ts), max(ts), interval '17 min') FROM event) g(start_time)
LEFT JOIN event e ON e.ts >= g.start_time
AND e.ts < g.start_time + interval '17 min'
GROUP BY 1
ORDER BY 1;
Ví dụ cho "15 phút một lần trong tuần qua" `
Và định dạng với to_char()
.
SELECT to_char(start_time, 'YYYY-MM-DD HH24:MI'), count(e.ts) AS events
FROM generate_series(date_trunc('day', localtimestamp - interval '7 days')
, localtimestamp
, interval '15 min') g(start_time)
LEFT JOIN event e ON e.ts >= g.start_time
AND e.ts < g.start_time + interval '15 min'
GROUP BY start_time
ORDER BY start_time;
Vẫn ORDER BY
và GROUP BY
trên dấu thời gian cơ bản giá trị , không phải trên chuỗi đã định dạng. Điều đó nhanh hơn và đáng tin cậy hơn.
db <> fiddle here
Câu trả lời có liên quan tạo ra số lượng đang chạy trong khung thời gian:
- PostgreSQL:số hàng đang chạy cho truy vấn 'theo phút'