【问题标题】:Slow query when using NOT EXIST in Query在查询中使用 NOT EXIST 时查询慢
【发布时间】:2015-10-01 10:52:00
【问题描述】:

我想就以下查询寻求一些帮助。

运行此脚本会导致系统超时。查询太慢了,只需要 5 分钟才能运行 22 条记录。我相信这与“NOT IN”声明有关。我已经在 Stackoverflow 中寻找有关此问题的答案,有些人建议使用 LEFT OUTER JOIN 和 WHERE NOT EXIST,但我似乎无法将其合并到此查询中。

   SELECT a.UserId, COUNT(DISTINCT(a.CustomerId)) AS TotalUniqueContact
FROM [UserActivityLog] a WITH(NOLOCK)
WHERE CAST(a.ActivityDatetime AS DATE) BETWEEN '2015-09-28' AND '2015-09-30' AND a.ID 
NOT IN (
    SELECT DISTINCT(COALESCE(a.activitylogid, 0))
    FROM [CustomerNoteInteractions] a WITH(NOLOCK)
    WHERE a.reason IN ('20', '36') AND CAST(a.datecreated AS DATE) BETWEEN '2015-09-28' AND '2015-09-30' AND a.UserId IN (SELECT b.Id
    FROM [User] b
    WHERE b.UserType = 'EpicUser' AND b.IsEpicEmployee = 1 AND b.IsActive = 1)
)
AND a.UserId IN (
SELECT b.Id
FROM [User] b
WHERE b.UserType = 'EpicUser' AND b.IsEpicEmployee = 1 AND b.IsActive = 1)
GROUP BY a.UserId

【问题讨论】:

  • 你能在你使用的数据库上添加标签吗..
  • 是的,NOT IN 有问题,为什么? ,因为在您的 NOT IN 条件下,SUB QUERY 中有一个 SUB QUERY,也就是说,您有 3 级查询只能在 NOT IN 中执行,这可能会导致太多时间。
  • 您需要使用INNER JOINLEFT JOIN 替换您在NOT IN 中的子查询。
  • @DyrandzFamador 已经添加了标签。无论如何,我为此使用 SQL Server 2008。

标签: sql sql-server-2008 optimization


【解决方案1】:

下面是使用 EXISTSNOT EXISTS 的等效查询:

SELECT a.UserId,
       COUNT(DISTINCT a.CustomerId) AS TotalUniqueContact
  FROM [UserActivityLog] a WITH(NOLOCK)
 WHERE CAST(a.ActivityDatetime AS DATE) BETWEEN '2015-09-28' AND '2015-09-30'
   AND EXISTS (SELECT *
                 FROM [User] b
                WHERE b.Id = a.UserId
                  AND b.UserType = 'EpicUser'
                  AND b.IsEpicEmployee = 1
                  AND b.IsActive = 1)
   AND NOT EXISTS (SELECT *
                     FROM [CustomerNoteInteractions] b WITH(NOLOCK)
                     JOIN [User] c
                       ON c.Id = b.UserId
                      AND c.UserType = 'EpicUser'
                      AND c.IsEpicEmployee = 1
                      AND c.IsActive = 1
                    WHERE b.activitylogid = a.ID
                      AND b.reason IN ('20', '36')
                      AND CAST(b.datecreated AS DATE) BETWEEN '2015-09-28' AND '2015-09-30' )
 GROUP BY a.UserId

显然,如果不了解您的数据,就很难理解什么会真正帮助您提高绩效。但这是我所期望的:

  • 我认为EXISTS/NOT EXISTS 版本的查询会有所帮助。
  • 我认为您在UserActivityLog.ActivityDateTimeCustomerNoteInteractions.datecreated 上的条件有问题。你为什么要投?不是日期类型吗?如果不是,为什么不呢?如果您可以利用这些列上的索引,您可能会获得很大的收益。但是对于演员表,我认为您不能在那里使用索引。你能做点什么吗?
  • 您还可能会从User.Id(可能是PK)和CustomerNoteInteractions.ActivityLogId 上的索引中受益。

另外,我不太喜欢使用 with (nolock) 来提高性能 (Bad habits : Putting NOLOCK everywhere)。

编辑

如果您的日期列是您在 cmets 中提到的 DateTime 类型,因此您使用 CAST 来消除时间部分,则更好的性能替代方案是 not 强制转换,而是修改您过滤列的方式。这样做将允许您利用日期列上的任何索引。这可能会产生很大的不同。

然后可以像这样进一步改进查询:

SELECT a.UserId,
       COUNT(DISTINCT a.CustomerId) AS TotalUniqueContact
  FROM [UserActivityLog] a WITH(NOLOCK)
 WHERE a.ActivityDatetime >= '2015-09-28'
   AND a.ActivityDatetime < dateadd(day, 1, '2015-09-30')
   AND EXISTS (SELECT *
                 FROM [User] b
                WHERE b.Id = a.UserId
                  AND b.UserType = 'EpicUser'
                  AND b.IsEpicEmployee = 1
                  AND b.IsActive = 1)
   AND NOT EXISTS (SELECT *
                     FROM [CustomerNoteInteractions] b WITH(NOLOCK)
                     JOIN [User] c
                       ON c.Id = b.UserId
                      AND c.UserType = 'EpicUser'
                      AND c.IsEpicEmployee = 1
                      AND c.IsActive = 1
                    WHERE b.activitylogid = a.ID
                      AND b.reason IN ('20', '36')
                      AND b.datecreated >= '2015-09-28'
                      AND b.datecreated < dateadd(day, 1, '2015-09-30'))
 GROUP BY a.UserId

【讨论】:

  • 非常感谢!我不久前运行了脚本,现在它的速度非常快。从几乎 7 分钟缩短到 1 分钟!至于您关于UserActivityLog.ActivityDateTime 的问题,我之所以选择它是因为它使用了 DateTime 格式,但不幸的是,由于系统现在很大,我对此无能为力。此外,数据库没有规范化并且没有正确引用,这就是查询往往很慢的原因。
  • 我很高兴它有帮助。我认为如果你索引你的 2 个日期列(如果它们还没有被索引),并且如果你摆脱演员表,你可以获得更好的性能。我添加了一个经过编辑的查询,以便您了解我的意思。
  • 非常感谢您的洞察力。这是非常有帮助的。我不知道该怎么感谢你。 :) 我会记下你在这里所说的一切。 :) :)
【解决方案2】:

这应该让你非常接近或完全工作:

  SELECT a.UserId, COUNT(DISTINCT(a.CustomerId)) AS TotalUniqueContact
FROM [UserActivityLog] a WITH(NOLOCK)
inner join [User] b with (Nolock) on a.userid = b.id 
        and b.UserType = 'EpicUser' AND b.IsEpicEmployee = 1 AND b.IsActive = 1
left outer join  [CustomerNoteInteractions] c with (nolock) on a.id = c.activitylogid
    and c.reason IN ('20', '36') AND CAST(c.datecreated AS DATE) BETWEEN '2015-09-28' AND '2015-09-30'
left outer join [User] d with (nolock) on c.userid = d.id 
        and d.UserType = 'EpicUser' AND d.IsEpicEmployee = 1 AND d.IsActive = 1
WHERE CAST(a.ActivityDatetime AS DATE) BETWEEN '2015-09-28' AND '2015-09-30'  
and c.activitylogid is null
GROUP BY a.UserId

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-22
    • 1970-01-01
    • 2014-08-02
    • 1970-01-01
    • 2011-06-11
    • 2019-03-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多