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 / 191005
Accepted
GWR
GWR
Asked: 2017-11-16 09:39:02 +0800 CST2017-11-16 09:39:02 +0800 CST 2017-11-16 09:39:02 +0800 CST

Agrupando subconjuntos de linhas com valores nulos em um conjunto ordenado

  • 772

Digamos que temos uma tabela onde cada linha é um dia, e é ordenada por esta coluna de dia. Em seguida, deixamos um conjunto de dados de associação que mostra em que dia os membros estavam ativos (e não).

Digamos que nosso conjunto de dados atual seja assim... A associação estava ativa do dia 3 ao 5, inativa do dia 5 ao 8 e ativa do dia 9 em diante, etc.

DAY     DATE        MEMBER  ACTIVE
 1      2017-01-01  123     null
 2      2017-01-02  123     null
 3      2017-01-03  123     2017-01-03
 4      2017-01-04  123     2017-01-04
 5      2017-01-05  123     2017-01-05
 6      2017-01-06  123     null
 7      2017-01-07  123     null
 8      2017-01-08  123     null
 9      2017-01-09  123     2017-01-09
10      2017-01-10  123     2017-01-10

...então ACTIVE=nullsignifica que a associação não estava ativa naqueles dias.

Com esta estrutura de dados, gostaria de chegar a um conjunto "recolhido", mostrando "spans" de tempo inativo/ativo:

MEMBER  MIN(DATE)   MAX(DATE)   STATUS
123,    2017-01-01, 2017-01-02  INACTIVE
123,    2017-01-03, 2017-01-05  ACTIVE
123,    2017-01-06, 2017-01-08  INACTIVE
123,    2017-01-09, 2017-01-10  ACTIVE

Eu tentei usar row_number() para de alguma forma particionar os subconjuntos de um determinado status, mas neste caso, usando min()/ max()sobre as linhas em que ACTIVE é nulo, trata-as como um único grupo, quando na realidade existem vários intervalos distintos de "associação inativa".

Como posso distinguir os períodos de associação inativa uns dos outros para fins de agrupamento? Que técnica posso usar para obter essa saída acima?

Aqui está o script para gerar os dados de origem fictícios:

CREATE TABLE ##SRC (ID INT, D DATE, MEMBER INT, ACTIVE DATE);

INSERT INTO ##SRC (ID, D, MEMBER, ACTIVE)
SELECT 1, '2017-01-01', 123, NULL UNION 
SELECT 2, '2017-01-02', 123, NULL UNION 
SELECT 3, '2017-01-03', 123, '2017-01-03' UNION 
SELECT 4, '2017-01-04', 123, '2017-01-04' UNION 
SELECT 5, '2017-01-05', 123, '2017-01-05' UNION 
SELECT 6, '2017-01-06', 123, NULL UNION 
SELECT 7, '2017-01-07', 123, NULL UNION 
SELECT 8, '2017-01-08', 123, NULL UNION 
SELECT 9, '2017-01-09', 123, '2017-01-09' UNION 
SELECT 10, '2017-01-10',    123, '2017-01-10' 
;
sql-server sql-server-2008-r2
  • 2 2 respostas
  • 147 Views

