试图找出值最后一次改变的时间

Sql*_*hes 26 sql-server t-sql sql-server-2008-r2

我有一个表,其中有一个 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')
Run Code Online (Sandbox Code Playgroud)

结果应该是:

Taco_ID      Taco_date
1            2012-07-01 00:00:05
Run Code Online (Sandbox Code Playgroud)

(因为 00:05 是最后一次Taco_Value更改。)

Aar*_*and 13

这两个查询依赖于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;
Run Code Online (Sandbox Code Playgroud)

一个减少窗口函数疯狂的替代方案:

;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;
Run Code Online (Sandbox Code Playgroud)

SQLfiddle 中的示例


更新

对于那些跟踪记录的人来说,如果Taco_value可以重复发生会发生什么,存在争论。如果对于任何给定的Taco_ID,它可以从 1 变为 2,然后返回到 1 ,则查询将不起作用。这是这种情况的解决方案,即使它不是像 Itzik Ben-Gan 这样的人可能能够梦想的间隙和岛屿技术,即使它与 OP 的场景无关 - 它也可能是与未来的读者相关。它有点复杂,我还添加了一个额外的变量 - 一个Taco_ID只有一个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;
Run Code Online (Sandbox Code Playgroud)

如果你想排除这些行,它有点复杂,但仍然是微小的变化:

;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;
Run Code Online (Sandbox Code Playgroud)

更新的SQLfiddle 示例


And*_*y M 13

基本上,这是@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
;
Run Code Online (Sandbox Code Playgroud)

注:本方案考虑了Taco_value只能增加的规定。(更准确地说,它假设Taco_value不能变回以前的值——实际上与链接的答案相同。)

查询的 SQL Fiddle 演示:http ://sqlfiddle.com/#!3/91368/2

  • 哇,嵌套的 MAX/MIN。*头脑风暴* +1 (7认同)

Tar*_*ryn 7

您应该能够同时使用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
Run Code Online (Sandbox Code Playgroud)

参见SQL Fiddle with Demo


ype*_*eᵀᴹ 5

另一个基于值不会再次出现的假设的答案(这基本上是@Aaron 的查询 2,浓缩在一个较少的嵌套中):

;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 ;
Run Code Online (Sandbox Code Playgroud)

测试: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 ;
Run Code Online (Sandbox Code Playgroud)

(或使用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 ;
Run Code Online (Sandbox Code Playgroud)

测试:SQL-Fiddle-2