SQL Server:IF EXISTS大大减慢了查询速度

时间:2015-06-24 16:43:46

标签: sql sql-server sql-server-2012 query-optimization exists

(正在使用SQL Server 2012)

我找到了一些关于查询优化的主题,并将EXISTS与COUNT进行比较,但我找不到这个确切的问题。

我的查询看起来像这样:

select * from
tblAccount as acc
join tblUser as user on acc.AccountId = user.AccountId
join tblAddress as addr on acc.AccountId = addr.AccountId
... **a few more joins**
where acc.AccountId in (
    select * accountid from
    (select accountid, count(*) from tblUser
    where flag = 1
    group by accountId) as tbl where c != 1

此查询立即运行(尽管db非常大,大约70Gb)。

当我将查询包装在EXISTS中时,如下所示:

if exists
(
  **Exact same query as above**
)
begin
RAISERROR('Account found without exactly one flagged user.', 16, 1);
end
else
begin
  print 'test passed.'
end

突然,查询大约需要5-6秒才能完成。我已经尝试过指定IF EXISTS(SELECT TOP 1 FROM ...并且还尝试了NOT EXISTS(甚至更慢)。但是它们都没有加快速度。

如果正常选择查询基本上立即完成,那么有人知道为什么将它包装在EXISTS中会导致如此多的额外计算吗?和/或任何人有任何想法解决这个问题(如果原始查询找到任何记录,我只是想抛出一个错误)。

谢谢!

6 个答案:

答案 0 :(得分:7)

您是否尝试使用TOP 1运行原始查询?很可能它会一样慢。

有时当优化器认为某些东西很有可能并且只需很少的努力就会返回大量数据时(即几乎所有记录都将被返回),它主要选择循环连接,因为它只需要获得第一个一个和一个循环连接只适用于获取一对记录。当结果证明不成立时,需要花费一天的时间来获得结果。

在你的情况下,这听起来很罕见,所以这个选择伤得很厉害。尝试执行类似SELECT @count = COUNT(*) FROM ...的操作,然后检查该计数是否为非零。

答案 1 :(得分:2)

我也打过这个问题。

当我自己运行查询时,查询是10毫秒,但是一旦我将它放入If Exists,它就会花费4分钟。无论我尝试了什么,它都没有回到10ms。该问题在4台不同的服务器上重新生成,但在2台服务器上没有。服务器都具有相同的数据库备份和相同的mssql 2012补丁级别。服务器在不同的操作系统和不同的硬件设置上。

我试过

  • 调整最大内存授予 - 无影响
  • 更改并行度的阈值 - 没有影响
  • 重写查询以使其更简单 - 无影响
  • 使用top 1 - 没有影响
  • 清除了更改之间的缓存 - 没有影响
  • 将查询分解为一些索引视图,我可以(不能使用外部联接对部件进行处理) - 没有影响
  • 应用推荐的缺失索引 - 将时间从4分钟减少到3分钟,但仍然不是我预期的10毫秒。
  • 将外部联接更改为不在(子查询)中的位置 - 无影响
  • 运行sp_updateStats - 无影响

对我有用的唯一解决方案是将结果放在临时表中,并对该临时表执行if存在。

SELECT top 1 1 AS垃圾INTO #me                 来自yourCraxyQueryHere 如果存在(选择1                 从我 )                 SELECT GETDATE()

希望这会有所帮助

答案 2 :(得分:0)

尝试:

if exists
(
  select 1 from... etc
)

答案 3 :(得分:0)

对我有用的是将查询的结果设置成一个变量,然后比较这个变量,别问我为什么,它只是为我醒来。无法解释

答案 4 :(得分:-1)

你有什么,三个嵌套的子查询?子查询总是很慢。你能将其中至少一个转换成连接吗?如:

select acc.AccountId from tblAccount as acc
    join tblUser as user on acc.AccountId = user.AccountId
    join tblAddress as addr on acc.AccountId = addr.AccountId
    join (select accountid, count(*) as c from tblUser
          where flag = 1
          group by accountId) as tbl ON tbl.accountid = user.accountid
    where tbl.c != 1

答案 5 :(得分:-2)

尝试SELECT 1而不是top 1 *。您实际上不需要返回数据,只是检查是否存在记录。

如果它是一个存储过程,您也可以尝试清除查询缓存,如下所示:

获取计划句柄:

SELECT qs.plan_handle
FROM sys.procedures AS p WITH (NOLOCK)
INNER JOIN sys.dm_exec_procedure_stats AS qs WITH (NOLOCK)
ON p.[object_id] = qs.[object_id]
WHERE qs.database_id = DB_ID()
and p.name like '%SprocName%'
ORDER BY qs.execution_count DESC OPTION (RECOMPILE);

然后通过将句柄放入此调用来清除缓存:

DBCC FREEPROCCACHE (0x05000F00C616D37C40E15E64010000000000000000000000);