我们的许多开发人员都在使用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')
)
对我而言,它看起来像CAST ...... AS DATE应该没用,因为我们正在将DATE转换为DATE,对吧?实际上并非如此。如果我创建并填充这样的表(我需要为我的演示加入...):
create table t(d date, i number);
insert into t(d, i) values (to_date('20161204', 'YYYYMMDD'), 1);
commit;
并尝试使用类似的连接查询,没有CAST,我得到以下解释计划:
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'))
使用CAST,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'))
我google了一下,发现转换应该在两个内部日期格式之间,由内部由ID为12和13的Oracle处理,因为我们可以找到对DUMP()的调用。没有CAST:
Typ=13 Len=8: 7,224,12,4,0,0,0,0
使用CAST:
Typ=12 Len=7: 120,116,12,4,1,1,1
这说明我们使用CAST的转化率低于没有(!!!)。
但是当我把绑定变量放在游戏中时它会变得更糟。如果我想获得这个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:数据类型不一致:预期DATE得到NUMBER。如果我添加CAST魔法,则会修复错误。我知道当我生成一个计划时,所有绑定变量都被认为是VARCHAR2。但是使用TO_DATE(),我们应该只使用DATE,对吧?这个号码来自哪里?
谢谢,如果你可以给我任何解释,因为我的大脑开始变得非常混乱所有奇怪的东西。
答案 0 :(得分:0)
获得日历的非常感兴趣的查询。更容易阅读:
SELECT to_date('20160101','yyyymmdd') + LEVEL
FROM DUAL
CONNECT BY LEVEL <= 90;
您正在寻找解决方案或只是调查本地问题?