【问题标题】:"SELECT TOP 1 1" VS "IF EXISTS(SELECT 1"“选择前 1 1” VS “如果存在(选择 1”
【发布时间】:2012-06-16 01:33:40
【问题描述】:

我有一些 .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 的返回值。

大多数性能优势都可以忽略不计。但如果两者始终返回相同的结果,那么为什么不选择 slightly 更快的方法呢?

“SELECT TOP 1 1”是检查 .NET 中是否存在记录的最佳方式吗?

(我们使用 .NET 3.5,我试图避免使用 LINQ,因为它没有在应用程序的其他地方使用。我们还有一些我们正在迁移/重写的旧版 VB6 应用程序,因此他们可能也需要执行此操作.)

编辑: 只是设计的更多细节。该记录是一个“标题”。当找到此标头时,还有另一个包含子记录的表将被读取/解析。没有记录是好事:没有工作可做。

EDIT2:缺少满足条件的记录会更频繁地发生。它们是零星的。

【问题讨论】:

  • 在没有符合条件的行时不起作用。试试SELECT TOP 1 1 FROM BigTable where 1 = 0
  • 这就是重点。我正在检查是否存在满足条件的行。
  • “编辑”之后:为什么要检查记录的存在而不是仅仅尝试获取符合条件的单个记录?如果 fetch (select) 没有返回任何记录,那么您就没有工作要做,而且它不会比只检查此类记录的存在贵多少。如果它确实返回记录,则您已加载标题数据并仅保存了一个数据库查询。
  • 这是个好问题。因为我不使用标题数据。我根据标志/状态而不是 ID 获取所有子记录。这是我正在读取的第三方数据库。
  • 感谢您的提问,我浏览了很多网站只是为了了解现有代码select top 1 1,真的,可读性对于维护和阅读您代码的其他人非常重要......跨度>

标签: .net sql-server


【解决方案1】:

根本没有区别,exists 甚至不会评估您语句的选择部分。所以,用你喜欢的那个吧。

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】:

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

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

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

    【讨论】:

    • 如果您只关心行的存在/不存在,我会使用COUNT(*) 明确警告反对,因为它必须处理整个 计算结果的表(而 TOP 1EXISTS 可以在找到单个匹配行后停止)
    • 同意@Damien_The_Unbeliever - 如果你只关心“存在”,则永远不应该使用“计数” - 这会导致额外的工作
    • Count 不好,count(*) 更糟.. 即使你别无选择,只能使用 count,至少使用 count(1)。
    【解决方案3】:

    我肯定会选择第二个选项:

    SELECT TOP 1 1
    FROM   BigTable
    WHERE  SomeColumn = 200 
    

    即使在 I/O 和 CPU 数量几乎相同的情况下,执行计划也更加简单高效。

    【讨论】:

      【解决方案4】:

      我推荐IF EXISTS(SELECT * ...),除非这实际上会导致性能问题。它以比替代方式更好理解的方式表达了查询的意图

      除非您确实需要表中的行数,否则我会避免使用COUNT(*)(如当前答案)。

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

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

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

      【讨论】:

      • 完全同意。这听起来很明显,但是让优化器清楚我们正在尝试做什么(检查是否存在)通常是获得正常性能的第一步。
      • 这看起来很棒!但是“SELECT TOP 1 1”在实际执行计划上仍然稍微快一些。也许如果这是在一个程序中?
      • @EricSwanson - 这可能归结为服务器设置中的各种细节(在硬件、软件和使用方面,实际表有多大,可用索引等) .我只是对一个中等大小的表进行了一些测试,无法区分它们之间的差异。
      • 我只是想知道为什么这更快...发送到服务器的查询文本更少? (这就是为什么我建议使用短名称的存储过程)
      • @EricSwanson - 两个文本都很短,应该只占用一个通过网络发送的数据包,因此网络开销应该会淹没查询的物理大小。
      【解决方案5】:

      这是你想要的,而不是 IF 语句

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

      【讨论】:

      • 我觉得这是最优雅的解决方案
      猜你喜欢
      • 2015-12-07
      • 2014-07-18
      • 1970-01-01
      • 2011-10-06
      • 2014-12-15
      • 1970-01-01
      • 1970-01-01
      • 2016-05-24
      • 1970-01-01
      相关资源
      最近更新 更多