【问题标题】:How to avoid rerunning a subquery used multiple times?如何避免重新运行多次使用的子查询?
【发布时间】:2021-06-14 23:36:43
【问题描述】:

以下是我遇到的问题的简化示例。假设我有一个查询“SomeQuery”(SELECT...FROM...WHERE...),它的输出如下所示:

Status MyDate
A 6/14/2021
A 6/12/2021
B 6/10/2021
A 6/8/2021
B 6/6/2021
A 6/4/2021

我需要获取最早的状态 A 日期,该日期大于状态 B 的最大日期。在这种情况下,2021 年 6 月 12 日。

我的查询如下所示:

SELECT
  MIN(MyDate) AS DateNeeded
FROM
  SomeQuery
WHERE
  Status = 'A'
  AND MyDate > (
    SELECT
      MAX(MyDate) AS MaxDateB
    FROM
      SomeQuery
    WHERE
      Status = 'B'
  )

这可行,但我想避免运行子查询两次。我尝试为子查询的第一个实例分配一个别名,然后使用该别名代替第二个实例,但这会导致“无效对象名称”错误。

任何帮助将不胜感激。谢谢。

【问题讨论】:

  • 如果没有实际的minimal reproducible example,很难提供答案,但很可能您可以使用窗口函数。
  • 我会使用 CTE。您也许可以在日期和不同类型上将表加入回自身。
  • @drHodge 自联接可能很慢,实际上与子查询相同。窗口函数几乎可以肯定是最好的答案
  • 也许您可以编辑您的问题并提供所需的结果
  • 考虑到您实际上正在查看 2 个读数(一个 Status = A,一个 Status = B),那么几乎任何计划都需要阅读两次(甚至是 CTE)。一个窗口函数可能只执行一组读取,但会增加计算时间。另一种方法是首先将SomeQuery 的结果存储在#temporary 表中,并在Status, MyDate 上使用非聚集索引,以便以一种有帮助的方式对其进行排序。使用索引,读取次数将最少并且几乎是瞬时的;但是您需要花费额外的时间来创建临时表。

标签: sql sql-server tsql subquery


【解决方案1】:

但为了避免两次击中表格,您可以使用窗口功能:

select top(1) Mydate from 
(
 select *, max(case when Status = 'B' then Mydate end) over () MaxBDate from data 
) t 
where status = 'A'
and MyDate > MaxBDate
order by Mydate

db小提琴here

【讨论】:

    【解决方案2】:

    我不确定我是否完全理解您的需求,但可以执行以下操作:

    ;WITH cte (MaxMyDate) as
    (
       SELECT
          MAX(MyDate) AS MaxDateB
        FROM
          SomeQuery
        WHERE
          Status = 'B'    
    )
    SELECT
      MIN(MyDate) AS DateNeeded
    FROM
      SomeQuery
    WHERE
      Status = 'A'
      AND MyDate > (SELECT MaxMyDate from cte)
    

    有些人可能会觉得这更容易阅读,因为一些复杂性已转移到 cte。

    【讨论】:

    • 嗨萌,感谢您的回答,但这并不能完全解决我遇到的问题。 “SomeQuery”是一个有几行长的选择语句,所以我试图避免在多个地方使用它。然而,在今天之前我不知道 CTE,我发现如果我使用我的原始解决方案但将“SomeQuery”定义为 CTE,我可以使用它而不是列出两次查询,所以你的答案仍然有用.谢谢。
    • @StevenLightning - 如果“SomeQuery”很复杂,您可以做的是创建一个用户定义的函数 (UDF),并在其中定义您的查询。然后你可以在多个地方重复使用它。但请注意,有不同类型的 UDF - 其中一些可能存在性能问题。在您的情况下,您需要一个值为 UDF 的内联表。
    猜你喜欢
    • 1970-01-01
    • 2016-01-23
    • 1970-01-01
    • 2022-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-05
    • 1970-01-01
    相关资源
    最近更新 更多