在这里,我们的许多开发人员正在使用Oracle分层子查询来构建日期列表,例如:
WITH mydays AS (
SELECT CAST( TO_DATE('20161201', 'YYYYMMDD') + ROWNUM AS DATE) AS d
FROM DUAL CONNECT BY TO_DATE('20161201', 'YYYYMMDD') + ROWNUM <= to_date('20161215', 'YYYYMMDD')
) 对我来说它看起来像是演员。AS DATE应该是无用的,因为我们要将一个日期转换为一个日期,对吧?实际上并非如此。如果我像这样创建并填充一个表(我的演示需要一个连接...):
create table t(d date, i number);
insert into t(d, i) values (to_date('20161204', 'YYYYMMDD'), 1);
commit;在没有强制转换的情况下,尝试使用这样的连接进行查询,我得到了以下解释计划:
WITH mydays AS (
SELECT TO_DATE('20161201', 'YYYYMMDD') + ROWNUM AS d
FROM DUAL CONNECT BY TO_DATE('20161215', 'YYYYMMDD') + ROWNUM <= to_date('20161201', 'YYYYMMDD')
)
select mydays.d from mydays join t on t.d = mydays.d;。
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 15 | 6 (17)| 00:00:01 |
|* 1 | HASH JOIN | | 1 | 15 | 6 (17)| 00:00:01 |
| 2 | VIEW | | 1 | 6 | 2 (0)| 00:00:01 |
| 3 | COUNT | | | | | |
|* 4 | CONNECT BY WITHOUT FILTERING| | | | | |
| 5 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 6 | TABLE ACCESS FULL | T | 1 | 9 | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("T"."D"=INTERNAL_FUNCTION("MYDAYS"."D"))
4 - filter(TO_DATE(' 2016-12-15 00:00:00', 'syyyy-mm-dd
hh24:mi:ss')+ROWNUM<=TO_DATE(' 2016-12-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))使用强制转换时,INTERNAL_FUNCTION()就消失了:
4 - filter(TO_DATE(' 2016-12-01 00:00:00', 'syyyy-mm-dd
hh24:mi:ss')+ROWNUM<=TO_DATE(' 2016-12-15 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))我用谷歌搜索了一下,发现转换应该在两种内部日期格式之间进行,这两种格式由id为12和13的Oracle内部处理,因为我们可以找到对DUMP()的调用。没有演员阵容:
Typ=13 Len=8: 7,224,12,4,0,0,0,0对于演员阵容:
Typ=12 Len=7: 120,116,12,4,1,1,1这解释了使用强制转换比不使用(!)的转换更少。
但当我使用绑定变量时,情况会变得更糟。如果我想要得到这个SQL的解释计划:
WITH mydays AS (
SELECT TO_DATE(:beginning, 'YYYYMMDD') + ROWNUM AS d
FROM DUAL CONNECT BY TO_DATE(:end, 'YYYYMMDD') + ROWNUM <= to_date(:beginning, 'YYYYMMDD')
)
select mydays.d from mydays join t on t.d = mydays.d我得到一个错误: ORA-00932:不一致的数据类型:预期的日期得到了数字。如果我添加了施法魔法,这个错误就被修复了。我知道当我生成一个计划时,所有的绑定变量都被认为是VARCHAR2。但是对于TO_DATE(),我们应该只处理DATE,对吧?这个数字是从哪里来的?
如果你能给我任何解释,谢谢你,因为我的大脑开始被那些奇怪的东西弄得乱七八糟。
发布于 2016-12-16 21:24:20
非常有趣的查询获取日历。更易于阅读:
SELECT to_date('20160101','yyyymmdd') + LEVEL
FROM DUAL
CONNECT BY LEVEL <= 90;您是在寻找解决方案,还是只是在调查当地的问题?
https://stackoverflow.com/questions/41184593
复制相似问题