Truy vấn để xác định ngày bắt đầu và ngày kết thúc dựa trên thời gian chồng lấp


8

Cho các dữ liệu sau:

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

Tôi cần xây dựng một bộ dữ liệu đáp ứng các quy tắc sau:

  1. Các nhóm được xác định trước bởi user_idvì vậy chúng ta chỉ nên so sánh các bản ghi từ cùng mộtuser_id
  2. Tất cả các hồ sơ bắt đầu ít nhất trong vòng 15 ngày kể từ khi bất kỳ hồ sơ nào khác được bắt đầu, đóng hoặc chết nên được tính là nhóm.
  3. Trong mỗi nhóm, kết thúc phải được tính là bản ghi đầu tiên được đóng hoặc tất cả các bản ghi có giá trị cho điểm chết và chúng tôi lấy ngày lớn nhất của cột chết.
  4. Nếu một bản ghi không bắt đầu trong vòng 15 ngày kể từ khi bắt đầu hoặc kết thúc một nhóm khác, thì nó sẽ bắt đầu một nhóm mới.

Về mặt thực tế, tôi tin rằng dữ liệu của tôi sẽ trông như thế này:

user_id | bắt đầu | kết thúc
-------------------------------------------------- ----
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 | VÔ GIÁ TRỊ
238846 | 2015-05-15 13:17:31 | VÔ GIÁ TRỊ

Bất cứ ai cũng có thể cung cấp một số hướng dẫn về cách xây dựng một truy vấn để đáp ứng các điều kiện này?

Đây là một liên kết đến các câu lệnh DDL và DML cho dữ liệu được trình bày trong câu hỏi này.

Ngoài ra, chúng ta có thể bỏ qua các quy tắc # 2 và # 4 và đơn giản hơn là chỉ đưa vào các bản ghi chồng lấp lẫn nhau. Quy tắc quan trọng hơn là trong một tập hợp nhất định, nếu có ngày đóng thì ngày đó sẽ kết thúc và không phải là ngày chết lớn nhất.


Điều này sẽ dễ dàng hơn với một sự thay đổi lược đồ. Không cần hai cột, đóng và chết. Chỉ cần có một cột "kết thúc" và sau đó là một lý do cho kết thúc.
Andrew Brennan

3 ví dụ đầu tiên của bạn có thể được mã hóa thành "Nếu một id bị 'đóng', thì đó là một nhóm cho chính nó. Vì điều đó dường như không làm nổi bật tất cả các quy tắc của bạn, vui lòng thêm các ví dụ khác.
Rick James

Câu trả lời:


3

Do không rõ ràng trong câu hỏi, tôi đã đưa ra bốn giải pháp khác nhau. Các giải pháp khác nhau về:

  1. Cho dù bạn có "xếp tầng" theo câu trả lời của Chris
  2. Khi bạn có một ngày đóng cửa, cho dù bạn sử dụng ngày sớm nhất cho nhóm đó hay ngày bắt đầu cho hồ sơ đã được đóng.

Xin lưu ý điều này được thực hiện trong SQL Server, không phải MySQL. Khác với một số thay đổi cú pháp rất nhỏ, nó sẽ hoạt động như nhau.

Thiết lập chung và dữ liệu mẫu cho cả bốn phương pháp

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 - SỬ DỤNG giải pháp RECORD ĐÓNG

Đây là giải pháp tôi tin rằng người hỏi đang tìm kiếm & phù hợp với kết quả của anh ta.

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. KHÔNG NỀN TẢNG - SỬ DỤNG giải pháp GHI ĐÓNG

Bắt đầu tính theo ngày đóng đầu tiên khi có sẵn, sau đó là ngày bắt đầu sớm nhất.

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. KHÔNG GIỚI HẠN - SỬ DỤNG giải pháp NGÀY TUYỆT VỜI NHẤT

Bắt đầu tính theo ngày sớm nhất.

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 - SỬ DỤNG giải pháp NGÀY LỚN NHẤT

Bắt đầu tính theo ngày sớm nhất.

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

-2

