2015-01-31 11 views
8

Per esempio v'è una certa tabella con le date:Come ottenere il conto di date consecutive

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

devo scrivere query di MS SQL, che restituirà conteggio di date consecutive a partire da ogni data nella tabella. Così il risultato sarà come:

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

Mi sembra che dovrei utilizzare le funzioni di LAG e piombo, ma ora ho anche non riesco a immaginare il modo di pensare.

+0

La sequenza non deve essere "3,2,1,2,1,1" anziché "1,2,3,1,2,1"? Hai menzionato la partenza, non la fine. –

+0

Mi spiace, ho terminato con ogni data. – Mutex

risposta

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

Ben fatto, è abbastanza intelligente e molto più veloce delle altre soluzioni –

+0

Per ottenere prestazioni migliori è meglio aggiungere un indice nella colonna MyDate (a causa dell'utilizzo di WINDOW FUNCTION) –

+0

Nice, grazie) – Mutex

0

Sto assumendo questa tabella:

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); 

Ecco una soluzione ricorsiva con le spiegazioni:

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

È possibile utilizzare questo 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 
) 

per produrre il seguente risultato:

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 

Tutto quello che dovete fare ora è di calcolare un totale parziale di flagfino a il prima occorrenza di un precedente valore zero:

;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 calcola il valore rn dello prima bandiera a valore zero che viene prima del giorno t fila. CROSS APPLY calcola il numero di record precedenti il ​​record corrente fino a la prima occorrenza di un precedente flag con valore zero.

Problemi correlati