"SELECT TOP 1 1" VS "IF EXISTS(SELECT 1"

50

我有一些.NET代码,它以相对高的间隔检查SQL记录是否存在。我希望使此检查尽可能“便宜”。

我想知道两个查询的特性:

IF EXISTS(SELECT 1
          FROM   BigTable
          WHERE  SomeColumn = 200)
  SELECT 1 AS FOUND
ELSE
  SELECT 0 AS FOUND

VS

SELECT TOP 1 1
FROM   BigTable
WHERE  SomeColumn = 200 
它们两者产生的执行计划相似。但是,"SELECT TOP 1 1" 似乎执行更快:查询次数较少,当未找到记录时,发送到管道中的数据也较少。我还假设它在客户端运行得更快,因为我只需要检查记录计数,而不是编组 IF EXISTS 的返回值。
大多数性能优势都微不足道。但如果两者始终返回相同的结果,那么为什么不选择稍微更快的方法呢?
".NET 中检查记录存在的最佳方法是"SELECT TOP 1 1"吗?
(我们使用 .NET 3.5,并且我正在尝试避免使用 LINQ,因为它在应用程序中没有被使用。我们还有一些需要迁移/重写的旧 VB6 应用程序,所以它们可能也需要执行此操作。)
编辑: 设计上再补充一点。这个记录是一个“头部”。当找到这个头部时,会读取/解析另一个具有子记录的表。缺少记录是一件好事:没有工作要做。
编辑2:不符合条件的记录缺失情况会更频繁出现,他们会以间歇性波动的方式出现。

当没有符合条件的行时,它不起作用。请尝试使用 SELECT TOP 1 1 FROM BigTable WHERE 1 = 0 - Preet Sangha
这就是重点。我正在检查是否存在符合条件的行。 - Eric Swanson
1
在“EDIT”之后:为什么要检查记录的存在性,而不是尝试获取与您的条件匹配的单个记录?如果获取(选择)没有返回记录,则您无需执行任何操作,而且这并不比仅检查此类记录的存在性更昂贵。如果它确实返回记录,则已加载标题数据,并且只保存了一个数据库查询。 - Tomek Szpakowicz
这是一个很好的问题。因为我不使用头数据。我基于标志/状态获取所有子记录,而不是ID。这是我正在读取的第三方数据库。 - Eric Swanson
谢谢你的问题,我浏览了很多网站只是为了理解现有代码 select top 1 1,实际上,可读性对于维护和其他人阅读你的代码非常重要... - serhio
注意:这里有一篇很好的文章:http://www.sqlservercentral.com/articles/T-SQL/154149/ - JohnLBevan
5个回答

42

我建议使用IF EXISTS(SELECT * ...),除非这实际上会导致性能问题。与其他替代方案相比,它更好地表达了查询的意图。

我建议避免使用COUNT(*)(就像当前答案中那样),除非你确实需要该表中的行数。

如果你想要检查结果的行数的“效率”,我可能会选择:

select 1 where exists(select * from BigTable where SomeColumn=200)

这将生成与您的第二个查询相同的结果集(0或1行)


3
完全同意。这听起来很明显,但是向优化器清楚地表明我们想要做什么(检查是否存在某些东西)通常是获得合理性能的第一步。 - Marc Gravell
这看起来很不错!但是"SELECT TOP 1 1"在实际执行计划上仍然稍微快一些。也许如果这是在一个存储过程中? - Eric Swanson
@EricSwanson - 这可能涉及到服务器设置的各种细节(包括硬件、软件和使用情况,实际表格的大小,可用的索引等)。我刚刚对一个中等大小的表格进行了几次测试,但是无法区分它们之间的差异。 - Damien_The_Unbeliever
我只是在想为什么这会更快...是因为发送到服务器的查询文本更少吗?(这就是为什么我建议使用一个简短名称的存储过程) - Eric Swanson
@EricSwanson - 这两段文本都足够短,只需要占用一个网络数据包的大小,因此网络开销应该会超过查询的物理大小。 - Damien_The_Unbeliever
好的,感谢您完成测试!我认为现在只是在为微小的改进纠结......我宁愿使用您的查询,因为当您阅读它时,它更有意义。 - Eric Swanson

17

这是你想要的替代IF语句的内容

  SELECT ISNULL(
     (SELECT TOP 1 1 FROM BigTable where SomeCol = 200), 0);

我认为这是最优雅的解决方案。 - neeohw
这并不像“if exists”那样轻松或优雅地传达意图。 - Paul-Sebastian Manole

8

完全没有区别,exists甚至不会评估语句中的select部分。所以,请使用您喜欢的那个。

declare @test table (name varchar(20))

-- comment out inserts for testing.
insert into @test (name) values ('bob the builder')
insert into @test (name) values ('bob the builder')

-- for giggles, put 1/0 here. You'll find that divide by zero error.
select 1 from @test

-- notice that you don't receive a divide by zero error.
-- the bit in the select portion is never executed, ever.
if not exists (select 1/0 from @test) select 'Yay!'
if     exists (select 1/0 from @test) select 'Boo!'

实际上,您可以互换使用这些:
... select * ... 
... select 1 ... 
... select top 1 * ... 
... select top 1 1 ... 
... select 'John Jacob Jingleheimer Schmidt' ... 

2
我会选择第二个选项:
SELECT TOP 1 1
FROM   BigTable
WHERE  SomeColumn = 200 

执行计划更简单高效,即使I/O和CPU数量大多相同。

-16

不确定SQL Server是否适用,但在其他数据库中,标准方法是:

SELECT COUNT(*) FROM BigTable where SomeCol = 200;

实际效率取决于您的索引等。


21
如果您只关心行的存在/不存在,我会明确警告不要使用COUNT(*),因为它必须处理整个表格才能计算结果(而TOP 1EXISTS在找到单个匹配行后可以停止)。 - Damien_The_Unbeliever
5
同意@Damien_The_Unbeliever的观点-如果您只关心“存在”,则永远不应使用“计数”-这会导致额外的工作。 - Marc Gravell
计数是不好的,count(*) 更糟糕。即使你别无选择,也请至少使用 count(1)。 - KnowledgeSeeeker

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