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)
对于那些跟踪记录的人来说,如果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
您应该能够同时使用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)
另一个基于值不会再次出现的假设的答案(这基本上是@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
| 归档时间: |
|
| 查看次数: |
38173 次 |
| 最近记录: |