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

Tổng thời lượng của các khoảng thời gian chồng chéo với mức độ ưu tiên bằng cách loại trừ chính khoảng thời gian chồng chéo

Cập nhật Giải pháp ban đầu của tôi không đúng. Việc hợp nhất các phạm vi không thể được xử lý trong một cửa sổ thông thường. Tôi đã tự nhầm lẫn khi sử dụng cùng một tên, trange , quên rằng cửa sổ nằm trên các hàng nguồn chứ không phải các hàng kết quả. Vui lòng xem SQL Fiddle được cập nhật với truy vấn đầy đủ cũng như một bản ghi được thêm vào để minh họa sự cố.

Bạn có thể đơn giản hóa yêu cầu chồng chéo cũng như xác định các khoảng trống và đảo bằng cách sử dụng các loại phạm vi PostgreSQL .

Truy vấn sau đây cố ý dài dòng để hiển thị từng bước của quy trình. Một số bước có thể được kết hợp.

SQL Fiddle

Đầu tiên, thêm [start, end] bao gồm phạm vi cho mỗi bản ghi.

with add_ranges as (
  select id, name, tsrange(start, "end", '[]') as t_range
    from activities
), 

 id | name |                    t_range                    
----+------+-----------------------------------------------
  1 | A    | ["2018-01-09 17:00:00","2018-01-09 20:00:00"]
  2 | A    | ["2018-01-09 18:00:00","2018-01-09 20:30:00"]
  3 | B    | ["2018-01-09 19:00:00","2018-01-09 21:30:00"]
  4 | B    | ["2018-01-09 22:00:00","2018-01-09 23:00:00"]
(4 rows)

Xác định các phạm vi chồng chéo như được xác định bởi && toán tử và đánh dấu sự bắt đầu của các đảo mới bằng 1 .

mark_islands as (
  select id, name, t_range,
         case
           when t_range && lag(t_range) over w then 0
           else 1
         end as new_range
    from add_ranges
  window w as (partition by name order by t_range)
),

 id | name |                    t_range                    | new_range 
----+------+-----------------------------------------------+-----------
  1 | A    | ["2018-01-09 17:00:00","2018-01-09 20:00:00"] |         1
  2 | A    | ["2018-01-09 18:00:00","2018-01-09 20:30:00"] |         0
  3 | B    | ["2018-01-09 19:00:00","2018-01-09 21:30:00"] |         1
  4 | B    | ["2018-01-09 22:00:00","2018-01-09 23:00:00"] |         1
(4 rows)

Đánh số các nhóm dựa trên tổng của new_range trong name .

group_nums as (
  select id, name, t_range, 
         sum(new_range) over (partition by name order by t_range) as group_num
    from mark_islands
),

 id | name |                    t_range                    | group_num 
----+------+-----------------------------------------------+-----------
  1 | A    | ["2018-01-09 17:00:00","2018-01-09 20:00:00"] |         1
  2 | A    | ["2018-01-09 18:00:00","2018-01-09 20:30:00"] |         1
  3 | B    | ["2018-01-09 19:00:00","2018-01-09 21:30:00"] |         1
  4 | B    | ["2018-01-09 22:00:00","2018-01-09 23:00:00"] |         2

Nhóm theo name, group_num để có được tổng thời gian trên đảo cũng như t_range hoàn chỉnh được sử dụng trong khấu trừ chồng chéo.

islands as (
  select name,
         tsrange(min(lower(t_range)), max(upper(t_range)), '[]') as t_range,
         max(upper(t_range)) - min(lower(t_range)) as island_time_interval
    from group_nums
   group by name, group_num
),

 name |                    t_range                    | island_time_interval 
------+-----------------------------------------------+----------------------
 A    | ["2018-01-09 17:00:00","2018-01-09 20:30:00"] | 03:30:00
 B    | ["2018-01-09 19:00:00","2018-01-09 21:30:00"] | 02:30:00
 B    | ["2018-01-09 22:00:00","2018-01-09 23:00:00"] | 01:00:00
(3 rows)

Đối với yêu cầu đếm thời gian chồng chéo giữa A tin nhắn và B thông báo, tìm các lần xuất hiện khi một A thông báo chồng lên một B và sử dụng * toán tử giao nhau để tìm giao lộ.

priority_overlaps as (
  select b.name, a.t_range * b.t_range as overlap_range
    from islands a
    join islands b
      on a.t_range && b.t_range
     and a.name = 'A' and b.name != 'A'
),

 name |                 overlap_range                 
------+-----------------------------------------------
 B    | ["2018-01-09 19:00:00","2018-01-09 20:30:00"]
(1 row)

Tính tổng thời gian của mỗi lần chồng chéo theo name .

overlap_time as (
  select name, sum(upper(overlap_range) - lower(overlap_range)) as total_overlap_interval
    from priority_overlaps
   group by name
),

 name | total_overlap_interval 
------+------------------------
 B    | 01:30:00
(1 row)

Tính tổng thời gian cho mỗi name .

island_times as (
  select name, sum(island_time_interval) as name_time_interval
    from islands
   group by name
)

 name | name_time_interval 
------+--------------------
 B    | 03:30:00
 A    | 03:30:00
(2 rows)

Tham gia tổng thời gian cho mỗi name đối với các điều chỉnh từ overlap_time CTE và trừ đi điều chỉnh cho duration cuối cùng giá trị.

select i.name,
       i.name_time_interval - coalesce(o.total_overlap_interval, interval '0') as duration
  from island_times i
  left join overlap_time o
    on o.name = i.name
;

 name | duration 
------+----------
 B    | 02:00:00
 A    | 03:30:00
(2 rows)


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Khóa nào, nếu có, có sử dụng 'CREATE TRIGGER' trong PostgreSQL 9.4.2 không

  2. Có bao giờ là một ý tưởng hay khi lưu trữ một mảng dưới dạng giá trị trường hoặc lưu trữ các giá trị mảng dưới dạng bản ghi?

  3. làm thế nào để bắt lỗi hàm pg_connect ()?

  4. Xóa / thay thế các ký tự đặc biệt trong giá trị cột?

  5. Cách lưu hoặc truy xuất một cột mảng bằng Hibernate