2012-06-20 9 views
21

Diciamo che ho la seguente tabella:Selezionare le righe in cui il valore della colonna è cambiato

Value Time 
0  15/06/2012 8:03:43 PM 
1  15/06/2012 8:03:43 PM  * 
1  15/06/2012 8:03:48 PM 
1  15/06/2012 8:03:53 PM 
1  15/06/2012 8:03:58 PM  
2  15/06/2012 8:04:03 PM  * 
2  15/06/2012 8:04:08 PM 
3  15/06/2012 8:04:13 PM  * 
3  15/06/2012 8:04:18 PM 
3  15/06/2012 8:04:23 PM 
2  15/06/2012 8:04:28 PM  * 
2  15/06/2012 8:04:33 PM  

Come faccio a selezionare le righe stellati, cioè, dove Value è cambiato? Fondamentalmente sto cercando di trovare il tempo in cui è stato modificato Value così posso fare altre domande sulla base di quegli intervalli di tempo. La soluzione non dovrebbe dipendere dal conoscere Value o Time in anticipo.

Mi sembra che questo non dovrebbe essere molto difficile (ma è abbastanza difficile per me a quanto pare!).

Attualmente sto utilizzando SQL Server 2008 sebbene abbia accesso al 2012 se le nuove funzioni finestra/analitica sono utili.

Ho provato ad adattare le soluzioni qui http://blog.sqlauthority.com/2011/11/24/sql-server-solution-to-puzzle-simulate-lead-and-lag-without-using-sql-server-2012-analytic-function/ ma la mia richiesta non è stata completata dopo un'ora! Penso che le unioni esplodano la dimensione della riga a qualcosa di ingestibile (o l'ho rovinato).

Posso risolvere questo problema con codice C# e più chiamate db, ma sembra qualcosa che potrebbe essere fatto in una funzione con valori di tabella o SP che sarebbe molto più bello.

Inoltre, una soluzione che funziona solo con l'aumento di Value è OK se è più semplice.

risposta

27

Credo che questo è ciò che stai dopo:

;WITH x AS 
(
    SELECT value, time, rn = ROW_NUMBER() OVER 
    (PARTITION BY Value ORDER BY Time) 
    FROM dbo.table 
) 
SELECT * FROM x WHERE rn = 1; 

Questo può essere lento se il gruppo di risultati è grande e non c'è un buon indice di supporto ...

EDIT

Ah, aspetta un secondo, i valori vanno su e giù, non solo fino ... se questo è il caso si può provare questo approccio molto più lento:

DECLARE @x TABLE(value INT, [time] DATETIME) 

INSERT @x VALUES 
(0,'20120615 8:03:43 PM'),-- 
(1,'20120615 8:03:43 PM'),--* 
(1,'20120615 8:03:48 PM'),-- 
(1,'20120615 8:03:53 PM'),-- 
(1,'20120615 8:03:58 PM'),-- 
(2,'20120615 8:04:03 PM'),--* 
(2,'20120615 8:04:08 PM'),-- 
(3,'20120615 8:04:13 PM'),--* 
(3,'20120615 8:04:18 PM'),-- 
(3,'20120615 8:04:23 PM'),-- 
(2,'20120615 8:04:28 PM'),--* 
(2,'20120615 8:04:33 PM'); 

;WITH x AS 
(
    SELECT *, rn = ROW_NUMBER() OVER (ORDER BY time) 
    FROM @x 
) 
SELECT x.value, x.[time] 
FROM x LEFT OUTER JOIN x AS y 
ON x.rn = y.rn + 1 
AND x.value <> y.value 
WHERE y.value IS NOT NULL; 

Risultati:

value time 
----- ----------------------- 
1  2012-06-15 20:03:43.000 
2  2012-06-15 20:04:03.000 
3  2012-06-15 20:04:13.000 
2  2012-06-15 20:04:28.000 
+0

Wow che era veloce L'aumento del valore è OK. Lo proverò. – agentnega

+0

Fantastico, grazie @ Aaron Bertrand. Sui miei dati la seconda versione ha impiegato meno del doppio del tempo della prima versione, quindi la differenza di prestazioni non è poi così male. – agentnega

+0

Come convertire questa query per includere anche un ID (nuova colonna) nell'output risultante? – ThinkCode

12
DECLARE @x TABLE(value INT, [time] DATETIME) 

INSERT @x VALUES 
(0,'20120615 8:03:43 PM'),-- 
(1,'20120615 8:03:43 PM'),--* 
(1,'20120615 8:03:48 PM'),-- 
(1,'20120615 8:03:53 PM'),-- 
(1,'20120615 8:03:58 PM'),-- 
(2,'20120615 8:04:03 PM'),--* 
(2,'20120615 8:04:08 PM'),-- 
(3,'20120615 8:04:13 PM'),--* 
(3,'20120615 8:04:18 PM'),-- 
(3,'20120615 8:04:23 PM'),-- 
(2,'20120615 8:04:28 PM'),--* 
(2,'20120615 8:04:33 PM'); 


; with temp as 
(
SELECT 
    value, [time], lag(value,1,-1) over (order by [time]) as lastValue 
FROM @x 
) 
SELECT 
    [value],[time] 
FROM 
    temp 
WHERE value <> lastValue 

risultati:

value time 
--------------------------- 
0 2012-06-15 20:03:43.000 
1 2012-06-15 20:03:43.000 
2 2012-06-15 20:04:03.000 
3 2012-06-15 20:04:13.000 
2 2012-06-15 20:04:28.000 
+3

Ordinato, ma vale la pena menzionare questo è supportato solo da SQL 2012 in poi. –

+0

Questo è stato super veloce! La query per le versioni precedenti ha funzionato 11 minuti e dopo ho dovuto cancellarlo. –

1

Possiamo farlo utilizzando sub query anche

SELECT sub1.value, sub1.time FROM 
    (SELECT *,rn,id FROM 
    (SELECT *,row_number() over (partition by value order by time) AS rn, row_number() over (order by time) AS id FROM x) order by time) sub1 
    LEFT OUTER JOIN 
    (SELECT *,rn,id FROM 
    (SELECT *,row_number() over (partition by value order by time) AS rn, row_number() over (order by time) AS id FROM x) order by time) sub2 
    ON sub1.id = sub2.id + 1 
    WHERE sub1.rn - sub2.rn <> 1 OR sub2.rn IS NULL; 

Così, ho confrontato i valori di 2 file se cambia allora la differenza di rn non sarà uguale a 1 altrimenti rn il valore aumenterà di 1 quindi, ho selezionato tutte le righe la cui differenza con il valore rn della riga successiva non è 1 e sub2.rn IS NULL è utilizzato per la prima riga perché il join si verificherà da id = 2.

Problemi correlati