获取第n个连续组的第一行/最后一行
Posted
技术标签:
【中文标题】获取第n个连续组的第一行/最后一行【英文标题】:Get first/last row of n-th consecutive group 【发布时间】:2013-12-04 16:11:03 【问题描述】:从第 n 组中选择单个记录/值的最简单方法是什么?该组由材料及其价格决定(价格可能会发生变化)。我需要找到最后一个材料价格组的最后一个日期和最后一个日期。所以我想知道价格何时发生变化。
我尝试了以下查询来获取当前(最后)价格的第一个日期,如果之前使用过该价格,则可能会返回错误的日期:
DECLARE @material VARCHAR(20)
SET @material = '1271-4303'
SELECT TOP 1 Claim_Submitted_Date
FROM tabdata
WHERE Material = @material
AND Price = (SELECT TOP 1 Price FROM tabdata t2
WHERE Material = @material
ORDER BY Claim_Submitted_Date DESC)
ORDER BY Claim_Submitted_Date ASC
这也只返回最后一个,我如何获得上一个?那么上一个价格最后/第一个使用的日期是什么时候?
我已经简化了我的架构并使用示例数据创建了this sql-fiddle。这里按时间顺序。所以 ID=7 的行是我需要的,因为它具有最新日期的倒数第二个价格。
ID CLAIM_SUBMITTED_DATE MATERIAL PRICE
5 December, 04 2013 12:33:00+0000 1271-4303 20
4 December, 03 2013 12:33:00+0000 1271-4303 20 <-- current
3 November, 17 2013 10:13:00+0000 1271-4846 40
7 November, 08 2013 12:16:00+0000 1271-4303 18 <-- last(desired)
2 October, 17 2013 09:13:00+0000 1271-4303 18
1 September, 17 2013 08:13:00+0000 1271-4303 10
8 September, 16 2013 12:15:00+0000 1271-4303 17
6 June, 23 2013 14:22:00+0000 1271-4303 18
9 January, 11 2013 12:22:10+0000 1271-4303 20 <-- a problem since this is older than the desired but will be returned by my simply sub-query approach above
是否甚至可以参数化这个值,所以如果我想知道第三个最后价格日期,nthLatestPriceGroup
?请注意,查询位于标量值函数中。
编辑:非常感谢大家。但不幸的是,一个简单的ROW_NUMBER
似乎在这里无济于事,因为我试图获得当前价格之前的最新价格 给定材料的行。所以GROUP BY
/PARTITION BY material,price
包含不属于最近最近的材料价格组的具有相同价格的行。
考虑到价格可能会发生变化
Date Price Comment
5 months ago 20 original price, note that this is the same as the curent which causes my query to fail!
3 months ago 18 price has changed, i might need the first and last date
2 months ago 20 price has changed, i might need the first and last date
1 month ago 18 previous price, i need the oldest and newest dates
NOW 20 current price, i need the first/oldest date from this group
所以我想要最后 20 组中最近一行的日期,最旧的 20 组无关紧要。所以我必须以某种方式按连续价格分组,因为价格在已经改变后可能会重复。
所以实际上我只需要上面列表中以1 month ago ... previous price
开头的价格组中的最新Claim_Submitted_Date
,这是上一个价格有效之前的日期。 cmets 中列出的其他信息非常好(nthLatestPriceGroup
子问题)。这就是上面示例数据中带有ID=7
的行。顺便说一句,这个价格组中最旧的行将是 ID=2
(17 月 17 日)而不是 ID=6
(6 月 23 日),即使后者更旧。之后有不同的价格(10)。这就是我不能使用简单排名函数的原因。
【问题讨论】:
只是为了澄清!您在 sql-fiddle 中提供的示例数据中您期望的结果是什么? @huMptyduMpty:很抱歉造成混乱,我现在希望用正确的所需行编辑我的答案。 SQLFiddle 能解决您的问题吗?据我了解,您正在根据组内的max ID
寻找price
@Kiril:谢谢。但不幸的是,它没有返回我需要的东西。我在我的问题的第一个版本中错误地请求了错误的行。根据我的要求,正确的行是带有ID=7
和date=November, 08
的行(在我上面的示例数据中对此进行了评论)。这是正确的行,因为它是最后一个材料价格组的第一行(日期描述)。当前价格是 20,最后是 18,在 18 组中最新的是 ID=7。您的查询仅返回给定材料的(第二)最旧的。主要问题是我的团队依赖于连续价格。
我是否正确地说您正在寻找给定 MATERIAL 的倒数第三个值??
【参考方案1】:
您需要在子查询中使用窗口函数 ROWNUMBER,...
这样的事情会让你到达那里:
ROW_NUMBER() OVER(PARTITION BY Price ORDER BY Claim_Submitted_Date DESC) AS Row
这是基于您的小提琴的更新:
DECLARE @material VARCHAR(20)
SET @material = '1271-4303'
SELECT * FROM
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY Material ORDER BY Claim_Submitted_Date ASC) AS rn
FROM tabdata t2
WHERE Material = @material
) res
WHERE rn=2
如果 idData 是增量的(因此是按时间顺序排列的),您可以使用这个:
SELECT * FROM
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY Material ORDER BY idData DESC) AS rn
FROM tabdata t2
WHERE Material = @material
) res
看看您的最新要求,我们可能都想多了(如果我理解正确的话):
DECLARE @MATERIAL AS VARCHAR(9)
SET @MATERIAL = '1271-4303'
SELECT TOP 1 *
FROM tabdata t2
WHERE Material = @material
AND PRICE <> ( SELECT TOP 1 Price
FROM tabdata
WHERE Material = @material
ORDER BY CLAIM_SUBMITTED_DATE desc)
ORDER BY CLAIM_SUBMITTED_DATE desc
--results
idData Claim_Submitted_Date Material Price
7 2013-11-08 12:16:00.000 1271-4303 18
这是基于此的fiddle。
【讨论】:
我已经尝试了排名功能,但他们没有给我我需要的结果。如果您使用我的小提琴创建一个返回第一行(ID = 1)的有效查询,那就太好了。 我试试看,给我五分钟。 你去蒂姆,希望这会有所帮助。对不起 SELECT *,我很着急。现在就去吧,希望这能解决你的问题。 检查小提琴上的数据,第 1 行与上面的示例不同。 更新小提琴以匹配上述内容:sqlfiddle.com/#!3/602d0d/9【参考方案2】:试试这个
DECLARE @material VARCHAR(20), @Nth INT
SET @material = '1271-4303'
SET @Nth = 2
;with CTE1 ([idData],[Claim_Submitted_Date], [Material], [Price], Rn)
as
(
SELECT *,
DENSE_RANK() OVER(ORDER BY PRICE DESC) AS rn
FROM tabdata
WHERE Material = @material
)
,
CTE2 ([idData], [Material], [Price], LastDate)
AS(
SELECT [idData], [Material], [Price], MAX([Claim_Submitted_Date])
FROM CTE1
WHERE rn = @Nth
GROUP BY [idData], [Material], [Price]
)
SELECT Top 1 [idData], [Material], [Price], LastDate
FROM CTE2
ORDER BY LastDate DESC
结果集
idData Material Price LastDate
7 1271-4303 18 2013-11-08 12:16:00.000
【讨论】:
谢谢。但不幸的是,它没有返回我需要的东西。我在我的问题的第一个版本中错误地请求了错误的行。根据我的要求,正确的行是ID=7
和date=November, 08
的行(在我上面的示例数据中对此进行了评论)。这是正确的行,因为它是最后一个材料价格组的第一行(日期描述)。当前价格是 20,最后是 18,在 18 组中最新的是 ID=7。您的查询仅返回给定材料的(第二)最旧的。主要问题是我的组依赖于连续价格。
谢谢,明天我会看看它,因为 sql-fiddle 已关闭,我在家。如果它有效,我希望找到更简洁和有效的东西,因为原始查询更复杂,并链接 10 个不同的表来查找材料、价格和日期,其中两个表每个包含近 1 亿行。
我有 tested it 但它不返回结果行。感谢您的努力。【参考方案3】:
在您的最后一个 cmets 之后,我提出的唯一解决方案是根据 Claim_Submitted_Date
计算不同的价格组,然后将获得的组索引作为分组标准的一部分。
不确定它是否会非常高效。希望它会有所帮助。
declare @materialId nvarchar(max), @targetrank int
set @materialId = '1271-4303'
set @targetrank =2
;with grouped as (
select *,
(select count( t.price) -- don't put a DISTINCT here. (I know, I did)
from tabdata as t
where t.Price <> tj.Price
and t.Claim_Submitted_Date> tj.Claim_Submitted_Date
and t.Material= @materialId
)as group_indicator
from tabdata tj
where Material= @materialId
),
rankedClaims as
(
select grouped.*, row_number() over (PARTITION BY material,price,group_indicator ORDER BY claim_submitted_date desc) as rank
from grouped
),
numbered as
(
select *, ROW_NUMBER() OVER (order by Claim_Submitted_Date desc) as RowNumber from
rankedClaims
where rank =1
)
select Id, Claim_Submitted_Date, Material, Price from numbered
where RowNumber=@targetrank
(也不确定是否应该处理同一日期不同价格的两次索赔t.Claim_Submitted_Date> tj.Claim_Submitted_Date
)
-------- 上一个答案
也许你可以试试类似的东西:
SELECT ranked.[CLAIM_SUBMITTED_DATE]
FROM
(
SELECT trimmed.*, ROW_NUMBER() OVER (ORDER BY claim_submitted_date) AS rank FROM
(
SELECT a.*
,row_number() over (PARTITION BY material,price ORDER BY claim_submitted_date) AS daterank
FROM tabdata a
WHERE a.material= '1271-4303'
)
AS trimmed
WHERE daterank=1
) AS ranked
WHERE rank=2
参数化排名似乎是可能的,因为它只涉及WHERE rank=2
【讨论】:
这里同样适用。很抱歉最初的混乱,因为我想要的结果不正确。我已经相应地编辑了我的答案。也许我在上面订购的样本数据和 cmets 有助于理解我想要实现的目标。 @TimSchmelter 感谢跟进。我已经看到了这个问题,但认为它与这种情况无关;而且,事实上,将等级参数化不适用于任何大于 2 的等级(我认为我误译了“倒数第二个的最后日期”)。会尝试再看一遍 这似乎像this fiddle 建议的那样工作,谢谢。如果我真的需要找到其他价格组,也许我会使用这种方法。当我回到办公室时,我必须用真实数据对其进行测试。 Mack 的最后一种简单方法已经完成了我真正需要的。但是,如果它真的有效,我会接受它,因为它是豪华版。 @TimSchmelter thx,但是在第二个(第三个?)想法上,恐怕这远非奢侈,并且会在两个交替价格的简单序列上惨败......一个比这个问题复杂得多的问题起初看起来;-) ...第四个想法,它只需要删除一个错误的'distinct'就可以让它工作......完成【参考方案4】:您是否尝试过诸如 row_number() 之类的窗口函数
select a.[IDDATA]
, a.[CLAIM_SUBMITTED_DATE]
, a.[MATERIAL]
, a.[PRICE]
,row_number() over (PARTITION by material,price order by claim_submitted_date) as seq
from tabdata a
where a.material= '1271-4303'
SQLFiddle
【讨论】:
一个简单的ROW_NUMBER
似乎在这里没有帮助,因为我试图在给定材料的当前价格之前获得最新价格的行。所以GROUP BY
/PARTITION BY material,price
包含不属于最后一个最近材料价格组的具有相同价格的行。考虑价格可以从 20(最旧)变为 18,再变回 20,然后再变回 18(最新)。我想要最后 20 组的最近一行的日期,最旧的 20 组无关紧要。所以我必须以某种方式按连续价格分组。以上是关于获取第n个连续组的第一行/最后一行的主要内容,如果未能解决你的问题,请参考以下文章