Bạn cần một mục dữ liệu mỗi tuần và mục tiêu (trước khi tổng hợp số lượng cho mỗi công ty). Đó là CROSS JOIN
đơn giản giữa generate_series()
và goals
. Phần đắt (có thể) là để có được state
hiện tại từ updates
cho mỗi. Giống như @Paul đã được đề xuất
, một LATERAL
tham gia có vẻ như là công cụ tốt nhất. Chỉ thực hiện cho các bản cập nhật updates
tuy nhiên, và sử dụng kỹ thuật nhanh hơn với LIMIT 1
.
Và đơn giản hóa việc xử lý ngày tháng với date_trunc()
.
SELECT w_start
, g.company_id
, count(*) FILTER (WHERE u.status = 'green') AS green_count
, count(*) FILTER (WHERE u.status = 'amber') AS amber_count
, count(*) FILTER (WHERE u.status = 'red') AS red_count
FROM generate_series(date_trunc('week', NOW() - interval '2 months')
, date_trunc('week', NOW())
, interval '1 week') w_start
CROSS JOIN goals g
LEFT JOIN LATERAL (
SELECT status
FROM updates
WHERE goal_id = g.id
AND created_at < w_start
ORDER BY created_at DESC
LIMIT 1
) u ON true
GROUP BY w_start, g.company_id
ORDER BY w_start, g.company_id;
Để thực hiện điều này nhanh chóng bạn cần một chỉ mục nhiều cột :
CREATE INDEX updates_special_idx ON updates (goal_id, created_at DESC, status);
Thứ tự giảm dần cho created_at
là tốt nhất, nhưng không hoàn toàn cần thiết. Postgres có thể quét ngược các chỉ mục gần như chính xác nhanh chóng. ( Tuy nhiên, không áp dụng cho thứ tự sắp xếp ngược của nhiều cột.
)
Lập chỉ mục các cột trong that gọi món. Tại sao?
Và cột thứ ba state
chỉ được thêm vào để cho phép nhanh chóng quét chỉ chỉ mục
về các bản cập nhật updates
. Trường hợp liên quan:
1 nghìn mục tiêu trong 9 tuần (khoảng thời gian 2 tháng của bạn trùng lặp với ít nhất 9 tuần) chỉ yêu cầu 9 nghìn lần tra cứu chỉ mục cho bảng thứ 2 chỉ 1 nghìn hàng. Đối với những chiếc bàn nhỏ như thế này, hiệu suất không phải là vấn đề nhiều. Nhưng khi bạn có thêm vài nghìn bảng trong mỗi bảng, hiệu suất sẽ kém đi khi quét tuần tự.
w_start
đại diện cho sự bắt đầu của mỗi tuần. Do đó, số đếm được dành cho đầu tuần. Bạn có thể vẫn trích xuất năm và tuần (hoặc bất kỳ chi tiết nào khác đại diện cho tuần của bạn), nếu bạn nhấn mạnh:
EXTRACT(isoyear from w_start) AS year
, EXTRACT(week from w_start) AS week
Tốt nhất với ISOYEAR
, như @Paul đã giải thích.
Có liên quan:
- Sự khác biệt giữa LATERAL và truy vấn con trong PostgreSQL là gì?
- Tối ưu hóa truy vấn GROUP BY để truy xuất bản ghi mới nhất cho mỗi người dùng
- Chọn trước hàng trong mỗi GROUP BY nhóm?
- PostgreSQL:số hàng đang chạy cho truy vấn 'theo phút'