2 respostas

  • Voted
  1. Best Answer
    Lennart - Slava Ukraini
    2017-11-16T13:04:18+08:002017-11-16T13:04:18+08:00

    Seus dados de amostra não correspondem à sua descrição e me confundiram no início. Como sp_BlitzErik aponta, este é um problema de ilha e lacuna. A solução é bastante simples se você tiver acesso às funções da janela. Primeiro, podemos enumerar a tabela por membro sozinho, vamos chamar isso de full_order (isso é o mesmo que dia, mas vou adicioná-lo para generalidade). Segundo, podemos enumerar a tabela por membro e se eles estavam ativos naquele dia, vamos chamar isso de partial_order

     select day, active, date, member
          , row_number() over (partition by member 
                               order by day) as fullorder
          , row_number() over (partition by member
                              ,case when active is null then 0 else 1 end
                              order by day) as partialorder
     from src
    
    DAY         ACTIVE     MEMBER      FULLORDER            PARTIALORDER        
    
    ----------- ---------- ----------- -------------------- --------------------
          1 -                  123                    1                    1
          2 -                  123                    2                    2
          3 01/03/2017         123                    3                    1
          4 01/04/2017         123                    4                    2
          5 01/05/2017         123                    5                    3
          6 -                  123                    6                    3
          7 -                  123                    7                    4
          8 -                  123                    8                    5
          9 01/09/2017         123                    9                    4
         10 01/10/2017         123                   10                    5
    

    Se a diferença entre full_order e partial_order for alterada, isso significa que active mudou de null para um valor ou vice-versa. Portanto, podemos formar um grupo com essa diferença. Dentro de cada grupo, podemos escolher o min(active) e max(active) para formar um intervalo:

    select member, grp, min(date), max(active) 
    from (
        select day, active, date, member
             , row_number() over (partition by member order by day) 
             - row_number() over (partition by member
                                 ,case when active is null then 0 else 1 end 
                                  order by day) as grp  
        from src
    ) 
    group by member, grp
    
    MEMBER      GRP                  3          4         
    ----------- -------------------- ---------- ----------
        123                    0 01/01/2017 -         
        123                    2 01/03/2017 01/05/2017
        123                    3 01/05/2017 -         
        123                    5 01/08/2017 01/10/2017
    

    Provavelmente é mais fácil adicionar outro nível de aninhamento para obter o resultado desejado:

    select member, min_active
         , coalesce(max_active, min_active) as max_active
         , case when max_active is null then 'INACTIVE' else 'ACTIVE' end as status 
    from (
        select member, grp, min(date) as min_active, max(active) as max_active 
        from (
            select day, active, date, member
                 , row_number() over (partition by member order by day) 
                 - row_number() over (partition by member
                                     ,case when active is null then 0 else 1 end 
                                     order by day) as grp  
            from src
        ) 
        group by member, grp)
    
    MEMBER      MIN_ACTIVE MAX_ACTIVE STATUS  
    ----------- ---------- ---------- --------
        123 01/01/2017 01/01/2017 INACTIVE
        123 01/03/2017 01/05/2017 ACTIVE  
        123 01/05/2017 01/05/2017 INACTIVE
        123 01/08/2017 01/10/2017 ACTIVE  
    
    • 3
  2. KumarHarsh
    2017-11-23T03:48:46+08:002017-11-23T03:48:46+08:00

    Em seguida, deixamos um conjunto de dados de associação que mostra em que dia os membros estavam ativos (e não).

    Desculpe Se entendi errado.você deveria ter postado ambos os dados da tabela então mencione o seu problema.Dessa forma é garantido obter a melhor consulta.

    Estou usando o CTE recursivo na minha consulta, o que pode ser evitado se você tiver a tabela.

    de qualquer forma o script é muito curto

    CREATE TABLE #SRC (ID INT, D DATE, MEMBER INT, ACTIVE DATE);
    
    INSERT INTO #SRC (ID, D, MEMBER, ACTIVE)
    SELECT 1, '2017-01-01', 123, NULL UNION 
    SELECT 2, '2017-01-02', 123, NULL UNION 
    SELECT 3, '2017-01-03', 123, '2017-01-03' UNION 
    SELECT 4, '2017-01-04', 123, '2017-01-04' UNION 
    SELECT 5, '2017-01-05', 123, '2017-01-05' UNION 
    SELECT 6, '2017-01-06', 123, NULL UNION 
    SELECT 7, '2017-01-07', 123, NULL UNION 
    SELECT 8, '2017-01-08', 123, NULL UNION 
    SELECT 9, '2017-01-09', 123, '2017-01-09' UNION 
    SELECT 10, '2017-01-10',    123, '2017-01-10' 
    
    ;with CTE as
    (
    select *, 1 flg  from #SRC where id=1
    
    union ALL
    
    select s.*
    ,case when s.active is null and c.active is null then  flg 
    when s.active is not null and c.active is not null then  flg 
    else flg+1 end   
    from #SRC S 
    inner JOIN cte c on s.member=c.member
    and s.id=c.id +1
    )
    select member,flg,MIN(D) MinD ,max(D)MaxD
    from cte
    group by member,flg
    
    drop table #SRC
    
    • 0

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

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