在Query中使用NOT EXIST时查询速度慢

时间:2015-10-01 00:45:08

标签: sql sql-server-2008 optimization

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

运行此脚本会导致系统超时。查询速度很慢,只需要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

2 个答案:

答案 0 :(得分: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)的忠实粉丝。

修改

如果您在评论中提及的日期列类型为DateTime,那么您使用CAST来消除时间部分,那么性能更好的替代方法是 强制转换,而是修改过滤列的方式。这样做可以让您利用日期列上的任何索引。它可能会产生很大的不同。

然后可以进一步改进查询:

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

答案 1 :(得分:0)

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

  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