web-dev-qa-db-ja.com

列の値が変更された行を選択します

次の表があるとします。

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     

スター付きの行、つまりValueが変更された行を選択するにはどうすればよいですか?基本的に、Valueが変更された時間を見つけようとしているため、それらの時間間隔に基づいて他のクエリを実行できます。解決策は、事前にValueまたはTimeを知っていることに依存すべきではありません。

これはそれほど難しいことではないように思えます(しかし、明らかに私にとっては十分に難しいです!)。

現在、SQL Server 2008を使用していますが、新しいウィンドウ/分析関数が役立つ場合は2012にアクセスできます。

ここでソリューションを適応させてみました http://blog.sqlauthority.com/2011/11/24/sql-server-solution-to-puzzle-simulate-lead-and-lag-without-using-sql- server-2012-analytic-function / しかし、クエリは1時間後に完了しませんでした!結合により、行サイズが管理不能なサイズに爆発する(または、それを台無しにした)と思います。

この問題はC#コードと複数のdb呼び出しで解決できますが、テーブル値関数またはSPの方がはるかに優れています。

また、増加するValueでのみ機能するソリューションは、それが簡単であればOKです。

27
agentnega

私はこれがあなたが求めていることだと思う:

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

結果セットが大きく、適切なサポートインデックスがない場合、これは遅い可能性があります...

[〜#〜] edit [〜#〜]

ああ、ちょっと待ってください、値が上下するだけでなく、上下します...もしそうなら、このずっと遅いアプローチを試すことができます:

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;

結果:

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

結果:

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

サブクエリを使用してこれを行うこともできます

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;

したがって、2行の値を変更して比較すると、rnの差は1と等しくなりません。そうでない場合、rn値は1ずつ増加するので、次の行のrn値との差が1ではないすべての行を選択し、 sub2.rn IS NULLは、結合がid = 2から発生するため、最初の行に使用されます。

0
chetan kumar