【问题标题】:SQL: How to properly check if a record existsSQL:如何正确检查记录是否存在
【发布时间】:2011-05-14 07:53:24
【问题描述】:

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

SELECT COUNT(*)

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

SELECT COUNT(*) 真的那么糟糕吗?

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

【问题讨论】:

    标签: sql optimization query-optimization


    【解决方案1】:

    最好使用以下任何一种:

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

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

    您使用的文档有多长时间了?尽管您已经阅读了很好的建议,但最近 RDBMS 中的大多数查询优化器都优化了SELECT COUNT(*),因此尽管理论上(和旧数据库)存在差异,但您不应该注意到实践中的任何差异。

    【讨论】:

    • 我会澄清我打算使用“key = value”子句的“唯一键”,但除此之外,我仍然支持我的答案。
    • 好的。在这个前提下,查询确实会返回一个或零个记录。但是:这个问题不限于一个独特的列。另外:第二个查询 count(1) 等价于实际 POV 中的 count(*)。
    • 问题是“验证 A 记录存在的正确方法是什么”。我将其解释为单数,如:1 条记录。我的回答已经涵盖了 count(*) 和 count(1) 之间的区别。我更喜欢 count(1),因为它不依赖于特定的 RDBMS 实现。
    【解决方案2】:

    我宁愿不使用 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
    

    【讨论】:

    • 一般我们在想做某事时使用它(验证),那么您的答案会更完整。
    • 值得一提的是使用 T-SQL
    【解决方案3】:

    你可以使用:

    SELECT 1 FROM MyTable WHERE <MyCondition>
    

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

    【讨论】:

    • 你是说TOP 1吗? -> (SELECT TOP 1 From MyTable WHERE )
    • 不,我的意思是“1”
    • 要使查询优化器甚至知道您不会读取/不需要剩余的数据集,您应该声明 SELECT TOP 1 1 FROM... WHERE... (或使用适当的查询提示为您的 RDBS)
    • Exists 运算符本身尝试仅检索绝对最小值的信息,因此添加 TOP 1 只会在查询大小中添加 5 个字符。 - sqlservercentral.com/blogs/sqlinthewild/2011/04/05/…
    【解决方案4】:

    其他答案都很好,但添加LIMIT 1(或the equivalent,以防止检查不必要的行。

    【讨论】:

    • 如果任何“检查存在”查询返回多于一行,我认为仔细检查 WHERE 子句而不是限制结果数更有用。
    • 我认为 Limit 是在 Oracle 中使用,而不是在 SQL Server 中
    • 我正在考虑它们可以合法地是多行的情况——问题是:“是否有(一个或多个)行满足这个条件?”在这种情况下,您不想查看所有这些,只需查看一个。
    • @Shantanu -- 我知道,这就是为什么我链接到解释其他形式的(非常彻底的)en.wikipedia 文章。
    【解决方案5】:
    SELECT COUNT(1) FROM MyTable WHERE ...
    

    将遍历所有记录。这就是它不能用于记录存在的原因。

    我会用

    SELECT TOP 1 * FROM MyTable WHERE ...
    

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

    【讨论】:

    • 如果是SELECT TOP 1,它会在找到一个之后实际终止还是继续找到所有能够说哪个是TOP?
    • PS:为了确保我总是IF EXISTS (SELECT TOP 1 1 FROM ... WHERE ..)
    • Star 运算符将强制 DBMS 访问聚集索引,而不仅仅是连接条件所需的索引。所以最好使用一个常数值作为结果,即选择 top 1 1 ...。这将返回 1 或 DB-Null,具体取决于条件是否匹配。
    • 很好。我喜欢第一个。
    【解决方案6】:

    你可以使用:

    SELECT 1 FROM MyTable WHERE... LIMIT 1
    

    使用select 1 防止检查不必要的字段。

    使用LIMIT 1 防止检查不必要的行。

    【讨论】:

    • 好点,但 Limit 适用于 MySQL 和 PostgreSQL,top 适用于 SQL Server,您应该在答案中注明
    【解决方案7】:

    你可以使用:

    SELECT COUNT(1) FROM MyTable WHERE ... 
    

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

    这将比SELECT * 更有效,因为您只需为每一行而不是所有字段选择值 1。

    COUNT(*) 和 COUNT(column name) 之间也有细微的差别:

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

    【讨论】:

    • 您错误地假设 DBMS 会以某种方式检查所有这些列。 count(1)count(*) 之间的性能差异只会在最脑残的 DBMS 中有所不同。
    • 不,我是说实际上是在你说它会更有效时依赖于实现细节。如果您真的想确保获得最佳性能,您应该使用代表性数据为特定实现分析它,或者完全忘记它。其他任何内容都可能具有误导性,并且在(例如)从 DB2 迁移到 MySQL 时可能会发生巨大变化。
    • 我想明确表示我并没有反对你的回答。它有用的。我唯一反对的是效率声明,因为我们在 DB2/z 中完成评估,发现count(*)count(1) 之间没有真正的区别。对于其他 DBMS'是否是这样,我不能说。
    • “其他任何内容都可能具有误导性,并且在(例如)从 DB2 迁移到 MySQL 时可能会发生巨大变化”移动 DBMS 时的 SELECT COUNT(*) 比 SELECT 1 或 COUNT(1) 中的实现差异。我坚信编写最清楚地表达你想要实现的代码的代码,而不是依赖优化器或编译器来默认你想要的行为。
    • 误导性陈述“COUNT(*)”的意思是“计算行数”句号。它不需要访问任何特定的列。在大多数情况下,甚至不需要访问行本身作为计数,任何唯一索引就足够了。
    【解决方案8】:

    其他选项:

    SELECT CASE
        WHEN EXISTS (
            SELECT 1
            FROM [MyTable] AS [MyRecord])
        THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
    END
    

    【讨论】:

    • CAST(1 AS BIT) 的目的是什么?为什么我不能只写THEN 1 ELSE 0
    • 你绝对可以返回 1 或 0。这完全取决于你最终想要什么类型的结果。我想返回布尔值而不是数值。
    【解决方案9】:

    我是这样用的:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-25
      • 2019-04-07
      • 1970-01-01
      • 2011-02-20
      相关资源
      最近更新 更多