【问题标题】:Is the inner SELECT statement replaceable by a JOIN statement?内部 SELECT 语句是否可以被 JOIN 语句替换?
【发布时间】:2021-09-05 19:07:54
【问题描述】:

我经常处理时间序列并处理大量数据。数据位于 SQL Server 数据库中,并使用以下查询进行查询:

SELECT  id, published_date, value_date, value
FROM    timeseries t1
WHERE   t1.id = 1011
        AND t1.value_date BETWEEN '2017-01-07 00:00:00' AND '2019-09-15 15:00:00'
        AND t1.published_date = (
                                SELECT  MAX(t2.published_date)
                                FROM    timeseries t2
                                WHERE   t2.published_date <= '2019-09-21 23:59:59'
                                        AND t2.id = t1.id
                                        AND t2.value_date = t1.value_date
                                )
ORDER   BY t1.value_date

上面的查询准确地返回了我需要的数据。事实上,该查询为我提供了给定时期的最后发布数据 (value_date)。但是,问题是如果周期变得非常大,查询需要很长时间。原因是该期间有几个出版物(published_date)。

我确定它与内部 SELECT 语句有关,因为它逐行搜索最大发布日期。我使用 JOIN 语句改进/加速查询的尝试没有奏效。

您将如何修改 SQL 查询?

非常感谢。

【问题讨论】:

  • 在您检查了实际执行计划以了解如何访问数据以及为什么它“慢”之前,您不会做任何事情。 “周期变得非常大”究竟是什么意思?而这个'2019-09-21 23:59:59' 是一个等待被发现的逻辑错误——时间值可能不是您的数据类型的“最大”可能值。使用独占的边界。
  • 您是否确定问题完全出在引擎内部,而不是您的结果集的消费者或网络传输?
  • 是的,我很确定它与查询有关,与网络无关。我在 SQLite 中尝试了相同的查询并获得了大致相同的查询时间(当然具有相同的索引)。
  • @SMor 关于您的第一篇文章:您可能不了解 published_date 背后的逻辑。

标签: sql sql-server tsql join select


【解决方案1】:

或者您可以使用带条件的 max()over() 窗口函数,然后使用公用表表达式,您可以将 published_date 与 max_published_date 进行比较

with cte as 
(
  SELECT  id, published_date, value_date, value, 
          max(case when published_date <= '2019-09-21 23:59:59' then published_date end)over(partition by id,value_date)max_publish_date
  FROM    timeseries t1
  WHERE   t1.id = 1011
          AND t1.value_date BETWEEN '2017-01-07 00:00:00' AND '2019-09-15 15:00:00'
  ORDER   BY t1.value_date
select id, published_date, value_date, value from cte where published_date=max_publish_date

【讨论】:

  • 这和蒂姆的建议一样快。
  • @grima 通常这比自加入要快。 id, value_date 上的索引会很有用
【解决方案2】:

您可以使用连接重写您的逻辑:

SELECT t1.id, t1.published_date, t1.value_date, t1.value
FROM timeseries t1
INNER JOIN
(
    SELECT id, value_date, MAX(published_date) AS max_published_date
    FROM timeseries
    WHERE published_date <= '2019-09-21 23:59:59'
    GROUP BY id, value_date
) t2
    ON t2.id = t1.id AND
       t2.value_date = t1.value_date AND
       t2.max_published_date = t1.published_date
WHERE
    t1.id = 1011 AND
    t1.value_date BETWEEN '2017-01-07 00:00:00' AND '2019-09-15 15:00:00'
ORDER BY
    t1.value_date;

但是,从性能的角度来看,这里更大的问题可能是您在 timeseries 表上没有任何索引。尝试添加以下索引以提高性能:

CREATE INDEX idx ON timeseries (id, value_date, published_date);

这个索引应该使别名为t2 的子查询运行得非常快。

【讨论】:

  • 首先,感谢您的帖子。是的,我正在使用索引。不幸的是,您建议的查询所需的时间大约是我的两倍。
  • @grima 你能用索引(id, value_date, published_date) 试试我的查询吗?
  • 我使用了完全相同的索引对,结果它花费的时间是我的两倍。
  • 那么也许你应该坚持你当前的查询。
  • @DRapp 感谢您的建议。这样做和我的一样快。
【解决方案3】:
SELECT t1.id, t1.published_date, t1.value_date, t1.value
FROM timeseries t1 
INNER JOIN timeseries t2 ON t2.id = t1.id
                         AND t2.value_date = t1.value_date
WHERE t1.id = 1011 
  AND t1.value_date BETWEEN '2017-01-07 00:00:00' AND '2019-09-15 15:00:00' 
  AND t2.published_date <= '2019-09-21'
HAVING t1.published_date = MAX(t2.published_date)
ORDER BY t1.value_date;

【讨论】:

  • 查询无法执行,因为它被终止并显示消息“在 HAVING 之前需要 GROUP BY 子句”。
猜你喜欢
  • 2017-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-03
  • 2011-05-07
  • 1970-01-01
相关资源
最近更新 更多