AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 104622
Accepted
Noah Goodrich
Noah Goodrich
Asked: 2015-06-20 14:19:06 +0800 CST2015-06-20 14:19:06 +0800 CST 2015-06-20 14:19:06 +0800 CST

Consulta para determinar as datas de início e término com base na sobreposição de tempo

  • 772

Dados os seguintes dados:

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

Preciso construir um conjunto de dados que atenda às seguintes regras:

  1. Os grupos são definidos primeiro por user_id, então devemos comparar apenas os registros do mesmouser_id
  2. Todos os registros que começaram pelo menos 15 dias após qualquer outro registro ter sido iniciado, fechado ou morto devem ser contados como grupo.
  3. De cada grupo, o final deve ser calculado como o primeiro registro fechado ou todos os registros têm um valor para mortos e tomamos a maior data da coluna morta.
  4. Se um registro não for iniciado dentro de 15 dias após o início ou término de outro grupo, ele iniciará um novo agrupamento.

Provisoriamente, acredito que meus dados devem ficar assim:

usuário_id | começou | fim
-------------------------------------------------- ----
238846 | 27/01/2015 15:14:50 | 2015-02-02 14:14:13
259140 | 23/03/2015 14:32:16 | 2015-03-24 15:57:32
242650 | 16/04/2015 16:19:08 | 16/04/2015 16:21:06
242650 | 21/05/2015 13:09:06 | NULO
238846 | 15/05/2015 13:17:31 | NULO

Alguém pode fornecer algumas orientações sobre como criar uma consulta para atender a essas condições?

Aqui está um link para as instruções DDL e DML para os dados apresentados nesta pergunta.

Como alternativa, poderíamos pular as regras nº 2 e nº 4 e simplesmente declarar que apenas os registros que se sobrepõem devem ser incluídos. A regra mais importante é que, em um determinado conjunto, se houver uma data fechada, ela se tornará o fim do conjunto e não a maior data morta.

mysql
  • 2 2 respostas
  • 2383 Views

2 respostas

  • Voted
  1. Best Answer
    Anthony Genovese
    2016-10-19T09:02:59+08:002016-10-19T09:02:59+08:00

    Devido à falta de clareza na questão, cheguei a quatro soluções diferentes. As soluções diferem em:

    1. Se você deve "cascatar" de acordo com a resposta de Chris
    2. Quando você tiver uma data fechada, use a data mais antiga para esse grupo ou a data inicial para o registro que está fechado.

    Observe que isso é feito no SQL Server, não no MySQL. Além de algumas alterações de sintaxe muito pequenas, deve funcionar da mesma forma.

    Configuração comum e dados de amostra para todos os quatro métodos

    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 - USANDO A SOLUÇÃO DE REGISTRO FECHADO

    Esta é a solução que acredito que o solicitante está procurando e corresponde aos seus resultados.

    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. SEM CASCATA - USANDO solução de REGISTRO FECHADO

    Início calculado pela primeira data de fechamento quando disponível e, em seguida, pela primeira data de início.

    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. SEM CASCATA - USANDO a solução EARLIEST DATE

    Início calculado apenas pela data mais antiga.

    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 - USING EARLIEST DATE solution

    Start calculated by earliest date only.

    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
    
    • 3
  2. Chris
    2015-06-20T23:08:40+08:002015-06-20T23:08:40+08:00

    Preocupa-me que não tenhamos uma imagem clara de como um grupo é definido. Digo isso apenas porque, dependendo de algumas condições não declaradas, as datas acima formarão um único grupo gigante ou 3 grupos onde um grupo domina o conjunto.

    Faltando condições de agrupamento?

    1) Essa regra de 15 dias é cascata? Se um registro Ycomeça 10 dias após outro registro Xe, em seguida, há outro registro Ziniciado 10 dias depois disso, isso forma um grupo de três registros X,Y,Zou dois grupos, cada um contendo dois registros X,Ye Y,Z? Eu fiz a suposição de que as regras de 15 dias se propagam para formar grupos maiores.

    2) As datas são inclusivas? Por exemplo, se um registro tiver uma data inicial e uma data final muitos meses depois, todos os dias dentro desse intervalo serão mesclados no grupo? Trato ambas as possibilidades em minha análise rápida abaixo.

    Agrupamentos potenciais

    Portanto, se começarmos com id 7714, veremos que a data de início é 27/01. Claramente, a próxima entrada 7882começando em 28/01 cai neste grupo. Observe, no entanto, que 7882termina em 15/05, portanto, qualquer coisa que comece dentro de 15 dias a partir de 15/05 deve ser adicionada ao grupo.

    Assim, 19690por meio 21210da inclusão no grupo, que por meio de cascata leva à 21918inclusão posterior no grupo. A cascata consumiu quase todas as entradas do conjunto. Chame isso GROUP A.

    Se, no entanto, o agrupamento também incluir a data, todas as entradas de 13190até 17124também devem pertencer a GROUP A, e agora todos os ids estão em um único grupo.

    Se as datas de GROUP Anão forem inclusivas, mas na verdade aderirem estritamente à regra '15 dias depois' com cascata, então, em vez disso, você teria um segundo grupo composto por 13190até 14020, e um terceiro grupo com uma única entrada, 17124.

    Basicamente, minha pergunta é: algum deles corresponde ao agrupamento pretendido ou há alguma outra informação que está faltando na definição do grupo? Sinto muito por uma resposta tão prolixa, mas não parece que sua saída solicitada provisória atenda à sua definição de agrupamento.

    Com esclarecimentos, tenho certeza de que podemos resolver esse problema.

    • -2

relate perguntas

  • Existem ferramentas de benchmarking do MySQL? [fechado]

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

  • Quando é o momento certo para usar o MariaDB em vez do MySQL e por quê?

  • Como um grupo pode rastrear alterações no esquema do banco de dados?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve