PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Tìm và tính tổng các phạm vi ngày có bản ghi chồng chéo trong postgresql

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
  1. 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ới count cụ thể
  2. 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
  3. HAVING lọc ra những ngày chỉ có một hoạt động tồn tại
  4. 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
  5. 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.
  6. 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:

demo:db <> fiddle

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.

  1. đ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
  2. 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
  3. 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ởi tsrange 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.
  4. 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.
  5. 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 đề
  6. 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.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PostgreSQL Không có chức năng Tăng tự động?

  2. SQLite hoạt động, nhưng cơ sở dữ liệu được di chuyển PostgreSQL gây ra LỖI - Django 3.0

  3. Đọc cam kết là điều bắt buộc đối với cơ sở dữ liệu SQL phân tán tương thích với Postgres

  4. PostgreSQL - cách cải thiện truy vấn / chỉ mục này

  5. Cách thay thế nhóm đã bắt bằng biểu thức đã đánh giá (thêm một giá trị số nguyên vào nhóm bắt)