web-dev-qa-db-ja.com

値が最後に変更された時間を見つけようとする

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 かわった。)

26
SqlSandwiches

これらの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の例

13
Aaron Bertrand

基本的に、これは @ 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

13
Andriy M

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

参照 SQL Fiddleデモあり

7
Taryn

値が再現されないという仮定に基づくもう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

5
ypercubeᵀᴹ

サンプルの構造とデータを提供するための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
2
Kenneth Fisher

ラグ値とリード値の差を取得しないのはなぜですか?差がゼロの場合、変化しませんでした。ゼロ以外の場合、変化しました。これは簡単なクエリで実行できます。

-- 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
1
JJ_Coder4Hire

今日も同様の問題がありました。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]

結果: enter image description here

0
user2846273