检查SQL查询是否会返回结果的有效方法

25

我想编写一个查询,根据结果是否存在,简单返回1或0。

我考虑使用以下语句:

IF EXISTS(
      select * from myTable 
      where id=7 and rowInsertDate BETWEEN '01/01/2009' AND GETDATE()
)
SELECT 1
ELSE
SELECT 0

这是一般的前提条件。

最终结果将是一个更复杂的查询,使用 sp_executesql 动态构造并执行,接受一个到多个参数和构建起来的字符串。

我的问题是,假设“计数”返回376986,并需要4秒钟才能计算完成。使用 IF EXISTS 是否会在找到满足条件的第一行时立即停止?

我正在决定是使用 IF EXISTS 还是仅查询 @@ROWCOUNT 并查看其是否大于零。

我尝试了一些测试,两者速度基本相同,但在2年后当有更多数据时,使用 IF EXISTS 是否可能会提高性能呢?

谢谢

10个回答

12

这是我在项目中能够得到的最快速度:

SELECT CASE WHEN EXISTS (
  select top 1 1 
  from myTable 
  where id=7 
  and rowInsertDate BETWEEN '01/01/2009' AND GETDATE()
) THEN 1 ELSE 0 END AS AnyData

这是我将要使用的一般前提。谢谢。 - Robert

11

你的id和date列上是否有索引?

也许你只需要:

select top 1  1 from myTable where id=7 and rowInsertDate > '01/01/2009' 

注意:如果数据存在则返回1,否则不返回任何内容。

另一个编辑。如果没有数据,则不会返回值为null的行,而是不返回任何行。更像是在比喻上的null。


这将给查询优化器提供更大的提示,我期望它会更快。 - Ben S
确保获取适当的平台相关限制条件,例如 top 1 或 limit 1 等。 - Kevin
1
我非常确定如果没有匹配的记录,它不会返回NULL,但是完全没有结果,这是非常不同的。这就是为什么EXISTS实际上存在的原因。 - Alex Bagnolini
啊,我明白了,实际上查询需要四个输入参数加上一个日期范围,所以我为这个问题简化了它。谢谢,伙计,好主意。 - Robert

11

使用IF EXISTS更加高效,因为它被优化成在找到第一行后立即停止。这是我总是执行此类检查的方式,而不是使用COUNT()。

为了进行性能比较,只需在每次测试之前清除数据和执行计划缓存(仅适用于非生产数据库服务器):

DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS

