【问题标题】:Oracle Date MatchingOracle 日期匹配
【发布时间】:2016-09-05 10:17:59
【问题描述】:

我正在尝试在 Oracle 12c 数据库表中执行批量数据更新,以获取与日期范围匹配的记录。日期有时间信息,所以我使用trunc() 函数去除时间部分。我可以使用此条件成功执行选择查询,但不知何故,使用相同 where 子句的更新与记录不匹配。有谁知道怎么回事?

select count(*) from TABLE_NAME
where TRUNC(DATE_FIELD) in ('30-Apr-13', '31-May-13', '31-Jul-13')
-- 554

但是..

update TABLE_NAME set SOME_FIELD = 'VALUE'
where TRUNC(DATE_FIELD) in ('30-Apr-13', '31-May-13', '31-Jul-13')
-- 0 rows updated.

--编辑--

我也试过to_date('30-Apr-13'), ...没有效果。

【问题讨论】:

  • SOME_FIELD 是否有任何索引(主要是 UNIQUE)?
  • 您能否为表格和示例数据添加 DDL(以显示完整日期/时间的格式)?使用显式转换和格式掩码有什么不同吗?如果是这样,当您看到这种行为时,您的 NLS_DATE_FORMAT 是什么?
  • 我无法重现“Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production”上的行为。我认为这种行为很奇怪。在此期间是否有其他人更改数据?
  • 试试 ANSI 日期文字:UPDATE table_name SET some_field = 'VALUE' WHERE TRUNC( date_field ) IN ( DATE '2013-04-30', DATE '2013-05-31', DATE '2014-07-31' )
  • @AlexPoole 你明白了!我必须在自动批处理加载程序中运行更新,该加载程序的日期格式与我用于选择的 sql 开发人员不同。对不起,应该提到这一点。一旦我将年份组件修改为完整形式,即 2013 年、2014 年,更新就成功了。请填写答案,以便我标记它。

标签: sql oracle date


【解决方案1】:

最初的问题并不清楚,但您依赖于日期和字符串之间的隐式转换,并通过其NLS_DATE_FORMAT 设置在具有不同默认格式的会话中运行查询和更新。复制效果:

create table table_name (date_field date, some_field varchar2(5));

insert into table_name values (timestamp '2013-04-30 00:00:01', null);

alter session set nls_date_format = 'DD-MON-RR';

select count(*) from TABLE_NAME
where TRUNC(DATE_FIELD) in ('30-Apr-13', '31-May-13', '31-Jul-13');

  COUNT(*)
----------
         1

alter session set nls_date_format = 'DD-MON-YYYY';

update TABLE_NAME set SOME_FIELD = 'VALUE'
where TRUNC(DATE_FIELD) in ('30-Apr-13', '31-May-13', '31-Jul-13');

0 rows updated.

隐式转换将您截断的日期列值更改为,例如,'30-Apr-2013',这与您正在比较的文字 '30-Apr-13' 不匹配。将您添加为编辑的版本与to_date('30-Apr-13') 进行比较,将转换为日期0013-04-30,但仍与日期2013-04-30 不匹配。

您仍然可以使用字符串比较,但提供四位数的年份表示:

update TABLE_NAME set SOME_FIELD = 'VALUE'
where TRUNC(DATE_FIELD) in ('30-Apr-2013', '31-May-2013', '31-Jul-2013');

1 row updated.

通常最好使用明确的格式掩码将字符串转换为日期(四位数年份更好,但如果您准备好应对以下风险,可以使用两位数歧义):

update TABLE_NAME set SOME_FIELD = 'VALUE'
where TRUNC(DATE_FIELD) in (to_date('30-Apr-2013', 'DD-Mon-YYYY'),
  to_date('31-May-2013', 'DD-Mon-YYYY'), to_date('31-Jul-2013', 'DD-Mon-YYYY'));

1 row updated.

update TABLE_NAME set SOME_FIELD = 'VALUE'
where TRUNC(DATE_FIELD) in (to_date('30-Apr-13', 'DD-Mon-RR'),
  to_date('31-May-13', 'DD-Mon-RR'), to_date('31-Jul-13', 'DD-Mon-RR'));

1 row updated.

或者因为它们是固定日期,您可以使用较短但明确的date literals

update TABLE_NAME set SOME_FIELD = 'VALUE'
where TRUNC(DATE_FIELD) in (date '2013-04-30', date '2013-05-31', date '2013-07-31');

1 row updated.

值得指出的是,执行TRUNC(DATE_FIELD) 将阻止使用该列上的任何索引,无论您将其与什么进行比较。您可以与日期范围进行比较以避免这种情况,例如:

update TABLE_NAME set SOME_FIELD = 'VALUE'
where (DATE_FIELD >= date '2013-04-30' and DATE_FIELD < date '2013-05-01')
or (DATE_FIELD >= date '2013-05-31' and DATE_FIELD < date '2013-06-01')
or (DATE_FIELD >= date '2013-07-31' and DATE_FIELD < date '2013-08-01');

【讨论】:

    【解决方案2】:

    使用ANSI date literals 而不是依赖NLS_DATE_FORMAT parameter 将字符串转换为日期:

    UPDATE table_name
    SET some_field = 'VALUE'
    WHERE TRUNC( date_field ) IN ( DATE '2013-04-30', DATE '2013-05-31', DATE '2014-07-31' )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多