2011-05-05 40 views
2

我有一個表,其中包含一個事件的IDDate。每行是一個日期。我想,以確定連續的日期範圍,鞏固輸出到顯示ID,StartDate,EndDate確定多個日期範圍的SQL(SQL Server 2000)

ID  Date 
200236 2011-01-02 00:00:00.000 
200236 2011-01-03 00:00:00.000 
200236 2011-01-05 00:00:00.000 
200236 2011-01-06 00:00:00.000 
200236 2011-01-07 00:00:00.000 
200236 2011-01-08 00:00:00.000 
200236 2011-01-09 00:00:00.000 
200236 2011-01-10 00:00:00.000 
200236 2011-01-11 00:00:00.000 
200236 2011-01-12 00:00:00.000 
200236 2011-01-13 00:00:00.000 
200236 2011-01-15 00:00:00.000 
200236 2011-01-16 00:00:00.000 
200236 2011-01-17 00:00:00.000 

輸出會是什麼樣子:

ID  StartDate EndDate 
200236 2011-01-02 2011-01-03 
200236 2011-01-05 2011-01-13 
200236 2011-01-15 2011-01-17 

如何在SQL Server 2000中處理這個有什麼想法?

+0

這是SQL Server嗎? – 2011-05-05 16:56:51

+0

SQL Server 2000 – 2011-05-05 16:58:03

回答

1
SELECT ... 
FROM ... 
WHERE date_column BETWEEN '2011-01-02' AND '2011-01-15' 

也許? Reference

或者你也可以做一個子查詢和使用MAX其中date是<連接下一個記錄=當前日期:

SELECT id, date, (SELECT MAX(date) FROM mytable WHERE date <= mytable.date) AS nextDate 
FROM mytable 

或使用:

SELECT TOP 1 date 
FROM   mytable 
WHERE  date <= mytable.date AND id <> mytable.id 
ORDER BY  date 

由於子查詢,以便在當前記錄之後抓取下一個日期。

+0

列出的數據只是表格的一個示例。有成千上萬的條目(行),我正在提供一個例子。有多個ID,每個ID可以有從1到無限日期的任何地方。 – 2011-05-05 17:00:48

+0

@GeorgeGonzola:因此,使用子選擇並引用當前行的日期來查找下一個日期(然後將該值作爲第三列)。 – 2011-05-05 17:02:54

0

This SO Question可能會幫助你。我直接鏈接到Rob Farley的答案,因爲我覺得這是一個類似的問題。

0

您可以採取的一種方法是添加一個字段,指示序列中的下一個日期。 (將其添加到當前表中或使用臨時表,將基礎數據存儲到臨時表中,然後更新序列中的下一個日期)。

你的出發數據結構將是這個樣子:

ID, PerfDate, NextDate 
200236, 2011-01-02, 2011-01-03 
200236, 2011-01-03, 2011-01-04 
etc. 

然後,您可以使用一系列相關子查詢的滾動數據成所需的輸出:

SELECT ID, StartDate, EndDate 
FROM (
SELECT DISTINCT ID, PerfDate AS StartDate, 
    (SELECT MIN([PerfDate]) FROM [SourceTable] S3 
    WHERE S3.ID = S1.ID 
    AND S3.NextDate > S1.PerfDate 
    AND ISNULL(
     (SELECT MIN(PerfDate) 
     FROM [SourceTable] AS S4 
     WHERE S4.ID = S1.ID 
     AND S4.NextDate > S3.NextDate), S3.NextDate + 1) > S3.NextDate) AS EndDate 
FROM [SourceTable] S1 
WHERE 
    ISNULL(
     (SELECT MAX(NextDate) 
     FROM [SourceTable] S2 
     WHERE S2.ID = S1.ID 
     AND S2.PerfDate < S1.PerfDate), PerfDate -1) < S1.PerfDate)q 
ORDER BY q.ID, q.StartDate 
0

這是我過去做過的方式。這是一個兩步的過程:

  1. 構建候選連續時段的
  2. 如果有任何重疊期,刪除所有,但最長的時期。

下面是一個腳本,顯示它是如何完成的。您可能能夠通過單一[bug,醜陋]的查詢來解決它,但試圖這樣做會讓我的頭部受傷。我使用臨時表,因爲它使調試變得更容易。

drop table #source 
create table #source 
(
    id int  not null , 
    dtCol datetime not null , 

    ----------------------------------------------------------------------- 
    -- ASSUMPTION 1: Each date must be unique for a given ID value. 
    ----------------------------------------------------------------------- 
    unique clustered (id , dtCol) , 

    ----------------------------------------------------------------------- 
    -- ASSUMPTION 2: The datetime column only represents a day. 
    -- The value of the time component is always 00:00:00.000 
    ----------------------------------------------------------------------- 
    check (dtCol = convert(datetime,convert(varchar,dtCol,112),112)) , 

) 
go 

insert #source values(1,'jan 1, 2011') 
insert #source values(1,'jan 4, 2011') 
insert #source values(1,'jan 5, 2011') 
insert #source values(2,'jan 1, 2011') 
insert #source values(2,'jan 2, 2011') 
insert #source values(2,'jan 3, 2011') 
insert #source values(2,'jan 5, 2011') 
insert #source values(3,'jan 1, 2011') 
insert #source values(4,'jan 1, 2011') 
insert #source values(4,'jan 2, 2011') 
insert #source values(4,'jan 3, 2011') 
insert #source values(4,'jan 4, 2011') 
go 

