Oracle内部日期转换:ORA-00932和奇怪的东西

时间:2016-12-16 12:37:03

标签: sql oracle date casting oracle11gr2

我们的许多开发人员都在使用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,对吧?这个号码来自哪里?

谢谢,如果你可以给我任何解释,因为我的大脑开始变得非常混乱所有奇怪的东西。

1 个答案:

答案 0 :(得分:0)

获得日历的非常感兴趣的查询。更容易阅读:

SELECT to_date('20160101','yyyymmdd') + LEVEL 
  FROM DUAL 
CONNECT BY LEVEL <= 90;

您正在寻找解决方案或只是调查本地问题?