Tôi lo ngại rằng chúng ta có thể không có một bức tranh rõ ràng về cách xác định một nhóm. Tôi chỉ nói điều này bởi vì, tùy thuộc vào một số điều kiện không có căn cứ, những ngày ở trên sẽ tạo thành một nhóm duy nhất khổng lồ hoặc 3 nhóm trong đó một nhóm thống trị tập hợp.

Thiếu điều kiện nhóm?

1) Liệu quy tắc 15 ngày này? Nếu một bản ghi Ybắt đầu 10 ngày sau khi kỷ lục khác X, và sau đó là một kỷ lục Zbắt đầu 10 ngày sau đó, sau đó thực hiện hình thức này một nhóm ba hồ sơ X,Y,Z, hoặc hai nhóm mỗi chứa hai kỷ lục X,YY,Z? Tôi đã đưa ra giả định rằng quy tắc 15 ngày xếp tầng để tạo thành các nhóm lớn hơn.

2) Là những ngày bao gồm? Ví dụ: nếu một bản ghi có ngày bắt đầu và sau đó là ngày chết nhiều tháng sau đó, liệu tất cả các ngày trong phạm vi đó có được hợp nhất vào nhóm không? Tôi xử lý cả hai khả năng trong phân tích nhanh của tôi dưới đây.

Nhóm tiềm năng

Vì vậy, nếu chúng tôi bắt đầu bằng id 7714, chúng tôi thấy rằng ngày bắt đầu là 1/27. Rõ ràng, mục tiếp theo 7882bắt đầu từ 1/28 rơi vào nhóm này. Tuy nhiên, lưu ý rằng 7882kết thúc vào ngày 5/15, do đó, bất cứ điều gì bắt đầu trong vòng 15 ngày kể từ ngày 5/15 phải được thêm vào nhóm.

Do đó, 19690thông qua việc 21210được thêm vào nhóm, thông qua xếp tầng dẫn đến 21918sau đó được thêm vào nhóm. Việc xếp tầng đã tiêu thụ gần như tất cả các mục trong tập hợp. Gọi này GROUP A.

Tuy nhiên, nếu nhóm cũng bao gồm ngày, tất cả các mục từ 13190tối đa 17124cũng phải thuộc về GROUP Avà bây giờ tất cả các id đều nằm trong một nhóm.

Nếu ngày từ GROUP Atrên không đầy đủ, nhưng thực sự nghiêm túc tuân thủ các '15 ngày sau khi' quy tắc với tầng, sau đó thay vào đó bạn sẽ có một nhóm thứ hai gồm 13190thông qua 14020, và một nhóm thứ ba với một mục duy nhất, 17124.

Về cơ bản, câu hỏi của tôi là, có bất kỳ trong số này phù hợp với nhóm dự định của bạn, hoặc có một số thông tin khác chúng tôi thiếu trong định nghĩa nhóm? Tôi xin lỗi vì câu trả lời dài dòng như vậy, nhưng có vẻ như đầu ra được yêu cầu dự kiến ​​của bạn đáp ứng định nghĩa nhóm của bạn.

Với sự làm rõ, tôi chắc chắn rằng chúng ta có thể giải quyết vấn đề này.


Điều gì sẽ xảy ra nếu tôi thoát khỏi quy tắc 15 ngày hoàn toàn? Điều đó sẽ đơn giản hóa vấn đề?

2
Ngoài ra, tôi nghĩ rằng bạn đã bỏ lỡ một chút về việc ưu tiên cho ngày đóng cửa đầu tiên trong ngày chết cuối cùng. Do đó, đối với nhóm đầu tiên bắt đầu từ ngày 1/27, ngày đóng cửa 2/2 trở thành ngày kết thúc của nhóm chứ không phải 5/15.

Rất tiếc, bạn đã đúng, tôi đã hiểu sai những gì bạn nói về lần đóng cửa cuối cùng / người chết cuối cùng ... Xin lỗi, tôi đã làm việc vào tối qua khoảng 12:30 vào ban đêm theo giờ Thái Bình Dương, vì vậy tôi có thể đã có một chút ngái ngủ. :) Ngoài ra, việc nhóm bổ sung theo dữ liệu người dùng có thể giúp ích, tôi nghĩ vậy. Tôi sẽ suy nghĩ thêm một chút và cố gắng quay lại với bạn.
Chris
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.