【问题标题】:Can I shorten this SQL query?我可以缩短这个 SQL 查询吗?
【发布时间】:2014-02-03 11:21:12
【问题描述】:

我写了一个查询来获取过去 5 个月没有活动的任务总数,包括那些在 5 个月前创建并且从未访问过的任务。

我可以进一步缩短这个查询,这样我就不必使用外部查询了。

  SELECT @NumNoActivity = COUNT(QnoActivity.AssignmentID) FROM

  (

    SELECT a.AssignmentID
    FROM Assignments a
    LEFT JOIN VISITS v ON v.AssignmentID = a.AssignmentID
    WHERE a.CurrentStatus = 1  
    AND a.StaffID = @StaffID
    GROUP BY a.AssignmentID,  a.CreatedDate
    HAVING DATEDIFF(MONTH, ISNULL(MAX(v.VisitDate),a.CreatedDate ), GETDATE())  > =5

  ) QnoActivity

【问题讨论】:

    标签: sql sql-server sql-server-2008


    【解决方案1】:

    您可以使用NOT EXISTS 删除连接:

    SELECT @NumNoActivity = COUNT(a.AssignmentID)
    FROM Assignments a
    WHERE a.CurrentStatus = 1  
    AND a.StaffID = @StaffID
    AND NOT EXISTS
            (SELECT * FROM VISITS v 
             WHERE v.AssignmentID = a.AssignmentID
             AND v.VisitDate>DATEADD(month,-5,GETDATE()))
    

    更新

    根据 Gordon Linoff 的评论,查询缺少 Assignments.CreatedDate 条件。这是一个更新的版本:

    SELECT @NumNoActivity = COUNT(a.AssignmentID)
    FROM Assignments a
    WHERE a.CurrentStatus = 1  
    AND a.StaffID = @StaffID
    AND (SELECT ISNULL(MAX(VisitDate), a.CreatedDate) FROM VISITS v 
             WHERE v.AssignmentID = a.AssignmentID) <= DATEADD(month,-5,GETDATE())
    

    这里是SQL Fiddle 来展示它是如何工作的。作业结果为:

    • 1 - 不包括在内,因为最近有一次访问
    • 2 - 包括因为没有访问和创建日期是旧的
    • 3 - 包括在内,因为只有最近一次访问
    • 4 - 不包括在内,因为没有访问但创建日期是最近的
    • 5 - 不包括在内,因为最近有一次访问

    【讨论】:

    • 你把sql查询改成3 Select Statements,而不是2 Select Statements,你觉得这是个好习惯吗
    • 选择次数本身并不重要。重要的是它将如何解决到执行计划中。此外,一个连接实际上也是 2 个选择。
    • 不,我不这么认为,你可以在这里看到很好的解释:stackoverflow.com/questions/19835485/… 你可以看到执行计划,Join 在第一种情况下执行的时间更少
    • @Szymon +1 但我认为您可以在内部查询中使用 COUNT 不需要使用外部查询,我会使用 v.VisitDate&gt;DATEADD(month,-5,GETDATE()) 而不是 DATEDIFF 所以 SQL 服务器将能够使用索引在v.VisitDate 上让它更快。
    • @HarshSharma 对于这个查询逻辑,如果你使用 JOIN,你也应该使用 GROUP BYCOUNT(DISTINCT AssignmentID)。所以NOT EXISTS 不是JOIN + GROUP BY。我不认为它会更慢,但实际速度肯定取决于索引和数据分布。
    【解决方案2】:

    还有另一种编写此查询的方法。您可以将其视为“任务”总数与过去 4/5 个月内有活动的“任务”总数之间的差异。

    以下采用这种方法。它排除了where 子句中的新“分配”,并计算了select 子句中的不同:

    SELECT (count(distinct a.AssignmentID) -
            count(distinct case when datediff(MONTH, v.VisitDate, GETDATE()) < 5 or
                                     v.VisitDate is null
                                then a.AssignmentId end)
           )
    FROM Assignments a LEFT JOIN
         VISITS v
         ON v.AssignmentID = a.AssignmentID
    WHERE a.CurrentStatus = 1 AND
          a.StaffID = @StaffID and
          a.CreatedDate <= DATEADD(month, -5, GETDATE());
    

    【讨论】:

    • 相差 6。应该返回 38 而不是 32。我认为它缺少那些没有访问过的作业。
    • @user1263981 。 . .多么尴尬,因为我专门用where 子句考虑了这种情况。我为它添加了适当的逻辑。
    猜你喜欢
    • 2021-10-31
    • 2021-08-20
    • 2020-11-05
    • 2011-04-25
    • 2022-01-17
    • 2018-01-11
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多