这取决于DBMS的查询优化器。如果测试显示IF EXISTS和COUNT都需要4秒钟,那么IF EXISTS可能没有被优化。 - Ben S
目前结果相同,但我更关心未来的查询。随着行数的增加,我想知道计数性能是否会变慢。有人建议不会,因为表已经被分析过了,执行计数仍然很快。我越来越倾向于使用IF EXISTS。 - Robert
我会使用清除缓存、检查持续时间、CPU和通过SQL Profiler读取的方式来比较两者,并期望EXISTS在SQL Server中不仅表现更好,而且在数据量增长时也能更好地扩展。对于少量数据,差异不大,但可扩展性很重要。 - AdaTheDev
IF EXISTS已经被优化。Solid Quality Learning(http://www.solidq.com/na/OurBooks.aspx)有一些非常好的书籍,我已经读了其中的一些,尽管我忘记了哪一本,但其中一本提供了我所遇到的查询优化过程中最详细的细节。IF EXISTS与其他替代方案是我之前研究过的一个场景。 - AdaTheDev

3
我会这样写:

我只会这样写:

IF EXISTS(
      SELECT 0 FROM myTable 
      WHERE id=7 and rowInsertDate BETWEEN '01/01/2009' AND GETDATE()
)
SELECT 1
ELSE
SELECT 0

这样你就不需要返回任何数据,只需检查条件。我发现这种查询结构非常快速。


2
如果您不需要376986行数据,只是想知道是否存在某个内容,那么使用IF EXISTS会更加合理。另外,一个有用的提示是请求一个索引列(主键),而不是*,因为您并不关心实际的数据。

谢谢,我会确保只请求一个列的结果,因为我实际上并不需要这些结果。 - Robert

1
最终结果将是一个更复杂的查询,使用一个到多个参数,并使用sp_executesql构建和执行字符串。
我认为你至少需要完整的FROM、JOIN和WHERE语法,否则你的实际查询可能什么都查不到(例如添加了一个原始IF EXISTS查询中不存在的INNER JOIN,结果不满足)。
如果你要这样做,你可能想把PKs放到某种“批量ID保持表”中,这样你就可以引用第二个“演示”查询的PKs。
如果你得到了376,986个结果,你打算怎么办?如果你要在屏幕上向用户显示它们,并进行某种分页,那么在“批量ID保持表”中拥有结果可能会有所帮助(尽管显然,对基础数据的任何添加/删除等都会破坏分页显示)。
或者,如果您要使用分页,请使用TOP / LIMIT / SET ROWCOUNT将结果限制为第一页的全部内容(确保有ORDER BY以便序列可重复),然后在用户按下NEXT-PAGE按钮时解决第2页要做什么的问题(我们通过NEXT-PAGE按钮包含上一个显示的记录的PK来解决排序顺序,以便下一页可以从那一点开始恢复)。
查询优化器会根据SELECT列表执行不同的操作-因此,请求“IF EXISTS”后跟随“SELECT Col1,COl2,... FROM ...”可能实际上意味着您以不同的方式两次运行完整的查询,使用不同的缓存数据和查询计划,因此总体上可能会对您的服务器造成更大的负担,并导致用户等待时间更长,而不是只获取前一页/ 100行等。
SQL Server将缓存sp_ExecuteSQL的查询计划,但请确保参数化查询以便在可能的情况下重用缓存的计划。

是的,我一定会对生成的查询进行参数化,并将我的参数传递给 sp_executesql。我记得第一次看到它时我笑了。现在我已经很熟悉它了 :) - Robert

1
我会直接返回 select exists 而不是将其作为 int。
SELECT EXISTS(SELECT 1 FROM MY_TABLE);

它将返回:

Select result, showing single row result with column exists being true

如果您需要自定义名称,则可以执行以下操作:
SELECT EXISTS(SELECT 1 FROM MY_TABLE) AS MY_CUSTOM_NAME;

0
select isnull(
(select 1 from myTable 
  where id=7 and rowInsertDate BETWEEN '01/01/2009' AND GETDATE())
,0)

5
您的回答可以通过添加更多支持性信息来改善。请通过[编辑]添加更多详细信息,例如引用或文档,以便他人可以确认您的答案是正确的。您可以在帮助中心中找到有关如何撰写良好答案的更多信息。 - Community
1
此答案已在低质量队列中进行了审核。以下是一些关于如何撰写好的答案?的指南。仅有代码的答案不被认为是好的答案,并且很可能会因为对学习者社区不够有用而被投票降低或删除。这只是对您来说显而易见。请解释它的作用,以及它与现有的已接受答案和其他10个答案的不同之处/ 优点来自审核 - Trenton McKinney

0
首先,您应该尝试创建一个包含您认为在两年内您(或您的继任者)可能需要处理的所有数据的虚拟数据库。然后,您的测试将更加有效。
IF EXISTS() 将更快,因为数据库引擎只需找到符合您条件的第一条匹配记录。当然,使用适当的索引会更快。
另一个提示是不要使用 *,因为您实际上不需要检索列。
IF EXISTS(select 1 from myTable where id=7 and rowInsertDate BETWEEN '01/01/2009' AND GETDATE())

根据我所了解的,这应该会(从速度上)更快一些。


1
不管你使用SELECT *...EXISTS都进行了优化,这没有任何区别。 - AdaTheDev

0

我认为Alex Bagnolini的答案是正确的。系统不允许我评论他的回答(新账户)。唯一的修改是将第二个1更改为id。

有时在项目部分中减少列表(即列列表)可以使数据库引擎仅命中索引,而不是表格,从而更快。当然,这取决于您的DB引擎和索引结构/大小。(所有rowInsertDate日期应该<getDate(),因此您可以跳过该比较)

SELECT CASE WHEN EXISTS ( select top 1 id from myTable where id=7 and rowInsertDate > '01/01/2009' ) THEN 1 ELSE 0 END AS AnyData


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接