甲骨文CAST COLLECT用于日期数据类型

时间:2018-11-09 13:48:23

标签: oracle

考虑类型:

CREATE OR REPLACE   TYPE date_array AS  TABLE OF DATE; 
CREATE OR REPLACE   TYPE number_array AS  TABLE OF NUMBER;
CREATE OR REPLACE   TYPE char_array AS  TABLE OF VARCHAR2(80);

查询:

WITH q AS
 (SELECT LEVEL ID,
         TRUNC(SYSDATE) + LEVEL MyDate,
         to_char(LEVEL) STRING
  FROM   dual
  CONNECT BY LEVEL < 5)
SELECT CAST(COLLECT(ID) AS number_array)
FROM   q;

返回数字集合

WITH q AS
 (SELECT LEVEL ID,
         TRUNC(SYSDATE) + LEVEL MyDate,
         to_char(LEVEL) STRING
  FROM   dual
  CONNECT BY LEVEL < 5)
SELECT CAST(COLLECT(STRING) AS char_array)
FROM   q;

返回字符串集合

WITH q AS
 (SELECT LEVEL ID,
         TRUNC(SYSDATE) + LEVEL MyDate,
         to_char(LEVEL) STRING
  FROM   dual
  CONNECT BY LEVEL < 5)
SELECT CAST(COLLECT(MyDate) AS date_array)
FROM   q

返回错误invalid datatype

任何人都可以解释为什么Date数据类型的行为不同吗?

1 个答案:

答案 0 :(得分:3)

这是我的发现...看来您遇到了一个错误,原因是计算的日期似乎具有与“数据库”日期不同的内部表示形式。我确实找到了解决方法,所以请继续阅读。

在我的oracle dev安装(Oracle 11g企业版11.2.0.4.0版-64位生产)上,我遇到了同样的问题。

但是...如果我创建一个包含您的测试数据的 physical 表:

create table test_data as 
SELECT LEVEL ID,
       TRUNC(SYSDATE) + LEVEL MyDate,
       to_char(LEVEL) STRING
FROM   dual
CONNECT BY LEVEL < 5

然后在此物理表上运行“ cast collect”运算符,它可以按预期工作:

-- this one works perfectly
SELECT CAST(COLLECT(MyDate) AS date_array) from test_data

但所有这些示例仍然无效:

-- here I just added 1 .. and it doesn't work
SELECT CAST(COLLECT(MyDate + 1) AS date_array) 
from test_data

-- here I am extracting sysdate, instead of a physical column... and it doesn't work
SELECT CAST(COLLECT(sysdate) AS date_array) 
from test_data

好像oracle认为计算的日期与物理日期

因此,我尝试使用明确的强制转换和EUREKA来“说服” oracle我提供的数据实际上是正常的DATE值!这可以正确地完成工作:

  WITH q AS
  (SELECT LEVEL ID,                                     
          -- this apparently unnecessary cast does the trick
          CAST( TRUNC(SYSDATE) + LEVEL AS DATE) MyDate, 
          to_char(LEVEL) STRING
   FROM   dual
   CONNECT BY LEVEL < 5)
 SELECT CAST(COLLECT(MyDate) AS date_array)
 FROM   q

是的...但是为什么?

看来,即使我们看到的值实际上是相同的,这两个值也不完全相同:

 select sysdate, cast (sysdate as date) from dual

所以我挖掘了两个值的内部表示形式,将“ dump”函数应用于两个值:

 select dump(sysdate), dump(cast (sysdate as date)) from dual

这些是我得到的结果:

DUMP(SYSDATE                    )  -> Typ=13 Len=8: 226,7,11,9,19,20,47,0   
DUMP(CAST(SYSDATEASDATE) as DUAL)  -> Typ=12 Len=7: 120,118,11,9,20,21,48

在内部,它们看起来像两种完全不同的数据类型!一个是12型,另一个是13型...它们的长度和表示形式不同。

无论如何,我发现了更多……似乎其他人已经注意到了这一点:https://community.oracle.com/thread/4122627

该问题的答案指向此文档:http://psoug.org/reference/datatypes.html

其中包含有关日期的冗长注释,摘录如下:

  

“发生了什么事?上面的信息不正确还是DUMP()   函数不能处理DATE值?不,您必须查看“ Typ =“   值以了解我们为什么看到这些结果。 “。数据类型   返回的是13,而不是12,这是外部DATE数据类型。发生这种情况   因为我们依赖TO_DATE函数!外部数据类型13是   内部c结构的长度取决于   c编译器表示结构。请注意,“ Len =”值为8   而不是7。Type13不是已发布的3GL接口的一部分   Oracle,主要用于PL / SQL中的日期计算   操作。请注意,将   值SYSDATE。”

无论如何,我重复:我认为这是一个错误,但至少我找到了一种解决方法:对DATE使用显式强制转换。