错误的结果可能是由损坏、错误和默默更改 SQL 语句的功能引起的。
-
索引损坏。很少有索引被损坏并且索引中的数据与表中的数据不匹配的情况。当查询计划更改并使用索引时,这会导致意外结果,但对于使用表访问的不同查询,一切看起来都很正常。有时,简单地重建对象就可以解决这个问题。如果没有,您将需要创建一个完全可重现的测试用例(包括数据);将其发布到此处或将其提交给 Oracle 支持。追踪这一点可能需要几个小时。
-
Bug.在极少数情况下,错误会导致返回或更改数据时查询失败。同样,需要一个完全可重现的测试用例
诊断这一点,可能需要一段时间。
-
切换 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)
这是一个古老的神话,看起来像货物崇拜编程。)