Fre*_*ope 4 oracle oracle-10g sap-hana
我们正在使用 SAP SLT 将 HANA 上的 SAP ECC 6.0 中的表复制到 Oracle 10g 仓库中。自开始以来,我们注意到NOT NULLHANA 中的列定义保留在表的 Oracle 副本中,但 HANA 将许多值存储为空字符串。Oracle 将空(varchar)字符串存储为NULLs 并且不知何故这与NOT NULL列定义不冲突(即我们NULL在列中定义为NOT NULL)。
查询这些表会产生奇怪的结果:
SELECT COUNT(*) FROM warehouse.table WHERE col IS NULL;
0
SELECT COUNT(*) FROM warehouse.table WHERE col = '';
0
SELECT COUNT(*) FROM warehouse.table GROUP BY NVL(col,'N');
X 503206
N 2377222
Run Code Online (Sandbox Code Playgroud)
所以我们可以通过使用或函数来 NULL判断这些列中有值,但是查询它们会返回奇怪的结果。NVLDECODE
一旦我们改变了列,我们就会得到正确的结果:
ALTER TABLE warehouse.table MODIFY (col NULL);
Table altered.
SELECT COUNT(*) FROM warehouse.table WHERE col IS NULL;
390986
Run Code Online (Sandbox Code Playgroud)
但当然我们不能改变列:
ALTER TABLE warehouse.table MODIFY (col NOT NULL);
ERROR at line 1:
ORA-02296: cannot enable (warehouse.) - null values found
Run Code Online (Sandbox Code Playgroud)
我不知道这是 Oracle 实现空字符串存储的问题,还是可能只是与 SAP 的 SLT 复制交互的一个怪癖。似乎 Oracle 不应该允许这些带有''或NULL值的行,因为复制尝试将它们放在那里,但我们没有看到任何错误表明这一点。
编辑以添加超立方体请求的查询:
SELECT LENGTH(col) FROM warehouse.table GROUP BY LENGTH(col);
2377222
1 503206
Run Code Online (Sandbox Code Playgroud)
你描述的情况不正常。
正如评论中已经提到的,在 Oracle 数据库中,空/零长度字符串被视为NULL.
https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements005.htm#i59110
笔记:
Oracle 数据库当前将长度为零的字符值视为空值。但是,这在未来版本中可能不再适用,Oracle 建议您不要将空字符串视为空字符串。
此外,WHERE col = ''永远不会返回结果,因为它基本上是WHERE col = NULL.
通过手动破坏数据字典来重现这些错误结果是相当简单的,但我不知道是什么导致了您的环境中仅凭这些信息。
SQL> create table t1 (c1 varchar2(20) not null disable);
Table created.
SQL> select constraint_name from user_constraints where table_name = 'T1';
CONSTRAINT_NAME
------------------------------
SYS_C005148
SQL> insert into t1(c1) values ('');
1 row created.
SQL> commit;
Commit complete.
SQL> select count(*) from t1 where c1 is null;
COUNT(*)
----------
1
Run Code Online (Sandbox Code Playgroud)
到目前为止一切正常。
数据库可以在基于约束执行 SQL 语句时跳过整个步骤。如果您在 上启用、验证、NOT NULL约束col,并且您的谓词为col is null,则数据库知道该列不能包含NULL,因此它将返回 0 行而不实际执行相关步骤。如果您对某列启用了已验证的约束,则该NULL$列的COL$字典表中的列将设置为 1。但即使使用已禁用、未验证的约束,损坏NULL$也足以使数据库返回错误的结果。
启用该约束的正确方法应该是(显然失败):
SQL> alter table t1 modify constraint SYS_C005148 enable validate;
alter table t1 modify constraint SYS_C005148 enable validate
*
ERROR at line 1:
ORA-02293: cannot validate (SYS.SYS_C005148) - check constraint violated
Run Code Online (Sandbox Code Playgroud)
现在我NULL$手动设置:
SQL> update col$ set null$ = 1 where obj# = (select object_id from user_objects where object_name = 'T1') and name = 'C1';
1 row updated.
SQL> commit;
Commit complete.
Run Code Online (Sandbox Code Playgroud)
然后再次运行查询:
SQL> select /*+ gather_plan_statistics */ count(*) from t1 where c1 is null;
COUNT(*)
----------
0
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID fjf8bcs2hhb7b, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(*) from t1 where c1 is null
Plan hash value: 4294799605
----------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 |
|* 2 | FILTER | | 1 | | 0 |00:00:00.01 |
| 3 | TABLE ACCESS FULL| T1 | 0 | 1 | 0 |00:00:00.01 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(NULL IS NOT NULL)
Note
-----
- dynamic sampling used for this statement
Run Code Online (Sandbox Code Playgroud)
返回 0 行,该表根本没有被访问(Starts = 0on Operation Id 3),因为FILTERon Operation Id 2: NULL IS NOT NULL,这显然是FALSE.
如果使用NVL(col, 'N')代替col,则数据库无法使用这种优化,将访问表并返回正确的结果:
SQL> select /*+ gather_plan_statistics */ nvl(c1, 'N'), count(*) from t1 group by nvl(c1, 'N');
NVL(C1,'N') COUNT(*)
-------------------- ----------
N 1
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------
SQL_ID 0zfyk18knxtfk, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ nvl(c1, 'N'), count(*) from t1 group by nvl(c1, 'N')
Plan hash value: 136660032
----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 3 | | | |
| 1 | HASH GROUP BY | | 1 | 1 | 1 |00:00:00.01 | 3 | 1156K| 1156K| 323K (0)|
| 2 | TABLE ACCESS FULL| T1 | 1 | 1 | 1 |00:00:00.01 | 3 | | | |
----------------------------------------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement
Run Code Online (Sandbox Code Playgroud)
FILTER这次没有,并且该表已被访问(Starts 1上Operation Id 2 - TABLE ACCESS FULL)。
10g 中存在一个错误,该错误导致对传递谓词的CHECK约束进行优化时导致错误结果NULL:
错误 5462687 - CHECK 约束可能导致错误结果(文档 ID 5462687.8)
这对我没有帮助,因为我自己破坏了字典。但是,如果您的实际查询比您发布的更复杂(否则不要理会这个),并且有传递谓词,您可以尝试在此编写的解决方法,并通过设置事件 10195 禁用此行为
$ oerr ora 10195
10195, 00000, "CBO don't use check constraints for transitive predicates"
// *Cause:
// *Action:
Run Code Online (Sandbox Code Playgroud)
例子:
alter session set event '10195 trace name context level 1';
Run Code Online (Sandbox Code Playgroud)
我不认为这是由数据的存储方式引起的,而只是与约束有关。