我正在努力创建一个查询来汇总日期范围,同时按两个字段连续分组 - 基本上我试图将其转为:
|Key|Valid|DateFrom |DateTo |
| 1| 0|2001-01-01|2001-01-31|
| 1| 0|2001-02-01|2001-02-20|
| 1| 1|2001-02-21|2001-02-28|
| 1| 0|2001-03-01|2001-03-15|
| 2| 1|2001-01-01|2001-01-31|
| 2| 0|2001-02-01|2001-02-20|
| 2| 0|2001-02-21|2001-02-28|
| 2| 1|2001-03-01|2001-03-15|
进入这个:
|Key|Valid|DateFrom |DateTo |
| 1| 0|2001-01-01|2001-02-20|
| 1| 1|2001-02-21|2001-02-28|
| 1| 0|2001-03-01|2001-03-15|
| 2| 1|2001-01-01|2001-01-31|
| 2| 0|2001-02-01|2001-02-28|
| 2| 1|2001-03-01|2001-03-15|
当然,一个简单的min(DateFrom),max(DateTo)组按键,有效不起作用,因为它不遵守日期范围的时间顺序。应该注意的是,每个密钥和有效组中的日期范围没有间隙。
我已经广泛搜索了解决方案(无论是在这里还是在网络上的其他地方),并找到了大量使用OVER和CTE对其进行分组的解决方案(尝试了两者)但我认为问题在于我正在尝试两个不同组的因素。我也尝试将范围转换为单独的日期,但我再也无法按时间顺序将它们按两组进行推广。
任何帮助将不胜感激。感谢。
答案 0 :(得分:0)
您可以先计算关键行(即有效或关键更改的位置),然后链接到该组的最大日期。
编辑 - 重写以处理由Dems标记的角落案例。本节还涉及序列中的空白
with keyItems as (
-- First find all the "Key Frames"
select d.*
from
data d
left outer join data d2
on d.[Key]=d2.[key] and d.valid=d2.valid and d.dateFrom = DateAdd(d,1,d2.dateto)
where d2.[key] is null
),
ordered as (
-- This is to provide a sequence number for the main query against these key frames
select
ROW_NUMBER() over (partition by [key] order by datefrom) as row,
*
from keyItems
),
rangeends([key],row,dateto) as (
select o.[key],o.row-1,MAX(d.DateTo)
from ordered o left outer join data d on d.[key]=o.[key] and d.DateTo < o.DateFrom
group by o.[key],o.row-1
union all
select o.[key],MAX(o.row),MAX(d.dateto)
from ordered o inner join data d on d.[key]=o.[key]
group by o.[key]
)
select
o1.[Key],
o1.Valid,
o1.DateFrom,
coalesce(r.dateto,o1.dateTo) as DateTo
from ordered o1
left outer join rangeends r on r.[key]=o1.[Key] and r.row=o1.row
答案 1 :(得分:0)
我面前没有SQL客户端,但你可以这样做......
WITH
sequenced_data
AS
(
SELECT
ROW_NUMBER() OVER (PARTITION BY Key ORDER BY DateFrom) AS KeyRow,
ROW_NUMBER() OVER (PARTITION BY Key, Valid ORDER BY DateFrom) AS KeyValidRow,
*
FROM
yourData
)
SELECT
Key,
Valid,
MIN(DateFrom) AS DateFrom,
MAX(DatTo) AS DateTo
FROM
sequenced_data
GROUP BY
Key,
Valid,
KeyRow - KeyValidRow
ORDER BY
Key,
MIN(DateFrom)
使用您的数据进行可视化......
|Key|Valid|DateFrom |DateTo |KeyRow|KeyValidRow|KeyRow - KeyValidRow
| 1| 0|2001-01-01|2001-01-31| 1| 1| 0
| 1| 0|2001-02-01|2001-02-20| 2| 2| 0
| 1| 1|2001-02-21|2001-02-28| 3| 1| 2
| 1| 0|2001-03-01|2001-03-15| 4| 3| 1
| 2| 1|2001-01-01|2001-01-31| 1| 1| 0
| 2| 0|2001-02-01|2001-02-20| 2| 1| 1
| 2| 0|2001-02-21|2001-02-28| 3| 2| 1
| 2| 1|2001-03-01|2001-03-15| 4| 2| 2
尽管KeyRow - KeyValidRow
并不一定会告诉您太多,但它确实为每个组提供了明确的值,因此GROUP BY
就足够了。
无论组中有多少条记录,它都能正常工作,但是假设数据中没有间隙或重叠。
答案 2 :(得分:0)
我无法想到使用光标。但这确实有效:
declare @example table (tKey int, Valid int, DateFrom date, DateTo date);
insert into @example values (1, 0, '2001-01-01', '2001-01-31');
insert into @example values (1, 0, '2001-02-01', '2001-02-20');
insert into @example values (1, 1, '2001-02-21', '2001-02-28');
insert into @example values (1, 0, '2001-03-01', '2001-03-15');
insert into @example values (2, 1, '2001-01-01', '2001-01-31');
insert into @example values (2, 0, '2001-02-01', '2001-02-20');
insert into @example values (2, 0, '2001-02-21', '2001-02-28');
insert into @example values (2, 1, '2001-03-01', '2001-03-15');
declare @output table (tKey int, Valid int, DateFrom date, DateTo date);
DECLARE ex_cursor CURSOR FOR
select
tKey,Valid,DateFrom,DateTo
from
@example
order by tKey, DateFrom
DECLARE @tKey int
DECLARE @Valid int
DECLARE @DateFrom date
DECLARE @DateTo date
DECLARE @last_tKey int
DECLARE @last_Valid int
DECLARE @min_Date date
DECLARE @max_Date date
OPEN ex_cursor;
FETCH NEXT FROM ex_cursor
INTO @tKey, @Valid, @DateFrom, @DateTo;
SET @last_tKey = @tKey;
SET @last_Valid = @Valid;
SET @min_Date = @DateFrom;
SET @max_Date = @DateTo;
WHILE @@FETCH_STATUS = 0
BEGIN
IF (@last_tKey <> @tKey OR @last_Valid <> @Valid)
BEGIN
-- output results
INSERT INTO @output SELECT @last_tKey, @last_Valid, @min_Date, @max_Date
-- reset values
SET @last_tKey = @tKey;
SET @last_Valid = @Valid;
SET @min_Date = @DateFrom;
SET @max_Date = @DateTo;
END
ELSE
BEGIN
IF (@DateTo > @max_Date) SET @max_Date = @DateTo
END
FETCH NEXT FROM ex_cursor
INTO @tKey, @Valid, @DateFrom, @DateTo
END
-- output one more time at end
INSERT INTO @output SELECT @last_tKey, @last_Valid, @min_Date, @max_Date
CLOSE ex_cursor;
DEALLOCATE ex_cursor;
SELECT * FROM @output ORDER BY tKey, DateFrom