SQL:如何正确检查记录是否存在


206

在阅读一些与SQL Tuning相关的文档时,我发现了这一点:

SELECT COUNT(*)

  • 计算行数。
  • 通常不正确地用于验证记录的存在。

是否SELECT COUNT(*)真的如此糟糕?

验证记录是否存在的正确方法是什么?

Answers:


251

最好使用以下两种方法之一:

-- Method 1.
SELECT 1
FROM table_name
WHERE unique_key = value;

-- Method 2.
SELECT COUNT(1)
FROM table_name
WHERE unique_key = value;

第一个选择应该没有结果或一个结果,第二个计数应该为零或一个。

您使用的文档多久了?尽管您已经阅读了很好的建议,但SELECT COUNT(*)无论如何,最新的RDBMS优化中的大多数查询优化器还是会如此,因此尽管理论(和较旧的数据库)有所不同,但您在实践中应该不会注意到任何差异。


1
我将阐明我打算在“键=值”子句中使用“唯一键”,但除此之外,我仍然没有答案。
Martin Schapendonk

1
好。在这种前提下,查询实际上只会返回一个或零个记录。但是:问题不限于唯一列。另外:第二个查询count(1)等同于实际POV中的count(*)。
马丁·巴

1
问题是“验证A记录存在的正确方法是什么”。我将其解释为单数形式,例如:1条记录。我已经回答了count(*)和count(1)之间的区别。我更喜欢count(1),因为它不依赖于特定的RDBMS实现。
Martin Schapendonk

192

我宁愿完全不使用Count函数:

IF [NOT] EXISTS ( SELECT 1 FROM MyTable WHERE ... )
     <do smth>

例如,如果要在将用户插入数据库之前检查用户是否存在,则查询可能如下所示:

IF NOT EXISTS ( SELECT 1 FROM Users WHERE FirstName = 'John' AND LastName = 'Smith' )
BEGIN
    INSERT INTO Users (FirstName, LastName) VALUES ('John', 'Smith')
END

通常,我们在需要做某事时使用它(验证),那么您的答案就更完整了。
AbnerEscócio18年

值得一提的是,使用T-SQL
Bronek '19

20

您可以使用:

SELECT 1 FROM MyTable WHERE <MyCondition>

如果没有符合条件的记录,则结果记录集为空。


你是说TOP 1吗?->(从MyTable WHERE <MyCondition>中选择顶部1)
Jacob

6
不,我的意思正是“1”
克特林Pitiş

1
为了使查询优化器甚至知道您不会读取/不需要其余数据集,您应声明SELECT TOP 1 1 FROM ... WHERE ...(或为
RDBS

3
Exists运算符本身尝试仅检索绝对的最小信息,因此添加TOP 1不会执行任何操作,除了在查询大小中添加5个字符外。- sqlservercentral.com/blogs/sqlinthewild/2011/04/05/...
AquaAlex

13

其他答案相当不错,但是添加LIMIT 1(或等效的)以防止检查不必要的行也很有用。


3
如果有任何“检查是否存在”查询返回多于一行,我认为对您的WHERE子句进行仔细检查而不是对结果数进行限制会更有用。
马丁·谢本顿克

2
我认为限制使用在Oracle中,而不是在SQL Server
山塔努古普塔

7
我正在考虑它们可以合法地为多行的情况-问题是:“是否存在(一个或多个)满足此条件的行?” 在这种情况下,您不想只看全部。
JesseW

1
@Shantanu-我知道,这就是为什么我链接到(非常贯穿)en.wikipedia文章来解释其他形式的原因。
JesseW

11
SELECT COUNT(1) FROM MyTable WHERE ...

将遍历所有记录。这就是很难用于记录存在的原因。

我会用

SELECT TOP 1 * FROM MyTable WHERE ...

找到1条记录后,它将终止循环。


万一SELECT TOP 1找到一个后实际上终止了它还是继续找到了所有可以说出哪个是TOP的东西?
Eirik H 2014年

3
PS:可以肯定的是,我总是IF EXISTS (SELECT TOP 1 1 FROM ... WHERE ..)
Eirik H

Star运算符将强制DBMS访问聚集索引,而不仅仅是访问联接条件所需的索引。因此最好使用常量值,即选择top 1 1...。这将返回1或DB-Null,这取决于条件是否匹配。
eFloh

这真好。我喜欢第一个。
isxaker

10

您可以使用:

SELECT COUNT(1) FROM MyTable WHERE ... 

要么

WHERE [NOT] EXISTS 
( SELECT 1 FROM MyTable WHERE ... )

这将比SELECT *您只需为每一行而不是所有字段选择值1 来提高效率。

COUNT(*)和COUNT(列名)之间也有细微的区别:

  • COUNT(*) 将计算所有行,包括空值
  • COUNT(column name)将仅计算出现的列名称的非空值

2
您错误地假设DBMS将以某种方式检查所有这些列。count(1)和之间的性能差异count(*)仅在大多数脑瘫的DBMS中有所不同。
paxdiablo

2
不,我是说,你实际上是依赖于实现细节,当你说出这将是更有效的。如果您确实想确保获得最佳性能,则应使用代表性数据针对特定的实现对它进行概要分析,或者完全不去理会。其他任何事情都可能引起误解,并且在从(例如)从DB2迁移到MySQL时可能会发生巨大变化。
paxdiablo

1
我想说明的是,我没有透露您的答案。它有用的。我唯一关心的问题是效率要求,因为我们已经在DB2 / z中进行了评估,发现count(*)和之间没有真正的区别count(1)。我不能说其他 DBMS 是否如此。
paxdiablo

3
“其他任何事情都有可能引起误解,并且在从(例如)从DB2迁移到MySQL时可能会发生巨大变化”与在SELECT 1中实现上的差异相比,在移动DBMS时,SELECT COUNT(*)的性能下降更有可能被咬伤或COUNT(1)。我坚信编写代码可以最清楚地表达您想要实现的目标,而不是依赖优化器或编译器来默认实现所需的行为。
温斯顿·史密斯

1
误导性语句“ COUNT(*)”表示“对行计数”句号。它不需要访问任何特定的列。而且在大多数情况下,甚至不需要访问该行本身,因为任何唯一索引就足够了。
詹姆斯·安德森

9

您可以使用:

SELECT 1 FROM MyTable WHERE... LIMIT 1

使用select 1以避免不必要的字段的检查。

使用LIMIT 1 以避免不必要的行的检查。


3
好点,但是Limit在MySQL和PostgreSQL上有效,在SQL Server上最有效,您应该在答案上注明
Leo Gurdian

0

我正在使用这种方式:

IIF(EXISTS (SELECT TOP 1 1 
                FROM Users 
                WHERE FirstName = 'John'), 1, 0) AS DoesJohnExist

0

其他选择:

SELECT CASE
    WHEN EXISTS (
        SELECT 1
        FROM [MyTable] AS [MyRecord])
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.