Oracle 不区分空字符串和空字符串?

Posted

技术标签:

【中文标题】Oracle 不区分空字符串和空字符串?【英文标题】:Oracle not distinguishing between nulls and empty strings? 【发布时间】:2010-11-19 02:01:51 【问题描述】:

显然,oracle 似乎无法区分空字符串和空值。例如。

Select name from TABLE_A where id=100;
  ID   NAME
  100  null

Update TABLE_A set NAME='' where id=100;
SELECT -->
  ID   NAME
  100  null

SELECT length(NAME) FROM TABLE_A WHERE id=100;
  null

我想不出任何好的理由为什么要构建 Oracle 以这种方式运行(它在 sqlplus 中也这样做吗?-我正在通过 java 接口访问,引用的文章使用了 php 客户端)。

您至少不想区分 0 长度和未定义长度吗?这是一个已知的问题?出于某种特定目的的故意行为?数据库理论的长期争论?是什么赋予了?

(这是由 Matt Solnit 对 this question 的回答提示的。)

【问题讨论】:

这个问题已被发布为相关问题,尽管它没有解决“为什么”:***.com/questions/1171196/… 【参考方案1】:

Oracle 非常非常非常老了。

回到80's 开发时(在没有任何标准之前),他们认为这是一个好主意,然后考虑到Oracle 存储其值的方式,确实如此。

Oracle 存储数据的方式如下(取自documentation):

数据中没有存储数据类型,只有数据长度和数据本身。

如果NULL 出现在具有值的两列之间,它会存储为单字节,这意味着列的长度为0(实际上是0xFF)。尾随 NULLs 根本不存储。

所以要存储'test'的值,Oracle需要存储5个字节:04 74 65 73 74

但是,要同时存储空字符串和NULLOracle 只需将数据长度设置为0

如果您的数据要存储在20 Mb 硬盘上,每个硬盘的成本为5,000$,这非常聪明。

后来,当标准出现时,它不再是一个好主意,但到那时已经有很多代码依赖于 NULL'' 是同一个东西。

VARCHAR 做这样的区分会破坏的代码。

为了解决这个问题,他们将VARCHAR 重命名为VARCHAR2(这不是任何标准的一部分),声明VARCHAR2从不区分NULL 和空string 并敦促大家改用这种数据类型。

现在他们可能正在等待Oracle 数据库中最后一个使用VARCHAR 的人死亡。

【讨论】:

唯一的原因是历史原因?而且25年都没有修好?考虑到它的成本如此之高,这怎么可能? ;) 成本!= 质量。我从未使用过 Oracle,但请查看 Lotus Notes。该软件的成本也很高,但很少有人会说它是一个高质量的应用程序。 @Steve 这是向后兼容性的问题。这种行为给新开发带来了一些问题,但是将其更改为 null != 空字符串会带来数千倍的问题,即升级到新的 Oracle 版本后代码突然无法正常运行。 @FrankSchmitt:我认为它们的意思是“不要依赖str IS NULL 过滤掉空字符串”或类似的东西,因为空字符串可能不再满足这个条件。 @FrankSchmitt:在 Oracle 中,您不必这样做,但您应该这样做。仅仅str IS NULL 在Oracle 中就可以了(截至目前),但在其他引擎中您应该添加str IS NULL OR str = ''。他们的观点是你在 Oracle 中也坚持第二个。【参考方案2】:

您可能想阅读两年多前在 OTN 上关于这个确切主题的冗长且有时有趣的讨论:http://forums.oracle.com/forums/thread.jspa?threadID=456874&start=0&tstart=0

问候, 抢。

【讨论】:

【参考方案3】:

这就是为什么像 Date 这样的聪明人说你不应该使用空值。

(不,我必须准确地说。事实上,这只是他在过去几十年中提到的支持这一主张的近数百个理由中的一个。)

编辑

其实我也想回应一下:

“使用 VARCHAR 进行这种区分会破坏大量代码。”

是的,可以肯定的是,通过在每次更新时将“空字符串”替换为 null 来至少打破标准的精神是一种较小的邪恶?

(注意:null 不等于任何东西,甚至不等于它本身,因此在将空字符串分配给列之后,oracle 会在该列中为您提供一个与您说想要的值不同的值出现在那里。哇。)

【讨论】:

【参考方案4】:

看起来甲骨文已经说过这种行为可能会在未来的版本中改变。没有提及何时以及哪个版本。

如果您可以访问metalink,请查看注释:1011340.6(不幸的是,由于限制,我无法在此处复制注释的内容)

如果您无法访问 metalink,请查看 10g 第 2 版文档中的以下内容here

【讨论】:

【参考方案5】:

@Ian,回复你。

Oracle 触发器可以引用创建它们的表:

create table t (id number(10) );

create or replace trigger  t_bir before insert  on  t for each row
declare
  l_id t.id%type;
begin
  select id
  into   l_id
  from   t
  where  id = :new.id;
exception
  when no_data_found then 
    null;
end;
/


SQL> insert into t values (20);

1 row is created.


SQL> select * from t;

        ID
----------
        20

【讨论】:

@tuinstoel:只是因为您使用的是 VALUES 子句。如果切换到INSERT SELECT: SQL> insert into t select 20 from dual;插入 t select 20 from dual * 第 1 行出现错误:ORA-04091:表 RWIJK.T 正在变异,触发器/函数可能看不到它 ORA-06512:在“RWIJK.T_BIR”,第 4 行 ORA-04088:期间出错执行触发器'RWIJK.T_BIR'

以上是关于Oracle 不区分空字符串和空字符串?的主要内容,如果未能解决你的问题,请参考以下文章

如何区分 .NET 中的空字符串和零长度字符串?

oracle存空字符串怎么会变成null

区分空字符串和null

Oracle数据库空字符串和空字符串比较的问题

用于测试非空字符串和非空字符串的兼容 SQL

Oracle 11 PL/SQL:检查变量是不是为空、空字符串和无结果