如何获取 2 列组合的最后记录?

How to get the last records for a combination of 2 columns?

我认为我的情况可以与 CamelCamelCamel、Keepa 等服务进行比较。 假设我每天跟踪几个国家的一篇文章的价格。所以我的 table,我们称它为 Trend,看起来像这样

Id     Created      ArticleId    Country    Price
-------------------------------------------------
01     19/11/05     452          US         45.90
02     19/11/05     452          CA         52.99
03     19/11/05     452          MX         99.99
04     19/11/06     452          US         20.00
05     19/11/06     452          CA         25.00
06     19/11/06     452          MX         50.00
...                
97     19/11/05     738          US         12.99
98     19/11/05     738          CA         17.50
99     19/11/05     738          MX         45.50

所以是第二天,我想更新 Trend table。如果某个国家/地区的价格仍然相同,我将跳过 article/country 组合。如果有新的价格我会添加新的记录。

现在我想查询 table 以获得每个 ArticleId / Country 组合。但只有它的最后一条记录(按时间戳排序)。因此,以上面的示例为例,我希望获得 ArticleId 452 的记录 040506。不是 010203

所以我从这个基本查询开始。但是我如何才能改变它以获得我预期的结果呢?

SELECT
    *
FROM
    Trend
ORDER BY 
    Created DESC

您可以结合使用 DISTINCTCROSS APPLY

SELECT DISTINCT ca.Id, ca.Created, t.ArticleId, t.Country, ca.Price
FROM Trend t
CROSS APPLY (SELECT TOP 1 Id, Created, Price
             FROM Trend
             WHERE ArticleId = t.ArticleId AND Country = t.Country
             ORDER BY Created DESC) ca

通常在使用 APPLY 编写查询时,您的连接列(ArticleIdCountry)构成另一个 table 上的唯一键。如果这适用于您的数据库,您可以删除 DISTINCT 并加快查询速度。

SELECT ca.Id, ca.Created, a.ArticleId, a.Country, ca.Price
FROM Article a
CROSS APPLY (SELECT TOP 1 Id, Created, Price
             FROM Trend
             WHERE ArticleId = a.ArticleId AND Country = a.Country
             ORDER BY Created DESC) ca

最后,如果您遇到性能问题,您可能需要创建一个索引。

CREATE NONCLUSTERED INDEX [NC_Trend_ArticleId] ON [Trend]
(
    [ArticleId] ASC,
    [Country] ASC,
    [Created] ASC
)
INCLUDE ([Price])

据推测 Id 是一个 PRIMARY KEY 并且已经被一个 CLUSTERED INDEX 覆盖,如果是这样,以上应该适用于大多数解决方案。

一种方法使用相关子查询进行过滤:

select t.*
from trend t
where t.created = (
    select max(t1.created) 
    from trend t1
    where t1.articleId = t.articleId and t1.country = t.country
)

为了提高性能,您需要在 (articleId, country, created) 上建立索引。

您可能还想考虑反left join方法:

select t.*
from trend t
left join trend t1 
    on  t1.articleId = t.articleId 
    and t1.country = t.country
    and t1.created > t.created
where t1.articleId is null

最后,另一种典型的解决方案是将 table 加入聚合查询:

select t.*
from trend t
inner join (
    select articleId, country, max(created) created
    from trend
    group by articleId, country
) t1 
    on  t1.articleId = t.articleId 
    and t1.country = t.country
    and t1.created = t.created

哪种解决方案性能更好取决于数据的大小和分布。