Derby 对 NULL 值的处理
Posted
技术标签:
【中文标题】Derby 对 NULL 值的处理【英文标题】:Derby's handling of NULL values 【发布时间】:2011-02-05 08:11:34 【问题描述】:我是 Derby 的新手,我注意到就null
值而言,我遇到了与使用 DB2 RDBMS 时类似的问题。 Derby 文档指出,null
值必须具有与之关联的类型(DB2 最终在 9.7 版本中删除了该类型):
http://db.apache.org/derby/docs/10.7/ref/crefsqlj21305.html
现在,我正在尝试在这里找到解决此问题的通用解决方案,因为这将成为我的数据库抽象库jOOQ 的一部分。下面的例子只是记录了这个问题。想想任何其他(更复杂的)例子。以下不起作用:
insert into T_AUTHOR (
ID, FIRST_NAME, LAST_NAME,
DATE_OF_BIRTH, YEAR_OF_BIRTH, ADDRESS)
select
1000, 'Lukas', 'Eder',
'1981-07-10', null, null
from SYSIBM.SYSDUMMY1
也不这样做(这实际上是 jOOQ 所做的):
insert into T_AUTHOR (
ID, FIRST_NAME, LAST_NAME,
DATE_OF_BIRTH, YEAR_OF_BIRTH, ADDRESS)
select ?, ?, ?, ?, ?, ?
from SYSIBM.SYSDUMMY1
因为这两个null
值没有与之关联的类型。解决方案是这样写:
insert into T_AUTHOR (
ID, FIRST_NAME, LAST_NAME,
DATE_OF_BIRTH, YEAR_OF_BIRTH, ADDRESS)
select
1000, 'Lukas', 'Eder',
'1981-07-10', cast(null as int), cast(null as varchar(500))
from SYSIBM.SYSDUMMY1
或者像这样,分别
insert into T_AUTHOR (
ID, FIRST_NAME, LAST_NAME,
DATE_OF_BIRTH, YEAR_OF_BIRTH, ADDRESS)
select
?, ?, ?, ?, cast(? as int), cast(? as varchar(500))
from SYSIBM.SYSDUMMY1
但很多时候,在 Java 中,null
应该转换为的类型是未知的:
int
),但在此示例中不起作用,因为您不能将 cast(null as int)
值放入 ADDRESS
。
使用 HSQLDB(此问题的另一个候选对象),我可以简单地编写 cast(null as object)
,这在大多数情况下都可以使用。但 Derby 没有 object
类型。
这个问题以前一直困扰着我使用 DB2,我还没有找到解决方案。有谁知道针对这些 RDBMS 中的任何一个的稳定和通用解决方案?
德比 DB2【问题讨论】:
【参考方案1】:如果您在 INSERT 上使用 VALUES 子句,则不必强制转换 NULL 值:
insert into T_AUTHOR (
ID, FIRST_NAME, LAST_NAME,
DATE_OF_BIRTH, YEAR_OF_BIRTH, ADDRESS)
VALUES (
1000, 'Lukas', 'Eder',
'1981-07-10', null, null
);
这将像您期望的那样工作(即数据库可以确定 NULL 对应于一个整数和 varchar(500)。这在 DB2 和 Derby 中都有效(并且应该也适用于几乎任何其他数据库引擎) .
您也可以将 VALUES 与参数标记一起使用,而不必强制转换它们。
发出insert into ... select from
语句时必须强制转换的原因是因为SELECT 部分具有优先权——select 语句返回某些数据类型,无论它们是否与您尝试插入的表兼容进入。如果它们不兼容,您将得到一个错误(对于像 DB2
【讨论】:
谢谢伊恩。改写 SQL 不是一种选择,因为它只是一个描述性示例。我将不得不期待任何形式的 any 类型的 SQL。但是很好地暗示了SELECT
在INSERT .. SELECT
中的优先级。实际上,我的库中有许多可以顺利运行的集成测试,只是有些失败。我希望找到一个通用的解决方案来解决这个问题......但也许我会把它作为一个未解决的问题,希望 Derby 纠正它,因为 INSERT .. SELECT
语法不是很常见.【参考方案2】:
DB2 使用空指示符...例如
EXEC SQL INSERT INTO TABNAM (FILD1, FILD2)
VALUES (:HOSTVAR1, :HOSTVAR2:NULL-IND2) END-EXEC.
注意 NULL-IND2 字段可以设置为 -1 表示数据库表中的该字段应为空。
对于 JDBC 或 Derby 是否有类似的指标?
【讨论】:
我不知道有这样的指标,很高兴知道。我得分析一下。据我所知,JDCB 中没有这样的指标。特别是,命名参数在 JDBC 中也不可用。但是有一个选项可以在setNull() 方法中显式设置NULL
以及JDBC 类型。也不确定德比战【参考方案3】:
如果您将列值作为问号替换值保留在 PreparedStatement 中,然后在运行时使用 PreparedStatement.setObject(N, null) 设置这些值会怎样?
一般来说,您的库更喜欢通过替换参数值来提供列值,以处理字符串中的引号、数据类型转换等问题,我认为这种通用替换机制也应该处理您的空值问题.
【讨论】:
感谢布莱恩的输入。我不知道为什么我离开了绑定变量。当然,我的库将始终使用绑定变量来提高性能/安全性等,而不是将值直接内联到 SQL。我会调整这个问题。 不用担心。您可能遇到issues.apache.org/jira/browse/DERBY-1938,它最近才修复。您使用的是最新的 Derby (10.7) 吗?如果没有,试试看 setObject(null) 现在是否工作得更好。 是的,我使用的是 Derby 10.7.1.1。但实际上这是setObject(null)
的一个很好的提示。我有两个错误的组合,首先Connection.prepareStatement()
调用不接受null
文字或cast(? as integer)
表达式(如果整数是错误的类型)。当我简单地为空值准备?
时,我的库调用PreparedStatement.setNull(N, Object.class)
,它也不起作用。但是setObject(N, null)
似乎有效。我会仔细检查...
嗯,这在这种情况下有效,但不是通用解决方案。如果我只是SELECT ? FROM SYSIBM.SYSDUMMY1
,那么德比会抱怨吗?在 select 子句中是不允许的。这确实是一个 SQL 方言抽象框架要解决的难题……【参考方案4】:
也许这个解决方案可以帮助你:
insert into T_AUTHOR (
ID,
FIRST_NAME,
LAST_NAME,
DATE_OF_BIRTH,
YEAR_OF_BIRTH,
ADDRESS
) select
1000,
'Lukas',
'Eder',
'1981-07-10',
(select YEAR_OF_BIRTH from T_AUTHOR where true = false),
(select ADDRESS from T_AUTHOR where true = false)
from SYSIBM.SYSDUMMY1
此方法不需要显式转换 null
,因为内部 select
将返回具有所需类型的 null。
【讨论】:
以上是关于Derby 对 NULL 值的处理的主要内容,如果未能解决你的问题,请参考以下文章