【问题标题】:How to get the last records for a combination of 2 columns?如何获取 2 列组合的最后一条记录?
【发布时间】:2020-05-19 15:11:34
【问题描述】:

我有一种情况,我认为可以将其与 CamelCamelCamel、Keepa 等服务进行比较。 假设我每天跟踪几个国家/地区的一篇文章的价格。所以我的桌子,我们称之为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 表。如果某个国家/地区的价格仍然相同,我会跳过文章/国家/地区组合。如果有新的价格我会添加新的记录。

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

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

SELECT
    *
FROM
    Trend
ORDER BY 
    Created DESC

【问题讨论】:

    标签: sql sql-server tsql date greatest-n-per-group


    【解决方案1】:

    您可以结合使用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)通常会构成另一个表上的唯一键。如果这适用于您的数据库,您可以删除 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])
    

    大概IdPRIMARY KEY 并且已经被CLUSTERED INDEX 覆盖,如果是这样,以上应该适用于大多数解决方案。

    【讨论】:

    • 谢谢。这似乎工作得很好。尽管这大约是 SELECT * FROM Trend 的 5 倍,但大约需要 5 倍。 10 万条记录。你能想出另一种方法来获得相同的结果吗?
    • @boop 它总是需要比SELECT * 更长的时间,但考虑到这么小的数据集,它不应该花费太长时间。你有覆盖指数吗?
    【解决方案2】:

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

    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
    

    最后,另一个典型的解决方案是使用聚合查询来连接表:

    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
    

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

    【讨论】:

    • 您能否解释一下哪种解决方案在哪种情况下表现最好?一天大约有 10 万条记录。所以表会快速增长。
    • @boop:事先很难说清楚,你真的应该根据你的数据测试每个解决方案。如果您有大量行和少量组(文章/国家元组),则相关子查询可能会提供良好的性能,但这只是一种猜测。
    • @boop 。 . .具有正确索引的相关子查询通常具有最佳性能或接近它。
    猜你喜欢
    • 1970-01-01
    • 2012-01-23
    • 2023-01-28
    • 2017-12-15
    • 1970-01-01
    • 2017-05-08
    • 1970-01-01
    • 2023-03-16
    相关资源
    最近更新 更多