我被卡住了。
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';
所有这些似乎都是有效的......
答案 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中阅读更多相关信息。