PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Jumlahkan durasi periode tumpang tindih dengan prioritas dengan mengecualikan tumpang tindih itu sendiri

Perbarui Solusi asli saya tidak benar. Konsolidasi rentang tidak dapat ditangani di jendela biasa. Saya bingung sendiri dengan menggunakan nama yang sama, trange , lupa bahwa jendela berada di atas baris sumber daripada baris hasil. Silakan lihat SQL Fiddle yang diperbarui dengan kueri lengkap serta catatan tambahan untuk mengilustrasikan masalah.

Anda dapat menyederhanakan persyaratan yang tumpang tindih serta mengidentifikasi celah dan pulau menggunakan Jenis rentang PostgreSQL .

Kueri berikut sengaja dibuat bertele-tele untuk menunjukkan setiap langkah proses. Sejumlah langkah dapat digabungkan.

SQL Fiddle

Pertama, tambahkan [start, end] inclusive yang inklusif rentang untuk setiap catatan.

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)

Identifikasi rentang yang tumpang tindih sebagaimana ditentukan oleh && operator dan tandai awal pulau baru dengan 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)

Beri nomor grup berdasarkan jumlah new_range dalam 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

Kelompokkan menurut name, group_num untuk mendapatkan total waktu yang dihabiskan di pulau serta t_range lengkap untuk digunakan dalam pengurangan tumpang tindih.

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)

Untuk persyaratan menghitung waktu tumpang tindih antara A pesan dan B pesan, temukan kemunculan saat A pesan tumpang tindih dengan B pesan, dan gunakan * operator intersect untuk menemukan persimpangan.

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)

Jumlahkan total waktu setiap tumpang tindih dengan 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)

Hitung total waktu untuk setiap 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)

Bergabunglah dengan total waktu untuk setiap name untuk penyesuaian dari overlap_time CTE, dan kurangi penyesuaian untuk duration terakhir nilai.

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. pg gem sslmode=verify-full, di mana menempatkan sertifikat?

  2. Mengekspor database PostgreSQL dengan phpPgAdmin

  3. ORDER BY kolom yang terkadang kosong menggunakan Active Record &Rails

  4. Masalah soket PostgreSQL Mountain Lion

  5. Aplikasi Rel Sederhana:Kesalahan Tidak dapat mengunjungi Integer