bản trình diễn:db <> fiddle (sử dụng tập dữ liệu cũ với phần A-B chồng chéo)
Tuyên bố từ chối trách nhiệm: Điều này hoạt động cho các khoảng thời gian trong ngày không phải cho dấu thời gian. Yêu cầu về ts được đưa ra sau đó.
SELECT
s.acts,
s.sum,
MIN(a.start) as start,
MAX(a.end) as end
FROM (
SELECT DISTINCT ON (acts)
array_agg(name) as acts,
SUM(count)
FROM
activities, generate_series(start, "end", interval '1 day') gs
GROUP BY gs
HAVING cardinality(array_agg(name)) > 1
) s
JOIN activities a
ON a.name = ANY(s.acts)
GROUP BY s.acts, s.sum
-
generate_series
tạo tất cả các ngày từ đầu đến cuối. Vì vậy, mỗi ngày một hoạt động tồn tại sẽ có một hàng vớicount
cụ thể - Nhóm tất cả các ngày, tổng hợp tất cả các hoạt động hiện có và tổng số của chúng
-
HAVING
lọc ra những ngày chỉ có một hoạt động tồn tại - Vì có những ngày khác nhau với các hoạt động giống nhau, chúng tôi chỉ cần một người đại diện:Lọc tất cả các bản sao bằng
DISTINCT ON
- Kết hợp kết quả này với bảng ban đầu để bắt đầu và kết thúc. (lưu ý rằng "end" là một từ dành riêng trong Postgres, tốt hơn bạn nên tìm một tên cột khác!). Bạn cảm thấy thoải mái hơn khi mất chúng trước đây nhưng vẫn có thể lấy những dữ liệu này trong truy vấn con.
- Nhóm sự tham gia này để có được ngày sớm nhất và mới nhất của mỗi khoảng thời gian.
Đây là phiên bản cho dấu thời gian:
WITH timeslots AS (
SELECT * FROM (
SELECT
tsrange(timepoint, lead(timepoint) OVER (ORDER BY timepoint)),
lead(timepoint) OVER (ORDER BY timepoint) -- 2
FROM (
SELECT
unnest(ARRAY[start, "end"]) as timepoint -- 1
FROM
activities
ORDER BY timepoint
) s
)s WHERE lead IS NOT NULL -- 3
)
SELECT
GREATEST(MAX(start), lower(tsrange)), -- 6
LEAST(MIN("end"), upper(tsrange)),
array_agg(name), -- 5
sum(count)
FROM
timeslots t
JOIN activities a
ON t.tsrange && tsrange(a.start, a.end) -- 4
GROUP BY tsrange
HAVING cardinality(array_agg(name)) > 1
Ý tưởng chính là xác định các khe thời gian có thể. Vì vậy, tôi tận dụng mọi thời gian đã biết (cả bắt đầu và kết thúc) và đưa chúng vào một danh sách được sắp xếp. Vì vậy, tôi có thể thực hiện lần kéo đầu tiên đã biết (17:00 từ đầu A và 18:00 từ đầu B) và kiểm tra khoảng thời gian nào trong đó. Sau đó, tôi kiểm tra nó cho lần thứ 2 và thứ 3, rồi thứ 3 đến thứ 4, v.v.
Trong khoảng thời gian đầu tiên, chỉ có A phù hợp. Trong thứ hai từ 18-19 cũng B là phù hợp. Trong ô tiếp theo 19-20 cũng C, từ 20 đến 20:30 A không phù hợp nữa, chỉ B và C. Ô tiếp theo là 20:30-22 trong đó chỉ B phù hợp, cuối cùng 22-23 D được thêm vào B và cuối cùng nhưng không kém phần D phù hợp với 23-23:30.
Vì vậy, tôi lấy danh sách thời gian này và nối nó với bảng hoạt động nơi các khoảng thời gian giao nhau. Sau đó, nó chỉ là nhóm theo khoảng thời gian và tổng hợp số lượng của bạn.
- điều này đặt cả hai t của một hàng vào một mảng có các phần tử được mở rộng thành một hàng cho mỗi phần tử với
unnest
. Vì vậy, tôi luôn tập trung vào một cột có thể được sắp xếp đơn giản - sử dụng chức năng cửa sổ
dẫn đầu cho phép lấy giá trị của hàng tiếp theo thành giá trị hiện tại. Vì vậy, tôi có thể tạo phạm vi dấu thời gian từ cả hai giá trị này bằng
tsrange
- Bộ lọc này là cần thiết vì hàng cuối cùng không có "giá trị tiếp theo". Điều này tạo ra một
NULL
giá trị được diễn giải bởitsrange
như vô cùng. Vì vậy, điều này sẽ tạo ra một khe thời gian sai đáng kinh ngạc. Vì vậy, chúng tôi cần lọc hàng này ra. - Tham gia các khoảng thời gian so với bảng ban đầu.
&&
toán tử kiểm tra xem hai loại phạm vi có trùng nhau hay không. - Nhóm theo các khoảng thời gian duy nhất, tổng hợp tên và số lượng. Lọc ra các khoảng thời gian chỉ có một hoạt động bằng cách sử dụng
HAVING
mệnh đề - Một chút khéo léo để có được điểm bắt đầu và điểm kết thúc phù hợp. Vì vậy, điểm bắt đầu là tối đa của hoạt động bắt đầu hoặc bắt đầu của một khoảng thời gian (có thể lấy bằng cách sử dụng
lower
). Ví dụ. Đi theo vị trí 20-20:30:Bắt đầu lúc 20h nhưng cả B và C đều không có điểm bắt đầu ở đó. Tương tự như thời gian kết thúc.