为什么Oracle 9i将空字符串视为NULL?

240

我知道它将空格字符' '视为NULL,但这并没有告诉我为什么会这样。据我了解SQL规范,' '并不等同于NULL——一个是有效的数据值,而另一个则表示缺少相同的信息。

你可以自由猜测,但请指出是否如此。如果有任何来自Oracle的人可以对此发表评论,那将非常棒!


10
随意猜测吧?但我不认为那会给你最好的答案集。 - SCdF
1
我想不会有,但我不确定这个问题是否有任何确定的答案,所以我觉得我应该打开大门。目前看来似乎效果还不错。 - Chris R
http://docs.oracle.com/database/121/SQLRF/sql_elements005.htm#SQLRF51094 - Jayan
相关链接:http://dba.stackexchange.com/q/49744/56961 - AlikElzin-kilaka
10个回答

239

我认为答案是Oracle非常古老。

在 SQL 标准出现之前的古老时期,Oracle 做了一个设计决策:在 VARCHAR/VARCHAR2 字段中,空字符串等同于 NULL,且只有一种 NULL 的含义(有关系理论家会区分从未被提示过的数据、用户不知道答案但存在的数据、没有答案的数据等,所有这些都构成某种 NULL 含义)。

当 SQL 标准出现并同意 NULL 和空字符串是不同的实体时,已经存在一些假定二者相等的 Oracle 用户代码。所以,Oracle 基本上只能选择破坏现有代码、违反 SQL 标准,或引入某种初始化参数来改变可能大量查询的功能。在这三个选项中,违反 SQL 标准(在我看来)是最不具破坏性的。

Oracle 保留了 VARCHAR 数据类型在将来的版本中遵循 SQL 标准的可能性(这就是为什么每个人在 Oracle 中使用 VARCHAR2,因为该数据类型的行为保证将来保持不变)。


63

Tom Kyte Oracle副总裁:

一个长度为零的varchar被视为NULL。

''不被视为NULL。

当''被赋值给char(1)时,它变成了' '(char类型是空格填充的字符串)。

当''被赋值给varchar2(1)时,它变成了'',这是一个长度为零的字符串,在Oracle中,一个长度为零的字符串是NULL(它不再是'')。


19
哇,汤姆相当讽刺。考虑到这些问题涉及SQL92的严重分歧,你会认为他会对此保持更少的攻击性......尽管他可能已经厌倦回答这些问题。 - Chris R
8
Tom最好的地方在于他给出明确的回答,准确地表达了他的想法。在Ask Tom上寻找一些人使用缩略语的评论。 - Chris Gill
10
如果第二行改为“*''不总是被视为NULL*”,则会更加准确。 - ypercubeᵀᴹ
3
改变 Tom 实际使用的词语并不能使引述更加精确。如果你认为 Tom 的措辞令人困惑,那可能吧。我认为他说得很准确。最令人困惑的情况是当 '' 被隐式转换为 VARCHAR2 时,例如 cast('' as char(1)) is null,这...居然是真的(参见链接)。 - sehe
1
@sehe 让我困惑的是 select 1 from dual where ('' is null)。 - matt freake
显示剩余6条评论

21

Oracle文档警告开发人员存在这个问题,至少可以追溯到版本7。

Oracle选择使用“不可能的值”技术来表示NULLS。例如,数字位置中的NULL将被存储为“负零”,这是一个不可能的值。任何由运算产生的负零都将在存储之前转换为正零。

Oracle还错误地选择认为长度为零(空字符串)的VARCHAR字符串是一个不可能的值,并且是表示NULL的合适选择。事实证明,空字符串远非不可能的值。甚至是字符串连接操作下的身份元素!

Oracle文档警告数据库设计人员和开发人员,未来的某个版本可能会打破空字符串和NULL之间的关联,并破坏任何依赖该关联的代码。

除了不可能的值之外,有一些标记NULLS的技术,但Oracle没有使用它们。

(我在上面使用“位置”一词来表示行和列的交叉点。)


Oracle文档警告数据库设计师和开发人员,未来的某个版本可能会打破空字符串和NULL之间的关联,并破坏任何依赖该关联的代码。请问您能提供此声明的参考资料吗? - Piotr Dobrogost
3
https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements005.htm - Walter Mitty

19

如果您把Oracle看作早期开发人员可能会这样做的方式-作为数据输入系统的后端,我认为这个问题就更有意义了。 数据库中的每个字段都对应着数据输入操作员在屏幕上看到的表单中的一个字段。 如果操作员没有在字段中输入任何内容,无论是“出生日期”还是“地址”,那么该字段的数据就是“未知的”。 没有办法让操作员指示某人的地址实际上是空字符串,而且这也没有太多意义。


8
只有在假设数据输入系统中的每个字段都是必填项时,这种说法才有意义。对于非必填字段(例如“狗的名字”),不回答是有效的,因此空字符串仍然有与NULL不同的目的。即使做出了这种假设,我怀疑早期开发人员将Oracle视为数据输入系统的“华丽后端”,因此我不确定这个答案是否有任何意义。 - Jared

