【问题标题】:Why count doesn't return 0 on empty table为什么 count 在空表上不返回 0
【发布时间】:2019-08-05 03:33:47
【问题描述】:

我需要计算一个表的行数,但我收到了 count(*) 异常行为的提示。

count(*) 在空表上使用多列选择时不返回结果。但如果我从 select 语句中删除其他列(单列选择),则会返回预期结果(0 行)。

在下面的代码中,您会发现多个测试来向您展示我在说什么。

下面的代码结构为:

1) 创建表

2) 空表测试多列选择,返回意外结果

3) 空表测试单列选择,返回预期结果

4) 填表测试多列选择,返回预期结果

问题

鉴于此结果,我的问题是:

为什么空表的多列选择不返回0,而单列选择返回呢?

预期结果定义

对我来说预期的结果意味着:

如果表为空,count(*) 返回 0。

如果表不为空,count 返回行数

--创建测试表

CREATE TABLE #EMPTY_TABLE(
    ID INT
)

DECLARE @ID INT
DECLARE @ROWS INT

--空表多列选择

--assignment attempt (Multi-column SELECT)
SELECT @ID = ID, @ROWS = COUNT(*) 
FROM #EMPTY_TABLE

--return Null instead of 0
SELECT @ROWS Test_01 , ISNULL(@ROWS, 1 )'IS NULL'

--Set variable with random value, just to show that not even the assignment is happening
SET @ROWS = 29

--assignment attempt (Multi-column SELECT)
SELECT @ID = ID, @ROWS = COUNT(*) 
FROM #EMPTY_TABLE

--return 29 instead of 0
SELECT @ROWS Test_02

--带空表的单列选择

--assignment attempt (Single-column SELECT)
SELECT @ROWS = COUNT(*)
FROM #EMPTY_TABLE

--returns 0 the expected result
SELECT @ROWS Test_03

--带填充表的多列选择

--insert a row
INSERT INTO #EMPTY_TABLE(ID)
SELECT 1

--assignment attempt
SELECT @ID = ID, @ROWS = COUNT(*) 
FROM #EMPTY_TABLE

--Returns 1
SELECT @ROWS Test_04

【问题讨论】:

  • 只是一个想法:select ID from #empty_table 只会返回一个结果行,如果表至少有一行,但是,select count(*) from #empty_table(隐式分组依据)应该返回一个结果行,即使有没有表格行。假设这是 sybase 中的有效查询,我假设这两个查询的结果被连接以回答您的查询,因为第一个查询不产生任何行,组合查询也不会产生任何结果,因此没有分配。但是...免责声明:我对 sybase 没有任何经验。
  • 感谢您输入 Jakumi。我实际上不知道这种行为是否也发生在其他数据库中,如 mysql 或 sqlserver。但根据我的理解,即使没有行,count(*) 也应该返回结果。在某些情况下这不适用,即在使用 group by 时(特定情况),但情况并非如此
  • 有趣。因此 Sybase 允许非聚合列不包含在 group by 中。顺便说一句,MS Sql Server 的 T-Sql 中也有同样的效果。测试here。除非这时需要 GROUP BY。
  • 显式 GROUP BY 只指定用于聚合的组,默认是属于一个组的所有行,需要显式 group by 通常只是为了在有多个未分组值时正确定义列列(可能)。
  • 额外测试:向该表添加另一个 ID。我希望结果是两条记录,每条记录为 1

标签: sql sybase sap-ase


【解决方案1】:

这个查询:

SELECT @ID = ID, @ROWS = COUNT(*) 
FROM #EMPTY_TABLE

问题是COUNT(*) 使它成为一个聚合查询,但您还想返回ID。没有GROUP BY

我怀疑你的最终问题是你忽略了这些错误。

This SQL Fiddle 使用 SQL Server(类似于 Sybase)。但是,失败是相当普遍的,并且由于查询在几乎任何数据库中都不起作用。

