2011-03-27 12 views
8

Ho una tabella di registrazioni, circa 300K record. Ho bisogno di una dichiarazione SQL che mostrerà il numero totale di registrazioni per quel particolare giorno?Valori cumulativi delle colonne dalle date correnti e precedenti

select 
count('x'),CONVERT(varchar(12),date_created,111) 
from reg group by 
cONVERT(varchar(12),date_created,111) 
order by 
CONVERT(varchar(12),date_created,111) 

risultato di questa query:

169  2011/03/24 
3016  2011/03/25 
2999  2011/03/26 

Risultato desiderato:

2011/03/25 3016+169 
2011/03/26 2999+3016+169 

Come si può fare?

+0

Oh, vedo [SQL-Server] è riservato per MS: o molto fuorviante. – vbence

+0

@vbence: sembra che tu non abbia sentito parlare di SQL Server. http://en.wikipedia.org/wiki/Microsoft_SQL_Server –

+0

@ p.campbell Wow .. tanta ostilità in un commento così breve. – vbence

risposta

2

Ecco due versioni per fare questo. Ho provato con 100000 righe distribuite su 6000 giorni su un computer veramente lento con memoria insufficiente, e questo dimostra che la versione cte è più veloce della versione loop. Le altre versioni suggerite qui (finora) sono molto più lente, a condizione che abbia compreso correttamente il problema.

CTE ricorsiva (10 secondi)

-- Table variable to hold count for each day 
declare @DateCount table(d int, c int, rn int) 
insert into @DateCount 
    select 
    datediff(d, 0, date_created) as d, 
    count(*) as c, 
    row_number() over(order by datediff(d, 0, date_created)) as rn 
    from reg 
    group by datediff(d, 0, date_created) 

-- Recursive cte using @DateCount to calculate the running sum 
;with DateSum as 
(
    select 
    d, c, rn 
    from @DateCount 
    where rn = 1 
    union all 
    select 
    dc.d, ds.c+dc.c as c, dc.rn 
    from DateSum as ds 
    inner join @DateCount as dc 
     on ds.rn+1 = dc.rn 
) 
select 
    dateadd(d, d, 0) as date_created, 
    c as total_num 
from DateSum 
option (maxrecursion 0) 

Loop (14 secondi)

-- Table variable to hold count for each day 
declare @DateCount table(d int, c int, rn int, cr int) 
insert into @DateCount 
    select 
    datediff(d, 0, date_created) as d, 
    count(*) as c, 
    row_number() over(order by datediff(d, 0, date_created)) as rn, 
    0 
    from reg 
    group by datediff(d, 0, date_created) 

declare @rn int = 1 

-- Update cr with running sum 
update dc set 
    cr = dc.c 
from @DateCount as dc 
where rn = @rn 

while @@rowcount = 1 
begin 
    set @rn = @rn + 1 

    update dc set 
    cr = dc.c + (select cr from @DateCount where rn = @rn - 1) 
    from @DateCount as dc 
    where rn = @rn 
end 

-- Get the result 
select 
    dateadd(d, d, 0) as date_created, 
    cr as total_num 
from @DateCount 

Modifica 1 La versione veramente veloce

The quirky update

-- Table variable to hold count for each day 
declare @DateCount table(d int primary key, c int, cr int) 
insert into @DateCount 
    select 
    datediff(d, 0, date_created) as d, 
    count(*) as c, 
    0 
    from reg 
    group by datediff(d, 0, date_created) 

declare @rt int = 0 
declare @anchor int 

update @DateCount set 
    @rt = cr = @rt + c, 
    @anchor = d 
option (maxdop 1) 

-- Get the result 
select 
    dateadd(d, d, 0) as date_created, 
    cr as total_num 
from @DateCount     
order by d 
+0

+1 La stravagante tecnica di aggiornamento è la più veloce - Semplicemente non del tutto documentata o garantita! Dovresti davvero avere un indice cluster e l'insieme 'MAXDOP 1' (come discusso qui http://www.sqlservercentral.com/articles/T-SQL/68467/) –

+0

