Permintaan untuk menentukan tanggal mulai dan berakhir berdasarkan waktu yang tumpang tindih

8

Diberikan data berikut:

id      |   user_id |   started             |   closed              |   dead
-------------------------------------------------------------------------------------------
7714    |   238846  |   2015-01-27 15:14:50 |   2015-02-02 14:14:13 |   NULL
7882    |   238846  |   2015-01-28 13:25:58 |   NULL                |   2015-05-15 12:16:07
13190   |   259140  |   2015-03-17 10:11:44 |   NULL                |   2015-03-18 07:31:57
13192   |   259140  |   2015-03-17 10:12:17 |   NULL                |   2015-03-18 11:46:46
13194   |   259140  |   2015-03-17 10:12:53 |   NULL                |   2015-03-18 11:46:36
14020   |   259140  |   2015-03-23 14:32:16 |   2015-03-24 15:57:32 |   NULL
17124   |   242650  |   2015-04-16 16:19:08 |   2015-04-16 16:21:06 |   NULL
19690   |   238846  |   2015-05-15 13:17:31 |   NULL                |   2015-05-27 13:56:43
20038   |   242650  |   2015-05-19 15:38:17 |   NULL                |   NULL
20040   |   242650  |   2015-05-19 15:39:58 |   NULL                |   2015-05-21 12:01:02
20302   |   242650  |   2015-05-21 13:09:06 |   NULL                |   NULL
20304   |   242650  |   2015-05-21 13:09:54 |   NULL                |   NULL
20306   |   242650  |   2015-05-21 13:10:19 |   NULL                |   NULL
20308   |   242650  |   2015-05-21 13:12:20 |   NULL                |   NULL
21202   |   238846  |   2015-05-29 16:47:29 |   NULL                |   NULL
21204   |   238846  |   2015-05-29 16:47:56 |   NULL                |   NULL
21208   |   238846  |   2015-05-29 17:05:15 |   NULL                |   NULL
21210   |   238846  |   2015-05-29 17:05:55 |   NULL                |   NULL
21918   |   242650  |   2015-06-04 17:04:29 |   NULL                |   2015-06-12 15:47:23

Saya perlu membuat kumpulan data yang memenuhi aturan berikut:

  1. Grup didefinisikan terlebih dahulu oleh user_idkarena itu kita hanya harus membandingkan catatan dari yang samauser_id
  2. Semua catatan yang dimulai setidaknya dalam 15 hari ketika catatan lainnya dimulai, ditutup atau mati harus dihitung sebagai grup.
  3. Dari masing-masing kelompok, akhir harus dihitung sebagai catatan pertama ditutup atau semua catatan memiliki nilai untuk mati dan kami mengambil tanggal terbesar dari kolom mati.
  4. Jika catatan tidak dimulai dalam 15 hari sejak awal atau akhir grup lain, maka itu memulai pengelompokan baru.

Untuk sementara, saya yakin data saya akan terlihat seperti ini:

user_id | mulai | akhir
-------------------------------------------------- ----
238846 | 2015-01-27 15:14:50 | 2015-02-02 14:14:13
259140 | 2015-03-23 ​​14:32:16 | 2015-03-24 15:57:32
242650 | 2015-04-16 16:19:08 | 2015-04-16 16:21:06
242650 | 2015-05-21 13:09:06 | BATAL
238846 | 2015-05-15 13:17:31 | BATAL

Adakah yang bisa memberikan panduan tentang cara membuat kueri untuk memenuhi persyaratan ini?

Berikut ini tautan ke pernyataan DDL dan DML untuk data yang disajikan dalam pertanyaan ini.

Atau, kita dapat melewati aturan # 2 dan # 4 dan lebih sederhana menyatakan bahwa hanya catatan yang saling tumpang tindih yang harus dimasukkan. Aturan yang lebih penting adalah bahwa dalam set yang diberikan, jika ada tanggal yang ditutup maka itu menjadi akhir set dan bukan tanggal mati terbesar.

Noah Goodrich
sumber
Ini akan lebih mudah dengan perubahan skema. Tidak perlu kedua kolom, tertutup dan mati. Hanya memiliki kolom "berakhir" dan kemudian alasan untuk akhirnya.
Andrew Brennan
3 contoh pertama Anda dapat dikodekan sebagai "Jika sebuah id 'tertutup', maka itu adalah grup tersendiri. Karena itu sepertinya tidak menyoroti semua aturan Anda, silakan tambahkan lebih banyak contoh.
Rick James