1

根据官方11g文档的说法

目前,Oracle数据库将长度为零的字符值视为null。然而,在未来的版本中可能不再如此,Oracle建议您不要将空字符串视为null。

可能的原因

  1. val IS NOT NULLval != ''更易读
  2. 无需检查两个条件val != '' and val IS NOT NULL

8
在一个完全符合ANSI标准的数据库中,您不必检查这两个条件。val <> ''已经排除了NULL。也许您的意思是val = '' OR val IS NULL。但是,不与NULL进行比较的空字符串是有用的! - ErikE
我同意比较部分。 - Sorter

0

空字符串与NULL相同,只是因为当这两种情况不同时,它是“较小的恶”。

在NULL和空字符串不同的语言中,必须始终检查这两个条件。


1
只需在列上设置“非空”约束,并仅在空字符串上进行检查。 - Egor Skriptunoff
8
检查这两个条件很简单:只有在字段不为NULL且不为空的情况下,WHERE Field <> ''才会返回true,在具有ANSI行为的数据库中,空字符串也会被视作非空。 - user565869
2
通过使它们相同,您将面临唯一性问题,因为 NULL !== NULL,但 '' === '',所以我认为这不是较小的罪恶。它会增加混淆。 - Tomáš Fejfar

-1

书中的例子

   set serveroutput on;   
    DECLARE
    empty_varchar2 VARCHAR2(10) := '';
    empty_char CHAR(10) := '';
    BEGIN
    IF empty_varchar2 IS NULL THEN
    DBMS_OUTPUT.PUT_LINE('empty_varchar2 is NULL');
    END IF;


    IF '' IS NULL THEN
    DBMS_OUTPUT.PUT_LINE(''''' is NULL');
    END IF;

    IF empty_char IS NULL THEN
    DBMS_OUTPUT.PUT_LINE('empty_char is NULL');
    ELSIF empty_char IS NOT NULL THEN
    DBMS_OUTPUT.PUT_LINE('empty_char is NOT NULL');
    END IF;

    END;

-2

因为不将其视为NULL并不特别有帮助。

如果在Oracle中在此领域犯了错误,通常会立即注意到。然而,在SQL Server中,它似乎可以正常工作,问题只会在某人输入空字符串而不是NULL时出现(可能来自.NET客户端库,其中null与“”不同,但通常将它们视为相同)。

我并不是说Oracle是正确的,但在我看来,两种方式都差不多糟糕。


3
调试更加容易。如果你看到屏幕上的空单元格或输入,则知道DB中的数据为空。在其他DB中,' '<> NULL,无法“看到”数据是NULL还是'',这会导致非常难以发现的错误。''=null是最合理的选择,即使它不是标准。 - Lucio M. Tato
5
通常,数据库工具将 NULL 值和空字符串显示为不同的值。实际上,即使是 Oracle SQL Developer 也会将 NULL 值显示为“(null)”。我猜这是为了区分 NULL 和空格,但这与 NULL 值和空字符串之间的差异无关。在其他数据库中,如果“<>NULL”,则您无法“看到”数据是否为空。 - Didier L

-6

首先,Oracle并不总是将null和空字符串视为相同的。按照定义,空字符串是不包含任何字符的字符串。这与null完全不同。null是数据的缺失。

大约五六年前,Oracle对待空字符串与null的方式是不同的。虽然像null一样,空字符串等于所有值且不同于所有值(我认为对于null来说这是可以接受的,但对于空字符串来说完全错误),但至少length(null string)会返回0,因为空字符串是长度为零的字符串。

目前在Oracle中,length(null)返回null,我想这还可以接受,但length(null string)也返回null,这是完全错误的。

我不明白为什么他们决定开始将这两个不同的“值”视为相同。它们意味着不同的事情,程序员应该有能力以不同的方式对待它们。他们改变方法的事实告诉我,他们真的不知道如何处理这些值。


1
需要引用来区分“空字符串”和NULL值。在除Oracle之外的任何数据库中,VARCHAR字段可以有一个值(零个或多个字符)或没有值(NULL),就是这样。 - user565869
1
五六年前,即2011年左右,属于10g时期(10.1发布于2003年,10.2在2005年发布)。10g绝对没有引入任何关于处理空值的全局性变化,并且从来没有区分NULL和空值字符串,这种区分毫无意义。恐怕这个答案完全是虚构的。 - William Robertson

-6

1
无效的日期时间值?不确定这是什么意思。你在这里发布了这个问题吗? - Mark Brady
1
这个问题早在stackoverflow之前就存在了 - 我在Oracle论坛上没有得到任何有用的信息,所以我创建了一个解决方法 - 我会找到我的笔记并在这里发布。 - Cade Roux
在这里发布问题的详细信息。 - Cade Roux

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接