【讨论】:

  • 即使分组结果也是一样的。你是对的 - 他忽略了错误。
  • 嗨@Gordon Linoff,我怀疑这是一般行为,但尚未测试。这就是为什么我真的很想了解这种行为。我忽略了哪些错误?如果没有显示错误?如果错误不存在,我不能忽略它。我可以忽略的是一些行为,而不是错误,因为在我介绍的案例中它们都没有。
【解决方案2】:

如果您正在计算行数并在没有行时尝试获取 ID - 您需要检查它们是否存在。 像这样的:

SELECT COUNT(*), 
    (CASE WHEN EXISTS(SELECT ID FROM EMPTY_TABLE) THEN (SELECT ID FROM EMPTY_TABLE) ELSE 0 END) AS n_id 
FROM EMPTY_TABLE

如果超过 1 行,您将收到子查询错误。

【讨论】:

  • 嗨@M.N.,我认为这不能回答问题
  • @Nelssen,如果您没有要返回的内容 - 查询将返回...什么都没有(不是 NULL)。 COUNT 和 EXISTS 不是从表中返回原始数据,但如果没有数据,则返回 COUNT 0,并且使用 EXISTS 提供您需要的一切。试试这样:SELECT ID / 100 AS FROM EMPTY_TABLE WHERE ID = 999 [nothing]。并选择 100 /(从 EMPTY_TABLE 中选择 g1,其中 ID = 999)[NULL]。因此,在您的情况下,如果您想将其与 count 一起使用,则查询需要返回一些内容。
  • 您好 M.N.,感谢您的努力,这不是“为什么我们需要做这个解决方法”的答案。此解决方案仅适用于非常特殊的情况,即 0 行和 1 行。如果它适用于所有场景(0、1 或多行),我会投票赞成,但不能像现在这样。确保您下次提供更可持续的解决方案:)
【解决方案3】:

所以我阅读了 sybase 的分组机制,并得出结论,在您的查询中,您有一个“Transact-SQL 扩展列”(请参阅​​:docs on group by 下的用法 -> Transact-SQL extensions to group通过并拥有):

包含聚合的选择列表可以包含不是聚合函数的参数且不包含在 group by 子句中的扩展列。扩展列会影响最终结果的显示,因为会显示额外的行。* (强调我的)

(关于*:这最后一条语句在您的特定情况下实际上是错误的,因为一行变成零行)

同样在 docs on group by 下的 Usage -> How group by 和使用聚合的查询你会发现:

group by 子句针对 group by 表达式中的每个唯一值将剩余的行收集到一个组中。 省略 group by 会为整个表创建一个组。 (强调我的)

所以本质上:

  1. 拥有COUNT(*) 将触发整个查询成为一个聚合,因为它是一个聚合函数(导致隐式GROUP BY NULL
  2. SELECT 子句中添加ID,然后将第一组(不包含行)扩展为其包含的行(无)并将其与聚合结果列连接在一起。

在您的情况下:计数为 0,因为您还查询了 id,对于每个 id,将生成一行,并将计数附加到该行。但是,由于您的表没有行,因此没有任何结果行,因此没有分配。 (一些示例在链接的文档中,由于没有 id 并且现有 id 必须在结果的 id 列中,...)

要始终获得计数,您可能应该只使用SELECT @ROWS = COUNT(*) 并分别选择 ID。

【讨论】:

  • 伟大的搜索@Jakumi,不幸的是这个具体的例子没有显示在链接中,但他们给出了一个很好的想法,说明在这种具体情况下会发生什么。阅读后我得出结论,该组是在没有行的情况下创建的,并且由于它被扩展,它将与没有行的表连接,并且(内部)将一个空表与另一个表连接的结果是一个空表.感谢您提供参考链接。
  • @Nelssen - 来自错误论点的正确结论 ;-)。您的问题与JOIN(可以这么说)无关,而与GROUP BY 无关。正如 Jakumi 所写:COUNT() 没有什么可分组的,所以根据定义,什么都不能显示。这就是它的工作原理。你说得对,这可能是由于加入空表造成的,但即使什么都没有,COUNT() 也会返回一些东西,除非混合中有 GROUP BY
猜你喜欢
  • 2020-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-22
  • 1970-01-01
  • 2015-09-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多