web-dev-qa-db-fra.com

Sélectionner les lignes où la valeur de la colonne a changé

Disons que j'ai le tableau suivant:

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     

Comment sélectionner les lignes suivies, c’est-à-dire où Value a changé? En gros, j'essaie de trouver le moment où Value a changé pour pouvoir effectuer d'autres requêtes en fonction de ces intervalles. La solution ne devrait pas dépendre de connaître Value ou Time à l'avance.

Il me semble que cela ne devrait pas être très difficile (mais c'est assez difficile pour moi apparemment!).

J'utilise actuellement SQL Server 2008 bien que j'aie accès à 2012 si les nouvelles fonctions de fenêtre/d'analyse sont utiles.

J'ai essayé d'adapter les solutions ici http://blog.sqlauthority.com/2011/11/24/sql-server-solution-to-puzzle-simulate-lead-and-lag-without-using-sql-server- 2012-analytic-function/ mais ma requête n'a pas abouti au bout d'une heure! Je pense que les jointures font exploser la taille de la ligne en quelque chose d'ingérable (ou je l'ai foiré).

Je peux résoudre ce problème avec du code C # et des appels multiples à la base de données, mais cela semble être une tâche qui pourrait être réalisée dans une fonction table-value ou SP, ce qui serait beaucoup plus agréable.

De plus, une solution qui ne fonctionne qu'avec une augmentation de Value est acceptable si cela est plus facile.

26
agentnega

Je pense que c'est ce que vous recherchez:

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

Cela peut être lent si le jeu de résultats est volumineux et s'il n'y a pas un bon index de support ...

MODIFIER

Ah, attendez une seconde, les valeurs montent et descendent, pas seulement vers le haut ... si c'est le cas, vous pouvez essayer cette approche beaucoup plus lente:

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;

Résultats:

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
30
Aaron Bertrand
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

Résultats:

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
12
valo

Nous pouvons le faire en utilisant également des sous-requêtes

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;

Donc, j'ai comparé les valeurs de 2 lignes si cela change, la différence de rn ne sera pas égale à 1, sinon la valeur de rn augmentera de 1; sub2.rn IS NULL est utilisé pour la première ligne car la jointure aura lieu à partir de id = 2.

0
chetan kumar