【问题标题】:Oracle Select * returns rows but Select count(1) return 0Oracle Select * 返回行,但 Select count(1) 返回 0
【发布时间】:2016-10-19 16:11:03
【问题描述】:

所以,这很奇怪,而且我以前从未见过。我希望有人有神奇的答案可以阐明这个问题......

SELECT * FROM TABLE -- returns rows... a lot of rows

然而,

SELECT COUNT(1) FROM TABLE -- returns zero (0), as in the number zero (0) as the result

这是表结构:

CREATE TABLE TRACKING (
  A_ID NUMBER,
  D_CODE NUMBER,
  HOD NUMBER,
  ADR_CNT NUMBER,
  TTL_CNT NUMBER,
  CREATED DATE,
  MODIFIED DATE
);
CREATE INDEX HOD_D_CODE_IDX ON TRACKING (HOD, D_CODE);
CREATE UNIQUE INDEX TRACKING_PK ON TRACKING (A_ID, D_CODE, HOD);
CREATE INDEX MOD_DATE_IDX ON TRACKING (MODIFIED);
ALTER TABLE TRACKING ADD CONSTRAINT TRACKING_PK PRIMARY KEY (A_ID, D_CODE, HOD);

Oracle 表如何有行但 count(1) 返回零?我在网上做了一些搜索,但一无所获。我发现的唯一其他帖子与 MS SQL Server 有关。这发生在 Oracle 中。

有什么想法吗?有人吗?

提前感谢您提供的任何帮助。

我可能会添加另一件事,希望它能帮助解答这个难题,Oracle 作业使用此表来聚合和填充另一个表。然而,这已经做了几天了。另一个表已完全填充并显示预期的记录数。我检查了 Oracle 作业日志,它显示全部成功,没有一个错误。

【问题讨论】:

  • SELECT COUNT(1) OVER () FROM table_name 返回什么?
  • 这真是太奇怪了。
  • 您是否正在与同一用户执行SELECTs?
  • 你能展示一下你的表格结构吗?
  • 它实际上是一个表,还是一个视图?我似乎记得一个像这样的错误。添加两个查询的执行计划以及您的确切 Oracle 版本可能会有所帮助。

标签: oracle


【解决方案1】:

错误的结果可能是由损坏、错误和静默更改 SQL 语句的功能引起的。

  1. 损坏的索引。很少有索引损坏并且索引中的数据与表中的数据不匹配。当查询计划更改并使用索引时,这会导致意外结果,但对于使用表访问的不同查询,一切看起来都很正常。有时简单地重建对象可以解决这个问题。如果没有,您将需要创建一个完全可重现的测试用例(包括数据);在此处发布或提交给 Oracle 支持。追踪此问题可能需要数小时。
  2. 错误。很少有错误会在返回或更改数据时导致查询失败。同样,需要一个完全可重现的测试用例 诊断此问题,这可能需要一段时间。
  3. 切换 SQL 的功能 有几种方法可以透明地更改 SQL 语句。查看虚拟专用数据库 (VPD)、DBMS_ADVANCED_REWRITE 和 SQL 翻译框架。

为了排除#3,下面的代码向您展示了一种邪恶的方法,以及如何检测它。首先,创建架构和一些数据:

CREATE TABLE TRACKING (
  A_ID NUMBER,
  D_CODE NUMBER,
  HOD NUMBER,
  ADR_CNT NUMBER,
  TTL_CNT NUMBER,
  CREATED DATE,
  MODIFIED DATE
);
CREATE INDEX HOD_D_CODE_IDX ON TRACKING (HOD, D_CODE);
CREATE UNIQUE INDEX TRACKING_PK ON TRACKING (A_ID, D_CODE, HOD);
CREATE INDEX MOD_DATE_IDX ON TRACKING (MODIFIED);
ALTER TABLE TRACKING ADD CONSTRAINT TRACKING_PK PRIMARY KEY (A_ID, D_CODE, HOD);

insert into tracking values (1,2,3,4,5,sysdate,sysdate);
commit;

起初,一切都按预期进行:

SQL> SELECT * FROM TRACKING;

      A_ID     D_CODE        HOD    ADR_CNT    TTL_CNT CREATED   MODIFIED
---------- ---------- ---------- ---------- ---------- --------- ---------
         1          2          3          4          5 17-JUN-16 17-JUN-16

SQL> SELECT COUNT(1) FROM TRACKING;

  COUNT(1)
----------
         1

然后有人这样做:

begin
  sys.dbms_advanced_rewrite.declare_rewrite_equivalence(
    'april_fools',
    'SELECT COUNT(1) FROM TRACKING',
    'SELECT 0 FROM TRACKING WHERE ROWNUM = 1',
    false);
end;
/

现在结果是“错误的”:

SQL> ALTER SESSION SET query_rewrite_integrity = trusted;

Session altered.

SQL> SELECT COUNT(1) FROM TRACKING;

  COUNT(1)
----------
         0

这可以通过查看解释计划来检测。在下面的示例中,谓词2 - filter(ROWNUM=1) 是出现问题的线索,因为该谓词不在原始查询中。有时,解释计划的“注释”部分会准确地告诉你为什么它被转换,但有时它只是提供线索。

SQL> explain plan for SELECT COUNT(1) FROM TRACKING;

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
Plan hash value: 1761840423

------------------------------------------------------------------------------------
| Id  | Operation         | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                |     1 |     2 |     1   (0)| 00:00:01 |
|   1 |  VIEW             |                |     1 |     2 |     1   (0)| 00:00:01 |
|*  2 |   COUNT STOPKEY   |                |       |       |            |          |
|   3 |    INDEX FULL SCAN| HOD_D_CODE_IDX |     1 |       |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter(ROWNUM=1)

15 rows selected.

(在不相关的说明中 - 始终使用 COUNT(*) 而不是 COUNT(1)COUNT(1) 是一个看起来像货物崇拜编程的古老神话。)

【讨论】:

  • 有趣...你让我沿着兔子小径走,在那里我发现了一些有趣的东西。显然有人正在插入会使 PK 变紫的记录。为了解决这个问题,他们在插入语句中添加了一个“忽略”子句。当我尝试重建索引时,它表明它不能,因为有重复的记录。我正在删除 dups 并重建索引以查看是否可以解决问题。待命!
【解决方案2】:

COUNT(SomeColumn) 将仅返回 SomeColumn 包含非空值的行数。 COUNT(*) 和 COUNT('Foo') 将返回表中的总行数。

来源:Count(*) vs Count(1)

是否有可能您的第 1 列的所有寄存器都为 NULL?如果它根本没有 NULL,它应该作为返回该表中所有行的 Count(*) 工作,如果是这种情况,我们需要更多信息来帮助您。

【讨论】:

  • SomeColumn 实际上是一个常数时,这不应该是一个因素,就像这里一样。无论如何,Oracle 将count(1) 重写为count(*),至少根据我在这里阅读的帖子。
  • 1 不表示第一列它是数字文字 1 并且是非空常量;因此,它应该始终被计算在内。
  • 我尝试过计算不同的列,结果仍然相同。
猜你喜欢
  • 1970-01-01
  • 2015-05-10
  • 1970-01-01
  • 2013-04-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-26
相关资源
最近更新 更多