ID、値、日付を持つテーブルがあります。このテーブルには多くのID、値、日付があります。
レコードは定期的にこのテーブルに挿入されます。 IDは常に同じままですが、値が変わる場合があります。
IDに加えて値が変更された最新の時刻を示すクエリを作成するにはどうすればよいですか?注:値は常に増加します。
このサンプルデータから:
Create Table Taco
( Taco_ID int,
Taco_value int,
Taco_date datetime)
Insert INTO Taco
Values (1, 1, '2012-07-01 00:00:01'),
(1, 1, '2012-07-01 00:00:02'),
(1, 1, '2012-07-01 00:00:03'),
(1, 1, '2012-07-01 00:00:04'),
(1, 2, '2012-07-01 00:00:05'),
(1, 2, '2012-07-01 00:00:06'),
(1, 2, '2012-07-01 00:00:07'),
(1, 2, '2012-07-01 00:00:08')
結果は次のようになります。
Taco_ID Taco_date
1 2012-07-01 00:00:05
(00:05が最後だったのでTaco_Value
かわった。)
これらの2つのクエリは、Taco_value
が常に増加するという前提に基づいています。
;WITH x AS
(
SELECT Taco_ID, Taco_date,
dr = ROW_NUMBER() OVER (PARTITION BY Taco_ID, Taco_Value ORDER BY Taco_date),
qr = ROW_NUMBER() OVER (PARTITION BY Taco_ID ORDER BY Taco_date)
FROM dbo.Taco
), y AS
(
SELECT Taco_ID, Taco_date,
rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID, dr ORDER BY qr DESC)
FROM x WHERE dr = 1
)
SELECT Taco_ID, Taco_date
FROM y
WHERE rn = 1;
ウィンドウ関数の狂気の少ない代替案:
;WITH x AS
(
SELECT Taco_ID, Taco_value, Taco_date = MIN(Taco_date)
FROM dbo.Taco
GROUP BY Taco_ID, Taco_value
), y AS
(
SELECT Taco_ID, Taco_date,
rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID ORDER BY Taco_date DESC)
FROM x
)
SELECT Taco_ID, Taco_date FROM y WHERE rn = 1;
SQLfiddle の例
追跡している人たちにとって、Taco_value
が繰り返されるとどうなるかについての争いがありました。特定のTaco_ID
で1から2になり、その後1に戻る可能性がある場合、クエリは機能しません。そのケースの解決策を次に示します。ItzikBen-Ganのような誰かが思いつくことができるギャップ&アイランドテクニックではなくても、OPのシナリオに関連していなくても、それは将来の読者に関連しています。これはもう少し複雑で、変数を1つだけ追加しました-Taco_ID
は、これまで1つしかありませんTaco_value
。
セット全体で値がまったく変化しなかったIDの最初の行を含める場合:
;WITH x AS
(
SELECT *, rn = ROW_NUMBER() OVER
(PARTITION BY Taco_ID ORDER BY Taco_date DESC)
FROM dbo.Taco
), rest AS (SELECT * FROM x WHERE rn > 1)
SELECT
main.Taco_ID,
Taco_date = MIN(CASE
WHEN main.Taco_value = rest.Taco_value
THEN rest.Taco_date ELSE main.Taco_date
END)
FROM x AS main LEFT OUTER JOIN rest
ON main.Taco_ID = rest.Taco_ID AND rest.rn > 1
WHERE main.rn = 1
AND NOT EXISTS
(
SELECT 1 FROM rest AS rest2
WHERE Taco_ID = rest.Taco_ID
AND rn < rest.rn
AND Taco_value <> rest.Taco_value
)
GROUP BY main.Taco_ID;
これらの行を除外する場合は、少し複雑ですが、小さな変更です。
;WITH x AS
(
SELECT *, rn = ROW_NUMBER() OVER
(PARTITION BY Taco_ID ORDER BY Taco_date DESC)
FROM dbo.Taco
), rest AS (SELECT * FROM x WHERE rn > 1)
SELECT
main.Taco_ID,
Taco_date = MIN(
CASE
WHEN main.Taco_value = rest.Taco_value
THEN rest.Taco_date ELSE main.Taco_date
END)
FROM x AS main INNER JOIN rest -- ***** change this to INNER JOIN *****
ON main.Taco_ID = rest.Taco_ID AND rest.rn > 1
WHERE main.rn = 1
AND NOT EXISTS
(
SELECT 1 FROM rest AS rest2
WHERE Taco_ID = rest.Taco_ID
AND rn < rest.rn
AND Taco_value <> rest.Taco_value
)
AND EXISTS -- ***** add this EXISTS clause *****
(
SELECT 1 FROM rest AS rest2
WHERE Taco_ID = rest.Taco_ID
AND Taco_value <> rest.Taco_value
)
GROUP BY main.Taco_ID;
更新 SQLfiddleの例
基本的に、これは @ Tarynの提案 派生テーブルのない単一のSELECTに「圧縮」されます。
SELECT DISTINCT
Taco_ID,
Taco_date = MAX(MIN(Taco_date)) OVER (PARTITION BY Taco_ID)
FROM Taco
GROUP BY
Taco_ID,
Taco_value
;
注:このソリューションでは、Taco_value
は増やすことしかできません。 (より正確には、Taco_value
を以前の値に戻すことはできません–実際、リンクされた回答と同じです。)
SQL Fiddleクエリのデモ: http://sqlfiddle.com/#!3/91368/2
min()
とmax()
の両方の集計関数を使用して結果を取得できるはずです。
select t1.Taco_ID, MAX(t1.taco_date) Taco_Date
from taco t1
inner join
(
select MIN(taco_date) taco_date,
Taco_ID, Taco_value
from Taco
group by Taco_ID, Taco_value
) t2
on t1.Taco_ID = t2.Taco_ID
and t1.Taco_date = t2.taco_date
group by t1.Taco_Id
値が再現されないという仮定に基づくもう1つの答え(これは基本的に@Aaronのクエリ2であり、1つ少ないネストに圧縮されています):
;WITH x AS
(
SELECT
Taco_ID, Taco_value,
Rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID
ORDER BY MIN(Taco_date) DESC),
Taco_date = MIN(Taco_date)
FROM dbo.Taco
GROUP BY Taco_ID, Taco_value
)
SELECT Taco_ID, Taco_value, Taco_date
FROM x
WHERE Rn = 1 ;
テスト: SQL-Fiddle
そして、値が再現される、より一般的な問題への回答:
;WITH x AS
(
SELECT
Taco_ID, Taco_value,
Rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID
ORDER BY MAX(Taco_date) DESC),
Taco_date = MAX(Taco_date)
FROM dbo.Taco
GROUP BY Taco_ID, Taco_value
)
SELECT t.Taco_ID, Taco_date = MIN(t.Taco_date)
FROM x
JOIN dbo.Taco t
ON t.Taco_ID = x.Taco_ID
AND t.Taco_date > x.Taco_date
WHERE x.Rn = 2
GROUP BY t.Taco_ID ;
(またはCROSS APPLY
したがって、value
を含むすべての関連行が表示されます):
;WITH x AS
(
SELECT
Taco_ID, Taco_value,
Rn = ROW_NUMBER() OVER (PARTITION BY Taco_ID
ORDER BY MAX(Taco_date) DESC),
Taco_date = MAX(Taco_date)
FROM dbo.Taco
GROUP BY Taco_ID, Taco_value
)
SELECT t.*
FROM x
CROSS APPLY
( SELECT TOP (1) *
FROM dbo.Taco t
WHERE t.Taco_ID = x.Taco_ID
AND t.Taco_date > x.Taco_date
ORDER BY t.Taco_date
) t
WHERE x.Rn = 2 ;
テスト: SQL-Fiddle-2
サンプルの構造とデータを提供するためのFYI +1。私が尋ねることができた唯一のことは、そのデータの予想される出力です。
編集:これは私を混乱させるつもりでした。私はこれを行うための「簡単な」方法があったことを新しいだけです。私は不正確な解決策を取り除き、正しいと私が信じているものを置いた。これは@bluefeetsに似たソリューションですが、@ AaronBertrandが行ったテストをカバーしています。
;WITH TacoMin AS (SELECT Taco_ID, Taco_value, MIN(Taco_date) InitialValueDate
FROM Taco
GROUP BY Taco_ID, Taco_value)
SELECT Taco_ID, MAX(InitialValueDate)
FROM TacoMin
GROUP BY Taco_ID
ラグ値とリード値の差を取得しないのはなぜですか?差がゼロの場合、変化しませんでした。ゼロ以外の場合、変化しました。これは簡単なクエリで実行できます。
-- example gives the times the value changed in the last 24 hrs
SELECT
LastUpdated, [DiffValue]
FROM (
SELECT
LastUpdated,
a.AboveBurdenProbe1TempC - coalesce(lag(a.AboveBurdenProbe1TempC) over (order by ProcessHistoryId), 0) as [DiffValue]
FROM BFProcessHistory a
WHERE LastUpdated > getdate() - 1
) b
WHERE [DiffValue] <> 0
ORDER BY LastUpdated ASC
今日も同様の問題がありました。PowerBIでは、Tabler.FillDownを使用してそれを解決できました。少し検索すると、FillDownのSQLバリアントが見つかりました。
https://www.oraylis.de/blog/fill-down-table-in-t-sql-last-non-empty-value
そのため、このソリューションをこの例に適合させるために時間をかけました。Taco_valueの再利用を示すために追加の行を追加しました。
Insert INTO Taco
Values (1, 1, '2012-07-01 00:00:09')
注:このソリューションでは、Taco_valueが増加および減少(または以前の値に戻る)する可能性があることを考慮しています
WITH help1
AS (SELECT
*
,[ChangeIndicator] = CASE
WHEN [Taco_value] = LAG([Taco_value],1) OVER(
ORDER BY
[Taco_ID]) THEN 0
ELSE 1
END
,[Taco_lag] = LAG([Taco_value],1) OVER(
ORDER BY
[Taco_date])
FROM [Taco]),
help2
AS (SELECT
*
,[RowGroup] = SUM([ChangeIndicator]) OVER(
ORDER BY
[Taco_date])
FROM [help1])
SELECT
*
,[ChangeDate] = FIRST_VALUE([Taco_date]) OVER(PARTITION BY [RowGroup]
ORDER BY
[Taco_date])
,[Taco_FillDown] = FIRST_VALUE([Taco_lag]) OVER(PARTITION BY [RowGroup]
ORDER BY
[Taco_date])
FROM [help2]
ORDER BY
[Taco_date]