Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Como identificar a primeira lacuna em vários intervalos de datas de início e término para cada membro distinto no T-SQL


Tente isto:http://www.sqlfiddle.com/#!3/c3365/ 20
with s as
(
  select *, row_number() over(partition by membercode order by startdate) rn
  from tbl
)
,gaps as
(
select a.membercode, a.startdate, a.enddate, b.startdate as nextstartdate
  ,datediff(d, a.enddate, b.startdate) as gap
from s a
join s b on b.membercode = a.membercode and b.rn = a.rn + 1
)
select membercode 
from gaps
group by membercode
having sum(case when gap <= 1 then 1 end) = count(*);

Veja a progressão da consulta aqui:http://www.sqlfiddle.com/#!3/ c3365/20

Como funciona, compare a data de término atual com a próxima data de início e verifique o intervalo de datas:
with s as
(
  select *, row_number() over(partition by membercode order by startdate) rn
  from tbl
)
select a.membercode, a.startdate, a.enddate, b.startdate as nextstartdate
  ,datediff(d, a.enddate, b.startdate) as gap
from s a
join s b on b.membercode = a.membercode and b.rn = a.rn + 1;

Resultado:
| MEMBERCODE |  STARTDATE |    ENDDATE | NEXTSTARTDATE | GAP |
--------------------------------------------------------------
|          1 | 2010-01-15 | 2010-01-20 |    2010-01-19 |  -1 |
|          1 | 2010-01-19 | 2010-01-22 |    2010-01-20 |  -2 |
|          1 | 2010-01-20 | 2010-01-25 |    2010-01-26 |   1 |
|          2 | 2010-01-20 | 2010-01-25 |    2010-01-30 |   5 |
|          2 | 2010-01-30 | 2010-02-05 |    2010-02-04 |  -1 |

Em seguida, verifique se um membro tem a mesma contagem de reivindicações sem lacunas em relação ao total de reivindicações:
with s as
(
  select *, row_number() over(partition by membercode order by startdate) rn
  from tbl
)
,gaps as
(
select a.membercode, a.startdate, a.enddate, b.startdate as nextstartdate
  ,datediff(d, a.enddate, b.startdate) as gap
from s a
join s b on b.membercode = a.membercode and b.rn = a.rn + 1
)
select membercode, count(*) as count, sum(case when gap <= 1 then 1 end) as gapless_count
from gaps
group by membercode;

Resultado:
| MEMBERCODE | COUNT | GAPLESS_COUNT |
--------------------------------------
|          1 |     3 |             3 |
|          2 |     2 |             1 |

Por fim, filtre-os, membros sem lacunas em suas reivindicações:
with s as
(
  select *, row_number() over(partition by membercode order by startdate) rn
  from tbl
)
,gaps as
(
select a.membercode, a.startdate, a.enddate, b.startdate as nextstartdate
  ,datediff(d, a.enddate, b.startdate) as gap
from s a
join s b on b.membercode = a.membercode and b.rn = a.rn + 1
)
select membercode 
from gaps
group by membercode
having sum(case when gap <= 1 then 1 end) = count(*);

Resultado:
| MEMBERCODE |
--------------
|          1 |

Observe que você não precisa fazer COUNT(*) > 1 para detectar membros com 2 ou mais reivindicações. Em vez de usar LEFT JOIN , usamos JOIN , isso descartará automaticamente os membros que ainda não receberam uma segunda reivindicação. Aqui está a versão (mais longa) se você optar por usar LEFT JOIN em vez disso (mesma saída acima):
with s as
(
select *, row_number() over(partition by membercode order by startdate) rn
from tbl
)
,gaps as
(
select a.membercode, a.startdate, a.enddate, b.startdate as nextstartdate
,datediff(d, a.enddate, b.startdate) as gap
from s a
left join s b on b.membercode = a.membercode and b.rn = a.rn + 1
)
select membercode 
from gaps
group by membercode
having sum(case when gap <= 1 then 1 end) = count(gap)
and count(*) > 1; -- members who have two ore more claims only

Veja como ver os dados da consulta acima antes de filtrar:
with s as
(
  select *, row_number() over(partition by membercode order by startdate) rn
  from tbl
)
,gaps as
(
select a.membercode, a.startdate, a.enddate, b.startdate as nextstartdate
  ,datediff(d, a.enddate, b.startdate) as gap
from s a
left join s b on b.membercode = a.membercode and b.rn = a.rn + 1
)
select * from gaps;

Resultado:
| MEMBERCODE |  STARTDATE |    ENDDATE | NEXTSTARTDATE |    GAP |
-----------------------------------------------------------------
|          1 | 2010-01-15 | 2010-01-20 |    2010-01-19 |     -1 |
|          1 | 2010-01-19 | 2010-01-22 |    2010-01-20 |     -2 |
|          1 | 2010-01-20 | 2010-01-25 |    2010-01-26 |      1 |
|          1 | 2010-01-26 | 2010-01-30 |        (null) | (null) |
|          2 | 2010-01-20 | 2010-01-25 |    2010-01-30 |      5 |
|          2 | 2010-01-30 | 2010-02-05 |    2010-02-04 |     -1 |
|          2 | 2010-02-04 | 2010-02-15 |        (null) | (null) |
|          3 | 2010-02-15 | 2010-03-02 |        (null) | (null) |

EDITAR no esclarecimento de requisitos:

Em seu esclarecimento, você deseja incluir membros que ainda não tenham uma segunda reivindicação, faça isso:http://sqlfiddle.com/#!3/c3365/22
with s as
(
select *, row_number() over(partition by membercode order by startdate) rn
from tbl
)
,gaps as
(
select a.membercode, a.startdate, a.enddate, b.startdate as nextstartdate
,datediff(d, a.enddate, b.startdate) as gap
from s a
left join s b on b.membercode = a.membercode and b.rn = a.rn + 1
)
select membercode 
from gaps
group by membercode
having sum(case when gap <= 1 then 1 end) = count(gap)
-- members who have yet to have a second claim are valid too
or count(nextstartdate) = 0; 

Resultado:
| MEMBERCODE |
--------------
|          1 |
|          3 |

A técnica é contar o nextstartdate do membro , se eles não tiverem a próxima data de início (ou seja, count(nextstartdate) = 0 ) então elas são apenas reivindicações únicas e válidas também, então basta anexar este OR doença:
or count(nextstartdate) = 0; 

Na verdade, a condição abaixo também será suficiente, mas eu queria tornar a consulta mais autodocumentada, portanto, recomendo contar com a próxima data de início do membro. Aqui está uma condição alternativa para a contagem de membros que ainda não receberam uma segunda reivindicação:
or count(*) = 1;

Aliás, também temos que mudar a comparação disso:
sum(case when gap <= 1 then 1 end) = count(*)

para isso (já que estamos usando LEFT JOIN agora):
sum(case when gap <= 1 then 1 end) = count(gap)