奇怪的Oracle时间戳行为

时间:2015-07-29 15:46:08

标签: sql oracle timestamp oracle12c timestamp-with-timezone

我被卡住了。

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

我有一个包含主键和索引的简单表。 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 :(完整)年份必须介于-4713和+9999之间,而不是0       01841. 00000 - &#34;(完整)年份必须介于-4713和+9999之间,而不是0&#34;       *原因:非法年份进入       *行动:在指定范围内输入年份

但是这个工作得很好:

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

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

现在更多调试......

SELECT DBTIMEZONE from dual;

返回+00:00

SELECT SESSIONTIMEZONE FROM dual;

返回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';

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

1 个答案:

答案 0 :(得分:1)

在查找过滤日期适合的分区时,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使用audit_from_ts,如果您仅 使用该高过滤器并不重要(尽管它是&#39} ;无论如何都有点多余);但如果您从较低的值搜索,可能会产生更大的影响。

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

  

例如,如果创建具有月间隔的间隔分区表且转换点位于2010年1月1日,则2010年1月间隔的下限为2010年1月1日。2010年7月间隔的下限是2010年7月1日,无论之前是否创建了2010年6月的分区。但请注意,使用分区的上限或下限超出为存储设置的范围的日期会导致错误。例如,==> phoenix_ecto Compiled lib/phoenix_ecto.ex Compiled lib/phoenix_ecto/plug.ex Compiled lib/phoenix_ecto/ison.ex ==> Compilation error on file lib/phoenix_ecto/html.ex == ** (CompileError) ib/phoenix_ecto/html.ex:7: unknown key :impl for struct Phoenix.HTML.Form (elixir) src/e ixir_map.erl:175: :elixir_map.--assert_struct_keys/5-1c$"011-0--/5 (elixir) src/e ixir_map.erl:48: :elixir_map.translate_struct/4 (elixir) src/e ixir_clauses.erl:36: :elixir_clauses.clause/7 (elixir) src/e ixir_def.erl:178: :elixir_def.translate_clause/7 (elixir) src/e ixir_def.erl:167: :elixir_def.translate_definition/8 [31m[1mcould not compile dependency phoenix_ecto, mix compile failed. You can recompile this dependency with 'mix deps.compile phoenix_ecto' or update it with 'mix deps.update phoenix_ecto' 导致上限为10000-01-01,如果10000超出合法范围,则不能存储。

因此,如果您在该月没有价值,那么无论您使用的是9999-12-31,9999-12-15,还是9999-12-01,它都不会产生任何逻辑差异。为您的过滤器。 (您可以通过将过渡日期设置为月份的第18个来使查询与9999-12-31一起使用,但这有点奇怪,并且您仍然无法在9999-12-17之后插入记录)

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