在非连续范围内查找开始日期和结束日期

时间:2012-06-16 15:18:41

标签: sql tsql sql-server-2005

我需要找到定义为的范围的开始和结束日期:开始日期是第一个日期,结束日期是后续日期是结束日期后两个月或更长时间的第一个日期。可能存在多个可能的范围

我的表格结构如下:

ID        int  identity(1,1),
fk_ID     char(9),
dateField datetime

数据如下:

1     a     2012-01-01
2     a     2012-01-05
3     a     2012-01-12
4     b     2012-02-01
5     a     2012-04-01
6     b     2012-05-01
7     a     2012-05-30

预期输出如下:

fk_id    startdate    enddate
a        2012-01-01   2012-01-12
a        2012-04-01   2012-05-30
b        2012-02-01   2012-02-01
b        2012-05-01   null

编辑: 通过执行以下操作:

CREATE TABLE #temp
(
    autonum     int     identity(1,1),
    id          char(9),
    sd          datetime
)

insert into #temp (id, sd) values ('a', '2012-01-01')
insert into #temp (id, sd) values ('a', '2012-01-05')
insert into #temp (id, sd) values ('a', '2012-01-12')
insert into #temp (id, sd) values ('a', '2012-03-01')
insert into #temp (id, sd) values ('a', '2012-04-03')
insert into #temp (id, sd) values ('a', '2012-06-06')
insert into #temp (id, sd) values ('b', '2012-02-12')
insert into #temp (id, sd) values ('b', '2012-02-15')
insert into #temp (id, sd) values ('b', '2012-03-01')
insert into #temp (id, sd) values ('b', '2012-04-03')
insert into #temp (id, sd) values ('b', '2012-06-01')

select t1.id, null as previousend, min(t1.sd) as nextstart
from #temp t1
group by t1.id
union
select t1.id, t1.sd as enddate, (select min(t2.sd) from #temp t2 where t1.id=t2.id and    t2.sd>t1.sd) as nextstart
from #temp t1
where (select min(t2.sd) from #temp t2 where t1.id=t2.id and t2.sd>t1.sd) >= dateadd(month, 2, t1.sd)
union
select t1.id, max(t1.sd), null
from #temp t1
group by t1.id

drop table #temp

我可以得到这样的输出:

id        previousend             nextstart
--------- ----------------------- -----------------------
a         NULL                    2012-01-01 00:00:00.000
a         2012-04-03 00:00:00.000 2012-06-06 00:00:00.000
a         2012-06-06 00:00:00.000 NULL
b         NULL                    2012-02-12 00:00:00.000
b         2012-06-01 00:00:00.000 NULL

这是非常接近的,但理想情况下,范围的开始和结束日期将在行上。

2 个答案:

答案 0 :(得分:2)

考虑到问题的所有变化,这是我最好的猜测。我仍然发现问题非常混乱,分裂,并且两个案例的预期结果似乎不匹配。有了这个查询:

;WITH x AS 
(
  SELECT a.id, sd = a.sd, ed = b.sd, rn1 = ROW_NUMBER() OVER 
    (PARTITION BY a.id, a.sd ORDER BY a.sd)
  FROM #temp AS a
  LEFT OUTER JOIN #temp AS b
  ON a.id = b.id
  AND b.sd >= a.sd
  AND b.sd <= DATEADD(MONTH, 2, a.sd)
),
y AS 
(SELECT id, sd, 
  ed = (SELECT MAX(ed) FROM x AS x2 
    WHERE x.id = x2.id AND x2.sd <= DATEADD(MONTH, 2, x.sd)
  )
FROM x
WHERE rn1 = 1
),
z AS 
(
  SELECT id, sd = MIN(sd), ed
  FROM y GROUP BY id, ed
)
SELECT id, sd, ed /* = CASE 
  WHEN ed > sd OR (sd = ed AND NOT EXISTS 
  (SELECT 1 FROM z AS z2 WHERE z2.id = z.id AND z.sd > z2.sd)) THEN ed END 
*/
FROM z
ORDER BY id, sd;

第一组数据的结果:

INSERT #temp (id, sd) VALUES
('a','2012-01-01'),
('a','2012-01-05'),
('a','2012-01-12'),
('b','2012-02-01'),
('a','2012-04-01'),
('b','2012-05-01'),
('a','2012-05-30');

如下:

id  sd          ed
a   2012-01-01  2012-01-12
a   2012-04-01  2012-05-30
b   2012-02-01  2012-02-01
b   2012-05-01  2012-05-01

第二集:

insert into #temp (id, sd) values ('a', '2012-01-01')
insert into #temp (id, sd) values ('a', '2012-01-05')
insert into #temp (id, sd) values ('a', '2012-01-12')
insert into #temp (id, sd) values ('a', '2012-03-01')
insert into #temp (id, sd) values ('a', '2012-04-03')
insert into #temp (id, sd) values ('a', '2012-06-06')
insert into #temp (id, sd) values ('b', '2012-02-12')
insert into #temp (id, sd) values ('b', '2012-02-15')
insert into #temp (id, sd) values ('b', '2012-03-01')
insert into #temp (id, sd) values ('b', '2012-04-03')
insert into #temp (id, sd) values ('b', '2012-06-01')

如下:

id  sd          ed
a   2012-01-01  2012-04-03
a   2012-06-06  2012-06-06
b   2012-02-12  2012-06-01

如果取消注释CASE块,您将获得开始日期和结束日期相同的结束日期的NULL。正如我多次提出的那样,你的问题是分裂的,你想要的结果似乎不匹配,所以我不确定答案是正确的。

答案 1 :(得分:1)

尝试在Fiddle上的第二个并且远非优雅,但似乎与最终记录不同,结束日期不是NULL:

CREATE TABLE temp
(
    id          char(9),
    d          datetime
);

insert into temp (id, d) values ('a', '2012-01-01');
insert into temp (id, d) values ('a', '2012-01-05');
insert into temp (id, d) values ('a', '2012-01-12');
insert into temp (id, d) values ('a', '2012-04-01');
insert into temp (id, d) values ('a', '2012-05-30');
insert into temp (id, d) values ('b', '2012-02-01');
insert into temp (id, d) values ('b', '2012-05-01');


SELECT 
   x.id ,
   min(x.sd) sd ,
   x.ed
FROM
  (SELECT 
      a.id ,
      a.sd ,
      max(a.ed) ed
   FROM
      ( 
      SELECT 
          j.id ,
          j.d sd ,
          q.D ed
       FROM temp j
          JOIN temp q 
             ON 
             j.id = q.id
             AND j.d <= q.d
       GROUP BY j.id ,
           j.d ,
           q.d 
      ) a
   WHERE datediff(m,a.sd,a.ed)<=2
   GROUP BY a.id ,
        a.sd
         )x
GROUP BY x.id ,
         x.ed
ORDER BY x.id ,
         min(x.sd) ,
         x.ed