Jawaban:

3

Karena kurangnya kejelasan dalam pertanyaan, saya menemukan empat solusi berbeda. Solusinya berbeda pada:

  1. Apakah Anda akan "terjun" per jawaban Chris
  2. Ketika Anda memiliki tanggal tertutup, apakah Anda menggunakan tanggal paling awal untuk grup itu atau tanggal mulai untuk catatan yang ditutup.

Harap dicatat ini dilakukan dalam SQL Server, bukan MySQL. Selain beberapa perubahan sintaks yang sangat kecil, itu harus bekerja sama.

Pengaturan umum dan data sampel untuk keempat metode

CREATE TABLE #example 
(
    id int NOT NULL DEFAULT '0',
    borrower_id int NOT NULL,
    started datetime NULL DEFAULT NULL,
    closed datetime NULL DEFAULT NULL,
    dead datetime NULL DEFAULT '0000-00-00 00:00:00'
);

CREATE TABLE #result 
(   
    borrower_id int NOT NULL DEFAULT '0',    
    started datetime NULL DEFAULT NULL,    
    ended datetime NULL DEFAULT NULL 
);    

INSERT INTO #example 
    (id, borrower_id, started, closed, dead) 
VALUES 
    (7714,238846,'2015-01-27 15:14:50','2015-02-02 14:14:13',NULL), 
    (7882,238846,'2015-01-28 13:25:58',NULL,'2015-05-15 12:16:07'), 
    (13190,259140,'2015-03-17 10:11:44',NULL,'2015-03-18 07:31:57'), 
    (13192,259140,'2015-03-17 10:12:17',NULL,'2015-03-18 11:46:46'), 
    (13194,259140,'2015-03-17 10:12:53',NULL,'2015-03-18 11:46:36'), 
    (14020,259140,'2015-03-23 14:32:16','2015-03-24 15:57:32',NULL), 
    (17124,242650,'2015-04-16 16:19:08','2015-04-16 16:21:06',NULL), 
    (19690,238846,'2015-05-15 13:17:31',NULL,'2015-05-27 13:56:43'), 
    (20038,242650,'2015-05-19 15:38:17',NULL,NULL), 
    (20040,242650,'2015-05-19 15:39:58',NULL,'2015-05-21 12:01:02'), 
    (20302,242650,'2015-05-21 13:09:06',NULL,NULL), 
    (20304,242650,'2015-05-21 13:09:54',NULL,NULL), 
    (20306,242650,'2015-05-21 13:10:19',NULL,NULL), 
    (20308,242650,'2015-05-21 13:12:20',NULL,NULL), 
    (21202,238846,'2015-05-29 16:47:29',NULL,NULL), 
    (21204,238846,'2015-05-29 16:47:56',NULL,NULL), 
    (21208,238846,'2015-05-29 17:05:15',NULL,NULL), 
    (21210,238846,'2015-05-29 17:05:55',NULL,NULL), 
    (21918,242650,'2015-06-04 17:04:29',NULL,'2015-06-12 15:47:23'); 

1. CASCADING - MENGGUNAKAN solusi RECORD TERTUTUP

Ini adalah solusi yang saya yakin penanya sedang mencari & mencocokkan hasilnya.

select *
into #temp1
from #example