insert #source values(200236 , '2011-01-02') 
insert #source values(200236 , '2011-01-03') 
insert #source values(200236 , '2011-01-05') 
insert #source values(200236 , '2011-01-06') 
insert #source values(200236 , '2011-01-07') 
insert #source values(200236 , '2011-01-08') 
insert #source values(200236 , '2011-01-09') 
insert #source values(200236 , '2011-01-10') 
insert #source values(200236 , '2011-01-11') 
insert #source values(200236 , '2011-01-12') 
insert #source values(200236 , '2011-01-13') 
insert #source values(200236 , '2011-01-15') 
insert #source values(200236 , '2011-01-16') 
insert #source values(200236 , '2011-01-17') 
go 

drop table #candidate_range 
go 
create table #candidate_range 
(
    rowId int  not null identity(1,1) , 
    id  int  not null , 
    dtFrom datetime not null , 
    dtThru datetime not null , 
    length as 1+datediff(day,dtFrom,dtThru) , 

    primary key nonclustered (rowID) , 
    unique clustered (id,dtFrom,dtThru) , 

) 
go 

-- 
-- seed the candidate range table with the set of all possible contiguous ranges for each id 
-- 
insert #candidate_range (id , dtFrom , dtThru) 
select id  = tFrom.id , 
     valFrom = tFrom.dtCol , 
     valThru = tThru.dtCol 
from #source tFrom 
join #source tThru on tThru.id  = tFrom.id 
        and tThru.dtCol >= tFrom.dtCol 
where 1+datediff(day,tFrom.dtCol,tThru.dtCol) = (select count(*) 
                from #source t 
                where t.id = tFrom.id 
                and t.dtCol between tFrom.dtCol and tThru.dtCol 
               ) 
order by 1,2,3 
go 

-- 
-- compare the table to itself. If we find overlapping periods, 
-- we'll keep the longest such period and delete the shorter overlapping periods. 
-- 
delete t2 
from #candidate_range t1 
join #candidate_range t2 on t2.id  = t1.id 
         and t2.rowId != t1.rowID 
         and t2.length < t1.length 
         and t2.dtFrom <= t1.dtThru 
         and t2.dtThru >= t1.dtFrom 
go 

這就是關於它的一切。

1

我剛剛在SQL Server 2008中做了類似的事情。我想下面的翻譯將SQL Server 2000的工作:

-- Create table variable 
DECLARE @StartTable TABLE 
(
    rowid INT IDENTITY(1,1) NOT NULL, 
    userid int, 
    startDate date 
) 

Insert Into @StartTable(userid, startDate) 
--This finds the start dates by finding unmatched values 
SELECT t1.ID, t1.[Date] 
FROM Example As t1 
LEFT OUTER JOIN Example As t2 ON t1.ID=t2.ID 
    And DateAdd(day, 1, t2.[Date]) = t1.[Date] 
WHERE t2.[Date] Is NULL 
ORDER BY t1.ID, t1.[Date] 

-- Create table variable 
DECLARE @EndTable TABLE 
(
    rowid INT IDENTITY(1,1) NOT NULL, 
    userid int, 
    endDate date 
) 

Insert Into @EndTable(userid, endDate) 
--This finds the end dates by getting unmatched values 
SELECT t1.ID, t1.[Date] 
FROM Example As t1 
LEFT OUTER JOIN Example As t2 ON t1.ID=t2.ID 
    And DateAdd(day, -1, t2.[Date]) = t1.[Date] 
WHERE t2.[Date] IS NULL 
ORDER BY t1.ID, t1.[Date] 

Select eT.userid, startDate, endDate 
From @EndTable eT 
INNER JOIN @StartTable sT On eT.userid = sT.userid 
AND eT.rowid = sT.rowid; 

因此,大家可以看到,我創建了兩個表變量,一個用於啓動和一個目的,通過自連接表上的日期要麼只是在[日期]欄的日期之前或之後。這意味着我只選擇沒有日期的記錄(所以這些記錄會在一段時間的開始),並且沒有記錄日期的記錄(所以這些記錄將在期)爲結束表。

當這些插入到表變量中時,由於標識列的原因它們按順序編號。然後我一起加入兩個表變量。因爲它們是有序的,所以開始和結束日期應該總是正確匹配。

此解決方案適用於我,因爲每天每個ID最多有一條記錄,而我只對天而不是幾個小時等感興趣。儘管它有幾個步驟,但我喜歡它,因爲它在概念上很簡單,並且可以消除沒有遊標或循環的匹配記錄。我希望它也能爲你工作。

+0

不錯,我喜歡臨時表的高效使用。順便說一句,我不確定這個解決方案與SQL Server 2008中類似的東西有什麼共同之處,但你可能想看看[這個答案](http://stackoverflow.com/questions/) 5662545/how-do-i-group-on-continuous-ranges/5662607#5662607)(以防萬一)。 – 2011-05-10 00:15:46

+0

謝謝!我的解決方案與此類似。實際上,使用CTE和分區來完成此任務要簡單得多,但不幸的是,這些東西在SQL Server 2000中不可用。 – 2011-05-10 00:20:54