2015-01-31 15 views
8

Ví dụ có một số bảng với ngày:Làm thế nào để có được đếm số ngày liên tiếp

2015-01-01 
2015-01-02 
2015-01-03 
2015-01-06 
2015-01-07 
2015-01-11 

tôi phải viết query ms sql, mà sẽ trở lại đếm số ngày liên tiếp bắt đầu từ mỗi ngày trong bảng. Vì vậy, kết quả sẽ như sau:

2015-01-01 1 
2015-01-02 2 
2015-01-03 3 
2015-01-06 1 
2015-01-07 2 
2015-01-11 1 

Dường như với tôi rằng tôi nên sử dụng các hàm LAG và LEAD, nhưng bây giờ tôi thậm chí không thể tưởng tượng được cách suy nghĩ.

+0

Chuỗi không nên là '3,2,1,2,1,1' thay vì' 1,2,3,1,2,1'? Bạn đã đề cập đến sự bắt đầu, không phải là kết thúc. –

+0

Xin lỗi, tôi kết thúc với mỗi ngày. – Mutex

Trả lời

10
CREATE TABLE #T (MyDate DATE) ; 
INSERT #T VALUES ('2015-01-01'),('2015-01-02'),('2015-01-03'),('2015-01-06'),('2015-01-07'),('2015-01-11') 

SELECT 
    RW=ROW_NUMBER() OVER(PARTITION BY GRP ORDER BY MyDate) ,MyDate 
FROM 
(
SELECT 
    MyDate, DATEDIFF(Day, '1900-01-01' , MyDate)- ROW_NUMBER() OVER(ORDER BY MyDate) AS GRP 
FROM #T 
) A 

DROP TABLE #T; 
+0

Thực hiện tốt, điều đó khá thông minh và nhanh hơn nhiều so với các giải pháp khác –

+0

Để đạt được hiệu suất tốt hơn, bạn nên thêm chỉ mục vào cột MyDate (vì sử dụng CHỨC NĂNG WINDOW) –

+0

Rất tốt, cảm ơn bạn) – Mutex

0

Tôi giả định bảng này:

SELECT * 
INTO #Dates 
FROM (VALUES 
    (CAST('2015-01-01' AS DATE)), 
    (CAST('2015-01-02' AS DATE)), 
    (CAST('2015-01-03' AS DATE)), 
    (CAST('2015-01-06' AS DATE)), 
    (CAST('2015-01-07' AS DATE)), 
    (CAST('2015-01-11' AS DATE))) dates(d); 

Dưới đây là một giải pháp đệ quy với lời giải thích:

WITH 
    dates AS (
    SELECT 
     d, 
     -- This checks if the current row is the start of a new group by using LAG() 
     -- to see if the previous date is adjacent 
     CASE datediff(day, d, LAG(d, 1) OVER(ORDER BY d)) 
     WHEN -1 THEN 0 
     ELSE 1 END new_group, 
     -- This will be used for recursion 
     row_number() OVER(ORDER BY d) rn 
    FROM #Dates 
), 
    -- Here, the recursion happens 
    groups AS (
    -- We initiate recursion with rows that start new groups, and calculate "GRP" 
    -- numbers 
    SELECT d, new_group, rn, row_number() OVER(ORDER BY d) grp 
    FROM dates 
    WHERE new_group = 1 
    UNION ALL 
    -- We then recurse by the previously calculated "RN" until we hit the next group 
    SELECT dates.d, dates.new_group, dates.rn, groups.grp 
    FROM dates JOIN groups ON dates.rn = groups.rn + 1 
    WHERE dates.new_group != 1 
) 
-- Finally, we enumerate rows within each group 
SELECT d, row_number() OVER (PARTITION BY grp ORDER BY d) 
FROM groups 
ORDER BY d 

SQLFiddle

1

Bạn có thể sử dụng này CTE:

;WITH CTE AS (
    SELECT [Date],   
      ROW_NUMBER() OVER(ORDER BY [Date]) AS rn, 
      CASE WHEN DATEDIFF(Day, PrevDate, [Date]) IS NULL THEN 0 
       WHEN DATEDIFF(Day, PrevDate, [Date]) > 1 THEN 0 
       ELSE 1 
      END AS flag 
    FROM (
     SELECT [Date], LAG([Date]) OVER (ORDER BY [Date]) AS PrevDate 
     FROM #Dates) d 
) 

để sản xuất các kết quả sau:

Date  rn flag 
=================== 
2015-01-01 1 0 
2015-01-02 2 1 
2015-01-03 3 1 
2015-01-06 4 0 
2015-01-07 5 1 
2015-01-11 6 0 

Tất cả bạn phải làm bây giờ là để tính toán một số hoạt động của flaglên đến các xuất hiện đầu tiên của một trước zero giá trị:

;WITH CTE AS (
    ... cte statements here ... 
) 
SELECT [Date], b.cnt + 1 
FROM CTE AS c 
OUTER APPLY (
    SELECT TOP 1 COALESCE(rn, 1) AS rn 
    FROM CTE 
    WHERE flag = 0 AND rn < c.rn 
    ORDER BY rn DESC 
) a 
CROSS APPLY (
    SELECT COUNT(*) AS cnt 
    FROM CTE 
    WHERE c.flag <> 0 AND rn < c.rn AND rn >= a.rn 
) b 

OUTER APPLY tính giá trị rn của trước tiên cờ không có giá trị xuất hiện trước giá trị t hàng. CROSS APPLY tính số lượng bản ghi trước bản ghi hiện tại tối đa lần xuất hiện đầu tiên của cờ có giá trị bằng không trước đó.

Các vấn đề liên quan