奇怪的 Oracle 时间戳行为

Strange Oracle Timestamp Behaviour

我卡住了。

Oracle 在处理时间戳时表现出奇怪的行为,让我解释一下:

我有一个带有主键和索引的简单 table。 AUDIT_FROM_TS 是主键的一部分。它按月间隔使用 AUDIT_FROM_TS 进行分区。

相关 DDL

CREATE TABLE "SDR"."TRADE_DEAL_F"(
...
"AUDIT_FROM_TS" TIMESTAMP (9) DEFAULT SYS_EXTRACT_UTC(SYSTIMESTAMP) NOT NULL ENABLE,
...
CONSTRAINT "PK_TRADE_DEAL" PRIMARY KEY ("TRADE_DEAL_ID", "VALID_FROM_DT", "AUDIT_FROM_TS")
...
PARTITION BY RANGE ("AUDIT_FROM_TS") INTERVAL (NUMTOYMINTERVAL(1,'MONTH')) 
...

当运行这个查询时:

select count(*) from trade_deal_f where AUDIT_FROM_TS < timestamp '9999-12-31 00:00:00';

我明白了

ORA-01841: (full) year must be between -4713 and +9999, and not be 0 01841. 00000 - "(full) year must be between -4713 and +9999, and not be 0" *Cause: Illegal year entered *Action: Input year in the specified range

但是这个效果很好:

select count(*) from trade_deal_f where AUDIT_FROM_TS < timestamp '9999-12-15 00:00:00';

我做了一些调试,如果将日期增加到 9999 年 12 月 16 日,也会抛出同样的错误。

现在更多调试...

SELECT DBTIMEZONE from dual;

returns +00:00

SELECT SESSIONTIMEZONE FROM dual;

returns Europe/London

有人可以帮忙吗?我不是 100% 确定这是时区问题,因为它会使日期偏移 2 周...

select count(*) from trade_deal_f where AUDIT_FROM_TS = timestamp '9999-12-31 00:00:00 Europe/London';
select count(*) from trade_deal_f where AUDIT_FROM_TS = timestamp '9999-12-31 00:00:00 GMT';
select count(*) from trade_deal_f where AUDIT_FROM_TS = timestamp '9999-12-31 00:00:00 UTC';
select count(*) from trade_deal_f where AUDIT_FROM_TS = timestamp '9999-12-31 00:00:00 +00:00';

所有这些似乎都是有效的...

Oracle 在查找您的筛选日期适合的分区时似乎使用了日期舍入,并且 12 月 16 日之后的高价值有效性检查被舍入了过去的 10000 年。

当您通过添加时区组件更改过滤器的数据类型时,查询会起作用,因为您强制对列值进行转换,这会阻止使用分区范围;指定 GMT 会更改计划:

----------------------------------------------------------------------------------------------------------                                                                                              
| Id  | Operation                 | Name         | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |                                                                                              
----------------------------------------------------------------------------------------------------------                                                                                              
|   0 | SELECT STATEMENT          |              |     1 |    13 |   120   (2)| 00:00:02 |       |       |                                                                                              
|   1 |  SORT AGGREGATE           |              |     1 |    13 |            |          |       |       |                                                                                              
|   2 |   PARTITION RANGE ITERATOR|              |   112K|  1430K|   120   (2)| 00:00:02 |     1 |     3 |                                                                                              
|*  3 |    TABLE ACCESS FULL      | TRADE_DEAL_F |   112K|  1430K|   120   (2)| 00:00:02 |     1 |     3 |                                                                                              
----------------------------------------------------------------------------------------------------------                                                                                              


PLAN_TABLE_OUTPUT                                                                                                                                                                                      
------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):                                                                                                                                                     
---------------------------------------------------                                                                                                                                                     

   3 - filter("AUDIT_FROM_TS"<TIMESTAMP' 9999-12-15 00:00:00.000000000')                                                                                                                                

-----------------------------------------------------------------------------------------------------                                                                                                   
| Id  | Operation            | Name         | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |                                                                                                   
-----------------------------------------------------------------------------------------------------                                                                                                   
|   0 | SELECT STATEMENT     |              |     1 |    13 |   124   (5)| 00:00:02 |       |       |                                                                                                   
|   1 |  SORT AGGREGATE      |              |     1 |    13 |            |          |       |       |                                                                                                   
|   2 |   PARTITION RANGE ALL|              |   112K|  1430K|   124   (5)| 00:00:02 |     1 |1048575|                                                                                                   
|*  3 |    TABLE ACCESS FULL | TRADE_DEAL_F |   112K|  1430K|   124   (5)| 00:00:02 |     1 |1048575|                                                                                                   
-----------------------------------------------------------------------------------------------------                                                                                                   


PLAN_TABLE_OUTPUT                                                                                                                                                                                      
------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):                                                                                                                                                     
---------------------------------------------------                                                                                                                                                     

   3 - filter(SYS_EXTRACT_UTC(INTERNAL_FUNCTION("AUDIT_FROM_TS"))<TIMESTAMP' 9999-12-15                                                                                                                 
              00:00:00.000000000')                                                                                                                                                                      

隐式 SYS_EXTRACT_UTC 导致它使用 PARTITION RANGE ALL,如果您 使用该高过滤器(尽管它是无论如何有点多余);但如果您也从低价值搜索,可能会产生更大的影响。

但是,如果您将转换日期作为一个月的第一天进行间隔分区(这里似乎就是这种情况),则您无论如何都不能在 9999 年 12 月插入任何具有 audit_from_ts 值的记录,因为将需要一个具有高值 10000-01-01 的分区,这不是合法日期。这是提到in the documentation:

For example, if you create an interval partitioned table with monthly intervals and the transition point is at January 1, 2010, then the lower boundary for the January 2010 interval is January 1, 2010. The lower boundary for the July 2010 interval is July 1, 2010, regardless of whether the June 2010 partition was previously created. Note, however, that using a date where the high or low bound of the partition would be out of the range set for storage causes an error. For example, TO_DATE('9999-12-01', 'YYYY-MM-DD') causes the high bound to be 10000-01-01, which would not be storable if 10000 is out of the legal range.

因此,如果您在那个月没有值,那么无论您使用 9999-12-31、9999-12-15 还是 9999-12-01 作为您的筛选。 (您可以通过将转换日期设为该月的 18 日来使查询与 9999-12-31 一起工作,但这会有点奇怪,并且您仍然无法在 9999-12-17 之后插入记录)。

Oracle 不认为这是一个错误。您可以在 My Oracle Support 文档 1507993.1 中阅读更多相关信息。