2011-05-29 42 views
0

我下面的表已經確定了組範圍:找出其中由標誌

declare @table table (dates int , is_missing tinyint, group_id numeric(18)) 
insert into @table(dates,is_missing,group_id) 
select 20110719,0,1 
union all 
select 20110720,0,1 
union all 
select 20110721,0,1 
union all 
select 20110722,1,1 
union all 
select 20110723,0,1 
union all 
select 20110724,0,1 
union all 
select 20110725,0,1 
union all 
select 20110726,1,1 
union all 
select 20110727,0,1 
union all 
select 20110728,1,1 
union all 
select 20110723,1,3 
union all 
select 20110724,0,3 
union all 
select 20110725,0,3 
union all 
select 20110726,1,3 
union all 
select 20110727,0,3 


select * from @table 
order by group_id, dates 

我所試圖做的是返回日期的範圍,這是由缺少的一天標誌標識每個組。爲了使這更清楚的查詢的結果將有看起來像這樣:

group_id start_date end_date  days_count 
1   20110719  20110721  3 
1   20110723  20110725  3 
1   20110727  20110727  1 
3   20110724  20110725  2 
3   20110727  20110727  1 

的is_missing標誌basicaly每組範圍分離。它實際上表示缺少日期,因此位於is_missing標誌之間的所有其他日期都是我試圖找到其開始日期和結束日期以及日期數量的組。

有沒有簡單的方法來做到這一點?

非常感謝。

回答

1

以下是使用Common Table Expression (CTE)ROW_NUMBER()的可能解決方案。這種類型的問題被稱爲islands。使用在這個堆棧溢出問題中使用的概念:sql group by only rows which are in sequence,下面的查詢是根據您提供的數據生成期望的輸出。

如果存儲在表中的數據按group_iddates列排序,則此查詢正常工作。我假設你的數據就是這種情況。否則,您可能需要調整解決方案。

根據Andriy M提供的建議修改了查詢。 感謝Andriy M.

查詢已更改,因此即使表格中的日期值未按順序提供正確的輸出。該問題的日期值存儲在int數據類型中,而不是日期格式。因此,下面提供了兩個查詢。如果表中包含存儲在int數據typeand第二查詢日期值如果表中包含存儲在datetimedate數據類型的日期值將工作首先查詢將正常工作。

此查詢僅在SQL Server versions 2005 and above有效。由於您在sql-server-2008下標記了您的問題,我認爲這應該適合您。

屏幕截圖#顯示存儲在表中的數據。屏幕截圖#根據表格數據顯示下面提到的查詢的輸出。

希望有所幫助。

查詢存儲在int的日期值數據類型:

WITH cte AS 
(  
    SELECT datenumeric 
     , is_missing 
     , group_id 
     , datenumeric 
       - DENSE_RANK() OVER (PARTITION BY is_missing ORDER BY group_id, datenumeric) AS partition_grp 
    FROM dbo.table_data 
) 
SELECT  cte.group_id 
     , MIN(cte.datenumeric)  AS start_date 
     , MAX(cte.datenumeric)  AS end_date 
     , COUNT(cte.datenumeric) AS days_count 
FROM  cte 
WHERE  cte.is_missing = 0 
GROUP BY cte.group_id 
     , cte.partition_grp 
ORDER BY cte.group_id 
     , cte.partition_grp; 

查詢日期值存儲在datetimedate數據類型:

WITH cte AS 
(  
    SELECT datevalue 
     , is_missing 
     , group_id 
     , DATEDIFF(DAY, 0, datevalue) 
       - DENSE_RANK() OVER (PARTITION BY is_missing ORDER BY group_id, datevalue) AS partition_grp 
    FROM dbo.table_data 
) 
SELECT  cte.group_id 
     , MIN(cte.datevalue)  AS start_date 
     , MAX(cte.datevalue)  AS end_date 
     , COUNT(cte.datevalue) AS days_count 
FROM  cte 
WHERE  cte.is_missing = 0 
GROUP BY cte.group_id 
     , cte.partition_grp 
ORDER BY cte.group_id 
     , cte.partition_grp; 

截圖#1:

1

截圖#2:

2

+0

基本上,您的解決方案確實遵循鏈接答案中的模式。但是,如果某天添加了一些歷史數據,並且較早的日期將使用較新的ID,那麼通過'date'而不是'id'對行進行排名會更安全。如果你選擇按日期排序,我還建議你用一些東西來替換全局的ROW_NUMBER(即第一個),用整數表示日期(比如'DATEDIFF(day,0,date)') ,因爲您使用的排名函數越多,您的查詢就越貴。 – 2011-05-30 08:12:28

+0

從另一個角度來說,使用'datenumeric'來計算組ID與你一樣有一個非常好的誘惑。爲什麼呢,它已經是數字了,並且在將其應用到公式之前似乎不需要任何特殊處理。但是你的方法實際上會在月份轉換時打破。例如,「20110530 - 1」和「20110531 - 2」產生相同的結果,相應的日期將被合併在一起。但下一個日期到達一個新組(這是不正確的),因爲'20110601 - 3'的結果是不同的。 ... – 2011-05-30 12:58:13

+0

因此,'datenumeric'應該被轉換兩次(到'varchar',然後到'datetime'),並與DATEDIFF一起使用,或者......排名,就像你最初一樣。 (我只是不確定哪一個會更好,但需要進行一些測試。) – 2011-05-30 12:58:52

0

隨着許多感謝溼婆的很好的解決方案,我想,如果有一次失蹤日期e數據,查詢將失敗。

所以我修改了一下這個查詢,並用ROW_NUMBER()來修復這個問題。

WITH cte AS 
(  
    SELECT dates 
     , is_missing 
     , group_id 
     ,ROW_NUMBER() OVER (ORDER BY group_id, dates) - 
      DENSE_RANK() OVER (PARTITION BY is_missing ORDER BY group_id, dates) AS partition_Id 
    FROM dbo.table_data 
) 
SELECT  group_id 
     , MIN(dates) AS start_date 
     , MAX(dates) AS end_date 
     , COUNT(*) AS days_count 
FROM  cte 
WHERE  is_missing = 0 
GROUP BY group_id 
     , partition_id 
ORDER BY group_id 
     , partition_id; 

或者可能缺少日期不會發生。 :)