while (select count(1) from #temp1)>0
begin
    --Grab only one user's records and place into a temp table to work with
    declare @curUser int
    set @curUser=(select min(borrower_id) from #temp1)

    select * 
    into #temp2
    from #temp1 t1
    where t1.borrower_id=@curUser

    while(select count(1) from #temp2)>0
    begin
        --Grab earliest start date and use as basis for 15 day window (#2 rule)
        --Use the record as basis for rules 3 and 4
        declare @minTime datetime
        set @minTime=(select min(started) from #temp2)

        declare @maxTime datetime
        set @maxTime=@minTime

        declare @curId int
        set @curId=(select min(id) from #temp2 where started=@minTime)

        select * 
        into #temp3
        from #temp2 t2
        where t2.id=@curId

        --Remove earliest record from pool of potential records to check rules against
        delete 
        from #temp2 
        where id=@curId

        --Insert all records within 15 days of start date, then remove record from pool
        while (select count(1) 
                from #temp2 t2 
                where t2.started<=DATEADD(day,15,@maxTime) 
                    or t2.closed<=DATEADD(day,15,@maxTime) 
                    or t2.dead<=DATEADD(day,15,@maxTime)  )>0
        begin
            insert into #temp3
            select *
            from #temp2 t2
            where t2.started<=DATEADD(day,15,@maxTime)  or t2.closed<=DATEADD(day,15,@maxTime)  or t2.dead<=DATEADD(day,15,@maxTime) 

            delete
            from #temp2
            where started<=DATEADD(day,15,@maxTime)  or closed<=DATEADD(day,15,@maxTime)  or dead<=DATEADD(day,15,@maxTime) 

            --set new max time from any column
            if (select max(started) from #temp3)>@maxTime
                set @maxTime=(select max(started) from #temp3)
            if (select max(closed) from #temp3)>@maxTime
                set @maxTime=(select max(started) from #temp3)
            if (select max(dead) from #temp3)>@maxTime
                set @maxTime=(select max(started) from #temp3)

        end

        --Calculate end time according to rule #3
        declare @end datetime 
        set @end = null
        set @end=(select min(closed) from #temp3)

        if @end is not null
        begin
            set @minTime=(select started from #temp3 where closed=@end)
        end

        if @end is null
        begin
            if(select count(1) from #temp3 where dead is null)=0
            set @end= (select max(dead) from #temp3)
        end

        insert into #result (borrower_id,started,ended)
        values (@curUser,@minTime,@end)

        drop table #temp3
    end

    --Done with the one user, remove him from temp table and iterate thru to the next user
    delete  
    from #temp1 
    where borrower_id=@curUser    

    drop table #temp2

end

drop table #temp1

drop table #example

select * from #result order by started

drop table #result

2. NON-CASCADING - MENGGUNAKAN solusi RECORD TERTUTUP

Mulai dihitung dengan tanggal tutup pertama saat tersedia, kemudian pada tanggal awal paling awal.

select *
into #temp1
from #example

while (select count(1) from #temp1)>0
begin
    --Grab only one user's records and place into a temp table to work with
    declare @curUser int
    set @curUser=(select min(borrower_id) from #temp1)

    select * 
    into #temp2
    from #temp1 t1
    where t1.borrower_id=@curUser

    while(select count(1) from #temp2)>0
    begin
        --Grab earliest start date and use as basis for 15 day window (#2 rule)
        --Use the record as basis for rules 3 and 4
        declare @minTime datetime
        set @minTime=(select min(started) from #temp2)

        declare @curId int
        set @curId=(select min(id) from #temp2 where started=@minTime)

        select * 
        into #temp3
        from #temp2 t2
        where t2.id=@curId

        --Remove earliest record from pool of potential records to check rules against
        delete 
        from #temp2 
        where id=@curId

        --Insert all records within 15 days of start date, then remove record from pool
        insert into #temp3
        select *
        from #temp2 t2
        where t2.started<=DATEADD(day,15,@minTime)

        delete
        from #temp2
        where started<=DATEADD(day,15,@minTime)

        --Insert all records within 15 days of closed, then remove record from pool
        insert into #temp3
        select *
        from #temp2 t2
        where t2.closed<=DATEADD(day,15,@minTime)

        delete
        from #temp2
        where closed<=DATEADD(day,15,@minTime)

        --Insert all records within 15 days of dead, then remove record from pool
        insert into #temp3
        select *
        from #temp2 t2
        where t2.dead<=DATEADD(day,15,@minTime)

        delete
        from #temp2
        where dead<=DATEADD(day,15,@minTime)

        --Calculate end time according to rule #3
        declare @end datetime 
        set @end = null
        set @end=(select min(closed) from #temp3)

        if @end is not null
        begin
            set @minTime=(select started from #temp3 where closed=@end)
        end

        if @end is null
        begin
            if(select count(1) from #temp3 where dead is null)=0
            set @end= (select max(dead) from #temp3)
        end

        insert into #result (borrower_id,started,ended)
        values (@curUser,@minTime,@end)

        drop table #temp3
    end

    --Done with the one user, remove him from temp table and iterate thru to the next user
    delete  
    from #temp1 
    where borrower_id=@curUser


    drop table #temp2

end

drop table #temp1

drop table #example

select * from #result

drop table #result

3. NON-CASCADING - MENGGUNAKAN solusi DATE AWAL

Mulai dihitung berdasarkan tanggal paling awal saja.

select *
into #temp1
from #example

while (select count(1) from #temp1)>0
begin
    --Grab only one user's records and place into a temp table to work with
    declare @curUser int
    set @curUser=(select min(borrower_id) from #temp1)

    select * 
    into #temp2
    from #temp1 t1
    where t1.borrower_id=@curUser

    while(select count(1) from #temp2)>0
    begin
        --Grab earliest start date and use as basis for 15 day window (#2 rule)
        --Use the record as basis for rules 3 and 4
        declare @minTime datetime
        set @minTime=(select min(started) from #temp2)

        declare @curId int
        set @curId=(select min(id) from #temp2 where started=@minTime)

        select * 
        into #temp3
        from #temp2 t2
        where t2.id=@curId

        --Remove earliest record from pool of potential records to check rules against
        delete 
        from #temp2 
        where id=@curId

        --Insert all records within 15 days of start date, then remove record from pool
        insert into #temp3
        select *
        from #temp2 t2
        where t2.started<=DATEADD(day,15,@minTime) or t2.closed<=DATEADD(day,15,@minTime) or t2.dead<=DATEADD(day,15,@minTime)

        delete
        from #temp2
        where started<=DATEADD(day,15,@minTime) or closed<=DATEADD(day,15,@minTime) or dead<=DATEADD(day,15,@minTime)

        --Calculate end time according to rule #3
        declare @end datetime 
        set @end = null

        set @end=(select min(closed) from #temp3)

        if @end is null
        begin
            if(select count(1) from #temp3 where dead is null)=0
            set @end= (select max(dead) from #temp3)
        end

        insert into #result (borrower_id,started,ended)
        values (@curUser,@minTime,@end)

        drop table #temp3
    end

    --Done with the one user, remove him from temp table and itterate thru to the next user
    delete  
    from #temp1 
    where borrower_id=@curUser    

    drop table #temp2

end

drop table #temp1

drop table #example

select * from #result

drop table #result

4. CASCADING - MENGGUNAKAN solusi DATE AWAL

Mulai dihitung berdasarkan tanggal paling awal saja.

select *
into #temp1
from #example

while (select count(1) from #temp1)>0
begin
--Grab only one user's records and place into a temp table to work with
declare @curUser int
set @curUser=(select min(borrower_id) from #temp1)

select * 
into #temp2
from #temp1 t1
where t1.borrower_id=@curUser

while(select count(1) from #temp2)>0
begin
    --Grab earliest start date and use as basis for 15 day window (#2 rule)
    --Use the record as basis for rules 3 and 4
        declare @minTime datetime
    set @minTime=(select min(started) from #temp2)


    declare @maxTime datetime
    set @maxTime=@minTime

    declare @curId int
    set @curId=(select min(id) from #temp2 where started=@minTime)

    select * 
    into #temp3
    from #temp2 t2
    where t2.id=@curId

    --Remove earliest record from pool of potential records to check rules against
    delete 
    from #temp2 
    where id=@curId

    --Insert all records within 15 days of start date, then remove record from pool
    while (select count(1) 
            from #temp2 t2 
            where t2.started<=DATEADD(day,15,@maxTime) 
                or t2.closed<=DATEADD(day,15,@maxTime) 
                or t2.dead<=DATEADD(day,15,@maxTime)  )>0
    begin
        insert into #temp3
        select *
        from #temp2 t2
        where t2.started<=DATEADD(day,15,@maxTime)  or t2.closed<=DATEADD(day,15,@maxTime)  or t2.dead<=DATEADD(day,15,@maxTime) 

        delete
        from #temp2
        where started<=DATEADD(day,15,@maxTime)  or closed<=DATEADD(day,15,@maxTime)  or dead<=DATEADD(day,15,@maxTime) 

        --set new max time from any column
        if (select max(started) from #temp3)>@maxTime
            set @maxTime=(select max(started) from #temp3)
        if (select max(closed) from #temp3)>@maxTime
            set @maxTime=(select max(started) from #temp3)
        if (select max(dead) from #temp3)>@maxTime
            set @maxTime=(select max(started) from #temp3)

    end

    --Calculate end time according to rule #3
    declare @end datetime 
    set @end = null

    set @end=(select min(closed) from #temp3)

    if @end is null
    begin
        if(select count(1) from #temp3 where dead is null)=0
        set @end= (select max(dead) from #temp3)
    end

    insert into #result (borrower_id,started,ended)
    values (@curUser,@minTime,@end)

    drop table #temp3
end

--Done with the one user, remove him from temp table and iterate thru to the next user
delete  
from #temp1 
where borrower_id=@curUser

drop table #temp2

end

drop table #temp1

drop table #example

select * from #result order by started

drop table #result
Anthony Genovese
sumber
-2

Saya khawatir bahwa kita mungkin tidak memiliki gambaran yang jelas tentang bagaimana suatu kelompok didefinisikan. Saya hanya mengatakan ini karena, tergantung pada beberapa kondisi yang tidak disebutkan, tanggal di atas akan membentuk satu kelompok tunggal raksasa, atau 3 kelompok di mana satu kelompok mendominasi set.

Kondisi pengelompokan yang hilang?

1) Apakah aturan 15 hari ini menurun? Jika catatan Ydimulai 10 hari setelah catatan lain X, dan kemudian ada catatan lain Zdimulai 10 hari setelah itu, maka apakah ini membentuk satu kelompok yang terdiri dari tiga catatan X,Y,Z, atau dua kelompok yang masing-masing berisi dua catatan X,Ydan Y,Z? Saya membuat asumsi bahwa aturan 15 hari itu mengalir membentuk kelompok yang lebih besar.

2) Apakah tanggal termasuk? Misalnya, jika satu catatan memiliki tanggal mulai dan kemudian tanggal mati berbulan-bulan kemudian, apakah semua hari dalam rentang itu digabungkan ke dalam grup? Saya memperlakukan kedua kemungkinan dalam analisis singkat saya di bawah ini.

Pengelompokan potensial

Jadi, jika kita mulai dengan id 7714, kita melihat bahwa tanggal mulai adalah 1/27. Jelas, entri berikutnya 7882mulai 1/28 jatuh di grup ini. Namun perhatikan bahwa 7882berakhir pada 5/15, jadi apa pun yang dimulai dalam 15 hari dari 5/15 harus ditambahkan ke grup.

Dengan demikian, 19690melalui 21210ditambahkan ke grup, yang melalui cascading mengarah ke 21918selanjutnya ditambahkan ke grup. Cascading telah menghabiskan hampir semua entri di set. Sebut ini GROUP A.

Namun, jika pengelompokan itu termasuk tanggal juga, semua entri dari 13190hingga 17124juga harus menjadi milik GROUP A, dan sekarang semua id dalam satu grup.

Jika tanggal dari GROUP Atidak inklusif, tetapi sebenarnya secara ketat mematuhi '15 hari setelah' aturan dengan Cascading, maka sebagai gantinya Anda akan memiliki kelompok kedua terdiri dari 13190melalui 14020, dan kelompok ketiga dengan single entry, 17124.

Pada dasarnya, pertanyaan saya adalah, apakah ada yang cocok dengan pengelompokan yang Anda maksudkan, atau adakah informasi lain yang tidak ada dalam definisi grup? Saya minta maaf atas jawaban yang bertele-tele, tetapi sepertinya output tentatif yang Anda minta tidak memenuhi definisi pengelompokan Anda.

Dengan klarifikasi, saya yakin kita bisa menyelesaikan masalah ini.

Chris
sumber
Bagaimana jika saya menyingkirkan aturan 15 hari sekaligus? Apakah itu menyederhanakan masalah?
Noah Goodrich
2
Juga, saya pikir Anda melewatkan sedikit tentang memberikan prioritas pada tanggal tertutup pertama dari tanggal mati terakhir. Akibatnya, untuk pengelompokan pertama dimulai pada 1/27, tanggal penutupan 2/2 menjadi akhir dari grup dan bukan 5/15.
Noah Goodrich
Astaga, Anda benar, saya salah menafsirkan apa yang Anda katakan tentang yang pertama kali ditutup / mati ... Maaf, saya sedang mengerjakan malam terakhir ini sekitar pukul 12:30 malam waktu Pasifik, jadi saya mungkin sedikit ngantuk. :) Juga, pengelompokan tambahan berdasarkan data pengguna dapat membantu, saya pikir. Saya akan memberikan sedikit pemikiran lagi, dan mencoba untuk kembali kepada Anda.
Chris