select 前 1 * 对比 select 前 1 1

select top 1 * vs select top 1 1

我知道有很多这样的问题,但我找不到与我的问题相关的问题。

看这个问题,Is Changing IF EXIST(SELECT 1 FROM ) to IF EXIST(SELECT TOP 1 FROM ) has any side effects?

在回答中具体参考这一段:

select * from sys.objects
select top 1 * from sys.objects
select 1 where exists(select * from sys.objects)
select 1 where exists(select top 1 * from sys.objects)

我正在运行一些我自己的测试来正确理解它。如答案所示:

select 1 where exists(select top 1 * from sys.objects)
select 1 where exists(select top 1 1 from sys.objects)

两者都导致相同的执行计划,也导致与

相同的计划
select 1 where exists(select * from sys.objects)
select 1 where exists(select 1 from sys.objects)

根据我对此类问题的研究,“SELECT TOP 1 1” VS “IF EXISTS(SELECT 1”。我推断这是商定的最佳实践:

select 1 where exists(select * from sys.objects)

我的第一个问题是为什么这个比这个更受欢迎:

select 1 where exists(select 1 from sys.objects)

为了理解它,我将它们分解为更基本的表达方式(我正在使用 'top 1' 来模仿类似 exists 的执行计划):

select top 1 * from sys.objects
select top 1 1 from sys.objects

我现在看到第一个是执行时间的 80%(相对于批次 2),而第二个只有 20%。那么使用

不是更好的做法吗
select 1 where exists(select 1 from sys.objects)

因为它可以应用于这两种情况,从而减少可能的人为错误?

这两者的区别:

select top 1 * from sys.objects
select top 1 1 from sys.objects

是在第一个子句中 SQL 服务器必须从 table 中获取所有列(从任意随机行),但在第二个子句中从任意行中获取“1”就可以了指数.

当这些子句在 exists 子句中时,情况会发生变化,因为在那种情况下 SQL 服务器知道它实际上不必获取数据,因为它不会被分配给任何东西,所以它可以像处理 select 1.

一样处理 select *

因为 exists 只检查一行,它内置了内部 top 1,所以手动添加它不会改变任何东西。

在 exists 子句中使用 select *select 1 的天气只是基于意见,而不是 1 你当然可以有 2 或 'X' 或任何你喜欢的。我个人总是使用 ... and exists (select 1 ...

SQL 服务器在查询编译/优化过程中相对较早地检测到 EXISTS 谓词,并消除了对此类子句的实际数据检索,用存在性检查取而代之。所以你的假设:

I now see that the first is 80% of the execution time (relative to the batch of 2) whilst the second is only 20%.

是错误的,因为在前面的比较中,您实际上已经检索了一些数据,如果将查询放入 (not) exists 谓词中,则不会发生这种情况。

大多数时候,除了一个重要的问题外,如何测试行的存在没有区别。假设你说:

if exists (select * from dbo.SomeTable)
...

代码模块中的某处(视图、存储过程、函数等)。然后,稍后,当其他人决定将 WITH SCHEMABINDING 子句放入此代码模块时,SQL 服务器将不允许它并且可能不会绑定到当前的列列表,而是抛出错误:

Msg 1054, Level 15, State 7, Procedure BoundView, Line 6
Syntax '*' is not allowed in schema-bound objects.

所以,简而言之:

if exists (select 0 from ...)

是一种最安全、最快且通用的存在性检查方法。

EXISTS 是一种子查询,它只能 return 一个基于是否有任何行被子查询 return 编辑的布尔值。选择 1 或 * 或其他在此上下文中无关紧要,因为结果始终只是 true 或 false。

您可以通过测试这两个语句生成完全相同的计划来验证这一点。

select 1 where exists(select * from sys.objects)
select 1 where exists(select 1 from sys.objects)

您在外部查询中 select 的内容很重要。如您所见,这两个语句产生非常不同的执行计划:

select top 1 * from sys.objects
select top 1 1 from sys.objects

第一个会比较慢,因为它实际上必须 return 真实数据。在这种情况下,连接到三个基础表:syspalnames、syssingleobjrefs 和 sysschobjs。

关于您在 EXISTS 子查询中放置的内容的偏好 - SELECT 1 或 SELECT * - 没关系。我通常说 SELECT 1,但 SELECT * 也一样好,您会在很多 Microsoft 文档中看到它。

我一直在寻找 标题中包含的实际问题的答案。我找到了 at this link:

Select Top 1 or Top n basically returns the first n rows of data based on the sql query. Select Top 1 1 or Top n s will return the first n rows with data s depending on the sql query.

For example, the query below produces the first name and last name of the first 10 matches. This query will return first name and last name only.

SELECT TOP 10 FirstName, LastName
  FROM [tblUser]
  where EmailAddress like 'john%'

Now, look at this query with select top 10 'test' - this will produce the same number of rows as in the previous query (same database, same condition) but the values will be 'test'.

SELECT TOP 10 'test'
  FROM [tblUser]
  where EmailAddress like 'john%'

因此,select TOP 1 * return 是第一行,而 select TOP 1 1 return 是 仅包含“1 ”。这如果查询 returns 至少一行,否则 Null will be returned in both cases.

作为附加示例,这个:

SELECT TOP 10 'test', FirstName
  FROM [tblUser]
  where EmailAddress like 'john%'

将 return 一个 table 包含一个用 "test" 填充的列和另一个用查询的前 10 个匹配项的名字填充的列。