@Martin -" Non documentato o garantito "potrebbe essere un valido motivo per scegliere la versione cte ricorsiva più lenta se le prestazioni sono ragionevoli. Forse l'aggiornamento può fallire perché l'ordine dell'aggiornamento non è garantito. Sarebbe male se iniziasse a fare aggiornamenti dal basso o se SQL Server scegliesse di eseguire l'aggiornamento in parallelo. Non ho idea se ciò possa accadere. Domani metterò alla prova questo lavoro dove ho un computer con più di un processore :). –

+1

@ Martin - Non ho letto il tuo commento modificato prima di aver postato il mio. L'indice cluster e 'maxdop 1' sembrano la soluzione alle cose di cui mi preoccupavo. –

1

Prova questo.

SELECT r1.date_created, 
    COUNT(*) AS number 
FROM (SELECT distinct(date_created) FROM reg) AS r1 
    LEFT JOIN reg AS r2 ON (r2.date_created <= r1.date_created) 
GROUP BY r1.date_created 

Naturalmente si deve indicizzare il tuo tavolo con qualcosa di simile:

CREATE INDEX datefilter ON reg (date_created); 
+0

questo non è corretto; non produce un valore cumulativo. –

+0

Ti darebbe il numero di registrazioni per un determinato giorno e per tutti i giorni precedenti combinati. Questo è tanto cumulativo quanto diventa. – vbence

+0

Ecco come appaiono i risultati della tua query: http://i.imgur.com/VLdrT.png –

2

Basta usare un SUM per ottenere un conteggio cumulativo:

SELECT reg1.date_created,  
     SUM(reg2.val) AS CumulativeValue 
FROM (
     select count(*) as RegCountForDay, 
       date_created 
     from reg 
     group by date_created 
    ) AS reg1 
LEFT JOIN reg AS reg2 ON (reg2.date_created <= reg1.date_created) 
GROUP BY reg1.date_created 
+0

Penso che questo darebbe esattamente lo stesso risultato delle due risposte precedenti. – vbence

+0

@vbence: avvia SQL Management Studio, incolla questa risposta ed eseguila. È SUM'ing su 'r2', non' r1'. –

+0

In quel momento ho pensato che anche la mia avesse dato i risultati corretti. :) – vbence

2

Attualmente si hanno 2 opzioni: prima sta usando join come proposto da vbence, il secondo è sottoquery:

SELECT r1.date_created, (SELECT COUNT(*) FROM reg r2 
WHERE r2.date_created<=r1.date_created) AS total_num 
FROM reg r1; 

Questi due approcci generano piani di esecuzione simili.

In futuro, quando SQLServer implementa ORDER BY per OVER con funzioni di aggregazione, si sarà in grado di scrivere

SELECT date_created, 
COUNT(*) OVER(ORDER BY date_created) as total_num 
FROM reg; 
+0

3 opzioni: il terzo utilizza un cursore o [il CLR funge da cursore più veloce] (http://sqlblog.com/blogs/adam_machanic/archive/2006/07/12/running-sums-yet -again-sqlclr-saves-the-day.aspx) –

+0

@Martin: Certo, è possibile ottenere gli stessi risultati con i cursori, ma i cursori hanno overhead delle prestazioni, quindi non li userei in questi casi ... – a1ex07

+1

Dipende quanti le righe devono essere elaborate. Il carico di lavoro del join triangolare cresce proporzionale al quadrato del numero di righe. Il carico del carico è lineare. –

1

si può risolvere questo problema attraverso query SQL di seguito ..Hai dato due colonne col1=Number e col2=Date

Select DATE,OUTPUT=SUM(InnerValue) from 
(
    Select T1.Date, T1.Number, InnerValue=ISNULL(T2.Number,0) from 
    (
    Select ID=DENSE_RANK() OVER(ORDER BY DATE),Date,Number from YourTable 
) As T1 
    LEFT JOIN 
    (
    Select ID=DENSE_RANK() OVER(ORDER BY DATE),Date,Number from YourTable 
) AS T2 
    ON T1.ID >= T2.ID 
) As MainTable GROUP BY DATE 
Problemi correlati