【问题标题】:Explaining COUNT return value when used without group不使用组时解释 COUNT 返回值
【发布时间】:2020-03-22 11:49:15
【问题描述】:

COUNT 返回在组中找到的项目数,默认情况下它包括 NULL 值和重复项。

前两个语句返回错误,后两个 - NULL:

1/0
1/'A'
1/NULL
NULL/1

但包裹在COUNT 中,它们返回10

SELECT COUNT(1/0);    -- 1
SELECT COUNT(1/'A');  -- 1
SELECT COUNT(1/NULL); -- 0
SELECT COUNT(NULL/1); -- 0

NULL 案例可以由文档解释为

COUNT(ALL expression) 计算组中每一行的表达式, 并返回非空值的数量。

因此,它们被评估,它们返回 NULLs,而 non null 值只是计数,我们得到 0

我想知道为什么前两个返回1

将值放在表中,一切看起来都很正常 - 抛出错误并且没有返回值:

DECLARE @DataSource TABLE
(
    [valueA] INT
   ,[valueB] INT
);

INSERT INTO @DataSource ([valueA],[valueB])
VALUES (1, 0);

SELECT COUNT([valueA]/[valueB])
FROM @DataSource;

(1 行受影响) Msg 8134, Level 16, State 1, Line 16 除以零 遇到错误。

完成时间:2020-03-22T13:47:44.5512536+02:00

也许这个1 row affected 被返回为COUNT

【问题讨论】:

  • 1 rows affected 消息适用于您的 insert 语句。 select count(1/0) 很好奇,不过,我预计那里会出现除以零的错误。
  • 哦!我知道你在那里做了什么...SET ARITHABORT OFF; select count(1/0);

标签: sql sql-server tsql group-by count


【解决方案1】:

COUNT 表达式中指定常量时,SQL Server 可以在编译时优化查询,避免在执行时计算表达式。结果值在前 2 个查询中永远不能是 NULL,在最后 2 个查询中始终是 NULL

下面是来自SELECT COUNT(1/0)SELECT COUNT(1/NULL) 执行计划的sn-ps:

<ScalarOperator ScalarString="Count(*)">
<Const ConstValue="NULL" />

客户端工具 (SSMS) 生成的“1 行受影响”消息,反映了 INSERT 语句返回的行数。如果不需要,请将SET NOCOUNT ON; 添加到脚本的开头。

【讨论】:

  • 这是有道理的。您使用 SSMS 和执行计划得到了这个运算符?
  • 是的,通过在 SSMS 中显示计划并右键单击以查看 XML。
  • 我明白了。因此,在编译时,引擎对表达式是否为 NULL 或其他内容感兴趣。如果它是NULL,它会被忽略,如果它是其他东西(产生错误的事件),它将被计算在内。这就是SELECT COUNT(1/0) FROM sys.tables; 计数正确的原因。
【解决方案2】:

正如您在进一步复制的文档中看到的那样,COUNT(&lt;expression&gt;) 计算每个 非空 值。如果要统计组中的所有记录,无论此列或那列的值如何,都需要COUNT(*)(或其他等效表达式,例如COUNT(1))。

COUNT() 中的表达式针对每一行进行评估;如果发生错误,则整个查询都会出错。我倾向于认为SELECT COUNT(1/0) 返回1 是一种违反直觉的行为,可能是由于过早优化(SELECT 1/0 会出错)。

但在现实生活中,就像在你的脚本中一样,你可以看到an error is raised

遇到除以零错误。

编辑 - 在其他数据库上进行测试显示结果不一致:

  • 在 MySQL、MariaDB 10.4 和 SQLite 中:SELECT COUNT(1/0) 返回 0

  • 在 Postgres 中:ERROR: division by zero

  • 在 Oracle 中:ORA-01476: divisor is equal to zero

  • 在 DB2 中:返回 1

【讨论】:

  • 我认为该操作具有SET ARITHABORT OFF 有效。在这种情况下,select count(1/0) 会返回 1。
  • @AlwaysLearning,select count(1/0) 总是返回 1,不管 ARITHABORT 设置为 ON 还是 OFF,试试吧。
  • @AlwaysLearning:好像不是related to ARITHABORT
  • 我同意这种行为是反直觉的,但我不会过早地调用优化。结果反映了COUNT 文档的正确语义。
  • ISO SQL 标准的许多细节由实施者定义,而且大多数 DBMS 产品并不严格遵守。我认为 ISO 标准要求在 SELECT 查询中使用 FROM 子句,但 SQL Server、PostgreSQL、Sybase 等允许省略它。
猜你喜欢
  • 2010-11-05
  • 1970-01-01
  • 2014-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多