【问题标题】:How to get latest until date and everything from date in a single SQL query如何在单个 SQL 查询中获取最新的截止日期和从日期开始的所有内容
【发布时间】:2017-07-30 20:51:27
【问题描述】:

好吧,所以我很讨厌 SQL... 如果我在 SQL Server (2015+) 中有一个表 Forecasts,其中包含 CityNameWeatherProviderNameTimeStamp、温度摄氏, 我想要的是过去 2 天报告的每个气象提供者对每个城市的预测,加上每个城市+供应商在过去2天没有预测任何东西的最新预测 em>。

是否有一个查询来获得比单独请求 2 个更高效的查询? 我知道我可以像这样获得最新的值(基于this article):

select t.CityName, t.WeatherProviderName, t.TimeStamp, t.TempCelcius from Forecasts t
  inner join (
    select CityName, WeatherProviderName, TempCelcius, max(TimeStamp) as maxTime 
    from Forecasts where TimeStamp < DATEADD(DAY,-2, GETDATE())
    group by CityName, WeatherProviderName
    ) tm on t.CityName = tm.CityName AND t.WeatherProviderName = tm.WeatherProviderName AND t.TimeStamp = tm.maxTime

(也尝试使用that SO question 的分区,但我的测试集慢了 3 倍以上)

我可以通过以下方式获得过去 2 天的所有内容:

select CityName, WeatherProviderName, TimeStamp, TempCelcius from Forecasts where TimeStamp > DATEADD(DAY,-2, GETDATE())

但不是将它们都运行到 2 个集合中并组合起来,有没有办法在一个快速的单个查询中同时获取它们?

答案说明

我按照@Forklift(谢谢)在下面的评论中的建议加入了工会。这是建议的选项中最快的。它看起来像这样:

SELECT t.CityName, t.WeatherProviderName, t.TimeStamp, t.TempCelcius FROM 
Forecasts t
  INNER JOIN (
    SELECT CityName, WeatherProviderName, TempCelcius, max(TimeStamp) AS maxTime 
    FROM Forecasts WHERE TimeStamp < DATEADD(DAY,-2, GETDATE())
    GROUP BY CityName, WeatherProviderName
    ) tm ON t.CityName = tm.CityName AND t.WeatherProviderName = tm.WeatherProviderName AND t.TimeStamp = tm.maxTime
    UNION
    SELECT CityName, WeatherProviderName, TimeStamp, TempCelcius FROM Forecasts WHERE TimeStamp > DATEADD(DAY,-2, GETDATE())

我还将@SqlZsm 标记为答案,因为它确实在单个查询中完成...所以根据您的确切需要,您可以感谢@Forklift 或@SqlZsm :)

【问题讨论】:

  • 如果您只想调用一次 SQL,可以使用 UNION 合并两个结果集。
  • 真;我想这至少可以消除往返行程。希望可能有一些东西没有运行数据库的 2 次迭代。不是很挑剔。尽可能满足我的教育需求。
  • 我认为问题在于它们实际上是 2 个具有相似形状返回数据的不同查询。我不确定您将如何通过只询问 SQL 一件事情来尝试取回这些不同的数据集。

标签: sql sql-server tsql


【解决方案1】:

这将返回过去两天的所有行,以及最近两天内没有行的行,使用带有 row_number() 的子查询:

select s.CityName, s.WeatherProviderName, s.TimeStamp, s.TempCelcius 
from (
  select t.CityName, t.WeatherProviderName, t.TimeStamp, t.TempCelcius 
    , rn = row_number() over (
        partition by t.CityName, t.WeatherProviderName
        order by t.TimeStamp desc
        )
  from Forecasts t
  ) as s
where s.TimeStamp > dateadd(day,-2, getdate())
  or rn = 1

rextester 演示:http://rextester.com/YQS70477

测试设置:

create table Forecasts (
    CityName varchar(32)
  , WeatherProviderName varchar(32)
  , TimeStamp datetime
  , TempCelcius float
)
insert into Forecasts values 
 ('Sierra Leon','CNN','19881230',30)
,('Sierra Leon','CNN','19881231',30)
,('Sierra Leon','BBC','19881231',30)
,('Sierra Leon','BBC',dateadd(day,-2, getdate()),28)
,('Sierra Leon','BBC',dateadd(day,-1, getdate()),29)
,('Sierra Leon','BBC',getdate(),30)

查询:

select s.CityName, s.WeatherProviderName, s.TimeStamp, s.TempCelcius 
from (
  select t.CityName, t.WeatherProviderName, t.TimeStamp, t.TempCelcius 
    , rn = row_number() over (
        partition by t.CityName, t.WeatherProviderName
        order by t.TimeStamp desc
        )
  from Forecasts t
  ) as s
where s.TimeStamp > dateadd(day,-2, getdate())
  or rn = 1

返回:

+-------------+---------------------+---------------------+-------------+
|  CityName   | WeatherProviderName |      TimeStamp      | TempCelcius |
+-------------+---------------------+---------------------+-------------+
| Sierra Leon | BBC                 | 09.03.2017 19:49:06 |          30 |
| Sierra Leon | BBC                 | 08.03.2017 19:49:06 |          29 |
| Sierra Leon | CNN                 | 31.12.1988 00:00:00 |          30 |
+-------------+---------------------+---------------------+-------------+

【讨论】:

  • 不,我不在这之后。我只关注 2 天前的最新值(见下面的错字注释),然后是从那以后发生的一切……例如,如果 CNN 自 1988 年以来没有对塞拉利昂做出预测,那么我想要那个。 ..但我只会在过去 2 天里接受他们所有的纽约预报。对不起,第一个查询的错字...应该是-2而不是-5。除了混乱之外没有太大的区别;但现在更正了...
  • @Wizbit 在基于此的子查询中删除了where
  • @SqlZsm 这会给我与我的第一个查询相同的 不是吗?根据帖子中的评论,我发现分区比内部连接慢很多
  • @Wizbit 我添加了一个演示,现在更有意义了吗?排名窗口函数(如row_number() over())和聚合窗口函数(如max() over())可以有不同的性能结果。我建议您先测试一下,然后再说它会变慢。 -- 最终结果可能比您之前的单个查询的总时间要慢,但如果您要 union 他们,您将承担 sort 运营商删除重复项的成本。
  • @SqlZsm。整洁的;我错过了OR。是的,单个查询;但是在我正在测试的 400k 行表上,它的输出速度比联合慢:联合 = CPU 时间 = 612 毫秒,经过时间 = 79 毫秒;分区 = CPU 时间 = 1994 毫秒,经过时间 = 187 毫秒。我仍然认为这些可能是最好的一对答案......
【解决方案2】:

你可以试试这个

select CityName, WeatherProviderName, TimeStamp, TempCelcius from Forecasts where TimeStamp > DATEADD(DAY,-2, GETDATE())
union all
select * from (
    select CityName, WeatherProviderName, TimeStamp, TempCelcius, row_number() over (partition by concat(CityName, WeatherProviderName) order by TimeStamp desc) as rn from Forecasts where TimeStamp < DATEADD(DAY,-2, GETDATE())
)
where rn = 1

【讨论】:

  • 这实际上是@Forklift 提到的联合方法与我在原始帖子中已经提到的最新值的分区方法;我说的与内部连接相同,但速度要慢 3 倍。
  • 我想我不确定你在那之后是什么。那么这仍然是一个悬而未决的问题吗?如果@SqlZim 有最佳答案,您应该将其标记为答案。
猜你喜欢
  • 2017-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-13
相关资源
最近更新 更多