Java / JPA程序员遇到的Oracle将空字符串视为NULL问题

15

当 Oracle 将空字符串存储为 NULL 值时,如何处理这种情况?

我希望它被存储为一个空字符串而不是 NULL 值,因为查询会更容易。

类似于以下代码可以选择空字符串和非空字符串,但不包括 NULL 值:

select * from mytable where myfield like '%';

如果我想选择空值(原本应该是空字符串),我需要这样选择:

select * from mytable where myfield like '%' or myfield is null;

我希望能够在SQL语句中避免反复使用or myfield is null的操作。

目前我想到的解决方案是在应用程序层面处理,例如,在实体中,我将所有字符串字段的默认值初始化为空格,例如:

@Entity
public class MyEntity {
  private String name = " ";

  public void setName(String name) {
    if (isEmptyString(name)) {
      name = " ";
    }
  }
  ...

}

或者,我可以使用Oracle 11g中我仍不知道的新类型,它可以将空字符串保持为其原样而不将其更改为null值?

谢谢!


我想你可以使用@PrePersist注解,检查名称字段的大小,如果为null,则用“”替换它。 - Erik
我通过不使用根本有缺陷的DBMS来解决那个特定的问题。换句话说,我使用DB2 :-) - paxdiablo
一些JPA实现可能通过插入一个特殊字符来表示空字符串来满足这一需求。这就是DataNucleus为您提供的服务。 - DataNucleus
1
从官方的Oracle文档中得知:目前Oracle数据库将长度为零的字符值视为空值。然而,在未来的版本中,这可能不再是真实情况,Oracle建议您不要将空字符串与null视为相同。这意味着NULL和""始终被认为是相等的吗?但是,您(和我)观察到""只是存储为NULL,因此与""的比较可能会产生不同的结果,对吗? - RobertG
6个回答

19

没错,这就是 Oracle 的工作方式。空字符串被视为 null。

当然,你可以在应用程序层面“修复”它 - 例如通过存储像你建议的" "值,但首先请考虑一下,你的“空字符串”值与 NULL 值相比有何区别?为什么你需要不同对待它们?我以前也遇到过这个问题,但通常发现我真正需要区分它们的情况非常少。


在应用程序级别上,这对我来说不是问题。但在 SQL 级别上,当尝试获取数据时,语句将充满检查空值的操作,因为就像我上面的例子一样,如果程序员忘记添加 is null 的检查,则数据将无法检索。如果 Oracle 将其保留为空字符串,则这是不必要的。 - Bertie
但是,如果您的列可为空,那不就是您必须接受的吗?如果您想获取myfield ='X'的行,并且还包括myfield为空的行,您只需要添加or myfield is null。这是无法避免的,但我真的看不出有什么问题。 - Tommi
假设在PostgreSQL中,myfield字段包含'hello'和'',执行带有where myfield like '%'的查询将显示两者。但是在Oracle中,当myfield包含'hello'和''时,执行带有where myfield like '%'的查询只会显示一个。而对于我们来说,''实际上非常重要,因为它被视为一个值。 - Bertie
1
如果您确实需要将''值与“真正的”NULL值分开,恐怕您必须自己实现解决方案(将空字符串转换为" ",正如您自己所说,是一种流行的选择)。对此我感到很抱歉。但我仍然强烈建议您考虑是否真的有必要这样做。 - Tommi
2
检索语言对它们进行了显着不同的处理。NULL字符串对象(未实例化的对象,即NULL)与空字符串对象(已实例化,值为'')不同。操作该对象的代码通常期望它被实例化;如果在意外未实例化的对象(NULL)上执行操作,则该代码将抛出空指针异常。例如,发布到视图的数据可能会以不同于空字符串的方式处理NULL。由非Oracle数据库(MSSQL)支持的应用程序代码需要在转移到Oracle时添加额外的空值验证。 - JoshDM

3

不,没有办法将空字符串视为空字符串。Oracle始终将长度为零的字符串视为NULL值。


3

这不仅涉及到特定条件的选择,还涉及到对Java字符串对象的处理。 如果您有一个字符串a="",则可以调用其length方法并获得0。 但如果您有一个字符串a=null,则在调用length时会出现nullpointer异常。 因此,在使用Oracle数据库时,必须始终检查字符串是否为null,然后再检查长度 :(


2

尝试

create index idx_myfield on mytable(nvl(myfield,-1));

select * from mytable where nvl(myfield,-1)=-1;

2

虽然对我来说还有点早,但是这句话的意思是:

select * from mytable where myfield like '%' or myfield is null

相同

select * from mytable

所以,Oracle让你的生活更简单!;)

抱歉这个例子太简单了,但是请想象一下这种情况:select * from mytable where myfield1 like '%' and myfield2 = 'xxx'; 任何值不为 null 的 myfield1 都会显示出来。然而如果 Oracle 能够存储空字符串,那么这些值也会被匹配并显示出来。也许问题是为什么我使用 like '%' 而不是直接跳过它。因为有时筛选值可能基于用户输入是动态的,如果为空,我通常会用 % 替换它,这就是 like '%' 的作用。如果用户输入 'abc',那么它就变成了 **like '%abc%'**。 - Bertie
@Albert,我还是有点困惑,请原谅我的笨。什么情况下需要使用 => 和 myfield1 like '%' <=?如果你的意思是当myfield1为null或者不为null时,那么这样做没有意义,因为所有行都将是null或者not null。此外,如果你格式化查询,使得你有 => where myfield1 like '%abc%' <=,你也不会使用索引,这就是为什么你不应该这样做(在我看来)。但也许我还是有所遗漏... - tbone

0
相信Oracle正在通过将空字符串转换为NULL来优化数据库。 首先,空字符串仍然需要在数据块级别显式存储在数据库存储中(*1)。将其存储为NULL可以减少数据存储占用。 其次,如果在列上定义了任何索引,则不包括NULL值在内,从而减少索引存储占用。 (*2)
从设计和开发的角度来看,除非空格真正改变了业务层面的数据语义,否则将其存储为NULL应该是可以的。
如上所建议,在框架级别(或父类)添加代码将消除在所有子类和对象中输入它的需要 - 这就是面向对象甚至基本编程所追求的。
如果我的代码表现最佳,因为我的数据库存储已经优化,那么我应该很高兴。 如果我的代码在生产中表现不佳,只是因为我想节省一点打字或未能进行精湛的设计/开发,那就太糟糕了?
我会很高兴Oracle正在为我在幕后进行优化。
关于Oracle NULL的全部内容:
NULL是一个特殊值。 NULL不等于任何东西,包括它本身,即NULL不等于NULL。

“除非空格确实改变了数据的语义”:确切地说,它确实会!虽然在几乎所有应用程序中,这种差异对于少数字段非常重要。这不是没有原因,大多数语言都存在这种差异。数据库不应阻止这种区分。如果Oracle的“优化”是一个选项,那就太好了,但它并没有提供选择。即使Oracle也知道他们错了,正如@RobertG指出的那样 https://docs.oracle.com/cd/B28359_01/server.111/b28286/sql_elements005.htm#SQLRF51081:“在未来的版本中,这可能不再成立”。 - user1075613

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