Liquibase 脚本 returns ORA-01843: 不是有效月份
Liquibase script returns ORA-01843: not a valid month
我在使用以下 liquibase 脚本时遇到问题:
<sql>
MERGE INTO A config
USING (SELECT 100 as id, '02.01.15 12:00:00' as CHANGED, 0 as DELETED, 1 as B FROM DUAL) src ON (src.id = config.id)
WHEN NOT MATCHED THEN
INSERT(id,CHANGED, DELETED, B) VALUES(src.id, src.CHANGED, src.DELETED, src.B)
WHEN MATCHED THEN
UPDATE SET config.B = src.B;
</sql>
当我在 sql 标签和数据库下的 运行 之间插入原始代码时(在 SQL Developer 中),结果是:1 行合并。
当我 运行 通过 liquibase 我得到错误 ORA-01843: 不是有效的月份.
怎么可能?
使用这个脚本:
<sql>
MERGE INTO A config
USING (SELECT 100 as id, TO_DATE('2015/02/01 12:00:00','YYYY/MM/DD HH24:MI:SS') as CHANGED, 0 as DELETED, 1 as B FROM DUAL) src ON (src.id = config.id)
WHEN NOT MATCHED THEN
INSERT(id,CHANGED, DELETED, B) VALUES(src.id, src.CHANGED, src.DELETED, src.B)
WHEN MATCHED THEN
UPDATE SET config.B = src.B;
</sql>
'02.01.15 12:00:00'
不是日期,它是一个字符串;如果您尝试将其插入到 DATE
数据类型列中,那么 Oracle 将尝试使用等效于:
将其转换为日期
SELECT TO_DATE(
'02.01.15 12:00:00',
( SELECT value FROM NLS_SESSION_PARAMETERS WHERE parameter = 'NLS_DATE_FORMAT' )
) as CHANGED
FROM DUAL
因此,如果您的 NLS_DATE_FORMAT
会话参数与您的字符串 '02.01.15 12:00:00'
的格式不匹配,那么它将引发异常 - 这就是您获得 ORA-01843: not a valid month
.
最好的解决方案是修改脚本以将字符串显式转换为日期:
MERGE INTO A config
USING (
SELECT 100 as id,
TO_DATE( '02.01.15 12:00:00', 'DD.MM.YY HH24:MI:SS' ) as CHANGED,
0 as DELETED,
1 as B
FROM DUAL
) src ON (src.id = config.id)
WHEN NOT MATCHED THEN
INSERT(id,CHANGED, DELETED, B) VALUES(src.id, src.CHANGED, src.DELETED, src.B)
WHEN MATCHED THEN
UPDATE SET config.B = src.B;
或使用时间戳文字:TIMESTAMP '2015-01-02 12:00:00'
但您也可以创建一个登录触发器来更改 NLS_DATE_FORMAT
会话参数。围绕此代码包装触发器:
ALTER SESSION SET NLS_DATE_FORMAT = 'DD.MM.YY HH24:MI:SS';
但是,这会将所有隐式转换中使用的日期格式从字符串更改为日期(反之亦然),因此可能会破坏同样依赖此类隐式转换的其他查询。此外,每个用户都可以随时更改他们的会话参数,因此在登录时设置此默认值依赖于他们在会话期间永远不会更改它。
[TL;DR] 修复您的脚本以不使用数据类型之间的隐式转换,而不是修改用于隐式转换的格式模型。
我在使用以下 liquibase 脚本时遇到问题:
<sql>
MERGE INTO A config
USING (SELECT 100 as id, '02.01.15 12:00:00' as CHANGED, 0 as DELETED, 1 as B FROM DUAL) src ON (src.id = config.id)
WHEN NOT MATCHED THEN
INSERT(id,CHANGED, DELETED, B) VALUES(src.id, src.CHANGED, src.DELETED, src.B)
WHEN MATCHED THEN
UPDATE SET config.B = src.B;
</sql>
当我在 sql 标签和数据库下的 运行 之间插入原始代码时(在 SQL Developer 中),结果是:1 行合并。
当我 运行 通过 liquibase 我得到错误 ORA-01843: 不是有效的月份.
怎么可能?
使用这个脚本:
<sql>
MERGE INTO A config
USING (SELECT 100 as id, TO_DATE('2015/02/01 12:00:00','YYYY/MM/DD HH24:MI:SS') as CHANGED, 0 as DELETED, 1 as B FROM DUAL) src ON (src.id = config.id)
WHEN NOT MATCHED THEN
INSERT(id,CHANGED, DELETED, B) VALUES(src.id, src.CHANGED, src.DELETED, src.B)
WHEN MATCHED THEN
UPDATE SET config.B = src.B;
</sql>
'02.01.15 12:00:00'
不是日期,它是一个字符串;如果您尝试将其插入到 DATE
数据类型列中,那么 Oracle 将尝试使用等效于:
SELECT TO_DATE(
'02.01.15 12:00:00',
( SELECT value FROM NLS_SESSION_PARAMETERS WHERE parameter = 'NLS_DATE_FORMAT' )
) as CHANGED
FROM DUAL
因此,如果您的 NLS_DATE_FORMAT
会话参数与您的字符串 '02.01.15 12:00:00'
的格式不匹配,那么它将引发异常 - 这就是您获得 ORA-01843: not a valid month
.
最好的解决方案是修改脚本以将字符串显式转换为日期:
MERGE INTO A config
USING (
SELECT 100 as id,
TO_DATE( '02.01.15 12:00:00', 'DD.MM.YY HH24:MI:SS' ) as CHANGED,
0 as DELETED,
1 as B
FROM DUAL
) src ON (src.id = config.id)
WHEN NOT MATCHED THEN
INSERT(id,CHANGED, DELETED, B) VALUES(src.id, src.CHANGED, src.DELETED, src.B)
WHEN MATCHED THEN
UPDATE SET config.B = src.B;
或使用时间戳文字:TIMESTAMP '2015-01-02 12:00:00'
但您也可以创建一个登录触发器来更改 NLS_DATE_FORMAT
会话参数。围绕此代码包装触发器:
ALTER SESSION SET NLS_DATE_FORMAT = 'DD.MM.YY HH24:MI:SS';
但是,这会将所有隐式转换中使用的日期格式从字符串更改为日期(反之亦然),因此可能会破坏同样依赖此类隐式转换的其他查询。此外,每个用户都可以随时更改他们的会话参数,因此在登录时设置此默认值依赖于他们在会话期间永远不会更改它。
[TL;DR] 修复您的脚本以不使用数据类型之间的隐式转换,而不是修改用于隐式转换的格式模型。