繁体   English   中英

Oracle SQL WITH子句和EXTRACT函数

[英]Oracle sql WITH clause and EXTRACT function

我试图了解下面两个查询之间的区别。 第二个引发语法错误。 有人可以帮忙吗?

1。

select 
  extract(year from (sysdate - to_date('2002-12-25')) year to month) Y 
from dual;

2。

with age(d) as
(
  select (sysdate - to_date('2002-12-25')) from dual
)
select 
  extract(year from (age.d) year to month) Y 
from age;

错误:ORA-30083:在间隔值表达式30083中发现语法错误。00000-“在间隔值表达式中发现语法错误” *原因:在分析间隔值时发现语法错误

在第二个查询中,有NUMBER个值sysdate - to_date('2002-12-25') 您不能从数字中EXTRACT年份。

在第一个查询中,您将数字转换回日期间隔数据类型之一。

-- datatype: internal NUMBER of a DATE subtraction
SELECT dump(sysdate - to_date('2002-12-25', 'YYYY-MM-DD'))
  FROM dual;

-- datatype: INTERVAL YEAR TO MONTH 
SELECT dump((sysdate - to_date('2002-12-25', 'YYYY-MM-DD')) year to month)
  FROM dual;  

-- datatype: NUMBER
SELECT dump(extract(year from (sysdate - to_date('2002-12-25', 'YYYY-MM-DD')) year to month))
  FROM dual;

DUMP返回一个VARCHAR2值,其中包含数据类型代码,字节长度和expr的内部表示形式。

您的问题可以简化为以下原因:

select (sysdate - date '2002-12-25') year to month from dual;

(SYSDA
------
+14-03

但不会将sysdate - date '2002-12-25'产生的相同数字插入相同的调用中,无论是否带有括号,都可以使用各种组合:

select (5217.4197) year to month from dual;

ORA-30083: syntax error was found in interval value expression

问题在于这两个数字实际上在内部是不同的。 如@hinoff所建议,您可以使用dump()函数来检查它们:

select dump(sysdate - date '2002-12-25')
from dual;

DUMP(SYSDATE-DATE'2002-12-25')
-----------------------------------
Typ=14 Len=8: 97,20,0,0,195,143,0,0

select dump(5217.4197) from dual;

DUMP(5217.4197)             
----------------------------
Typ=2 Len=5: 194,53,18,42,98

类型2 记录number 并在MoS文档ID 1031902.6中进一步说明。

如果您将计算显式转换为2型数字,则会得到相同的错误:

select (cast(sysdate - date '2002-12-25' as number)) year to month from dual;

ORA-30083: syntax error was found in interval value expression

类型14似乎是一个未记录的内部表示形式,并且Oracle能够-在内部-以一种普通2型数字无法实现的方式在时间间隔之间来回转换。

您可以使用numtodsinterval(5217.4197, 'DAY')将数字转换为间隔,但是由于日秒间隔和年月间隔不兼容,因此无法从中提取年数(是有道理的,因为一年中的天数会有所不同)。

如果尝试使用时间戳而不是日期来避免出现类似的问题,那么您会看到类似的问题:

select (systimestamp - timestamp '2002-12-25 00:00:00') year to month from dual;

(SYSTI
------
+14-03

select dump(systimestamp - timestamp '2002-12-25 00:00:00') from dual;

DUMP(SYSTIMESTAMP-TIMESTAMP'2002-12-2500:00:00')
------------------------------------------------------------------------
Typ=190 Len=24: 97,20,0,0,9,0,0,0,38,0,0,0,44,0,0,0,24,177,20,9,10,0,0,0

它也不是记录的间隔类型之一,但可以在内部转换为年月或日秒。 尝试在CTE中使用它也会失败; 使用extract(year from age.d)会得到“ ORA-30076:提取源的无效提取字段”,因为此时它被视为一个DS间隔,而extract(year from (age.d) year to month)返回到ORA-30083。

您最好从间隔切换到其他单位,例如月份:

with age(m) as
(
  select months_between(sysdate, date '2002-12-25') from dual
)
select 
  trunc(age.m / 12) Y
from age;

         Y
----------
        14

如果还需要月份数,则可以使用trunc(remainder(age.m, 12))

或者,如果您确实需要间隔,请在CTE中进行转换:

with age(ym) as
(
  select numtoyminterval(
    months_between(sysdate, date '2002-12-25'), 'MONTH') from dual
)
select 
  extract(year from age.ym) Y 
from age;

或更接近您的原件:

with age(ym) as
(
  select (sysdate - date '2002-12-25') year to month from dual
)
select 
  extract(year from age.ym) Y 
from age;

有趣的是, 文档显示这实际上是不允许的-“产生间隔值的六个组合在间隔表达式中有效”,而date-date并不是这六个组合之一 这可能表明类型14比数字类型更接近间隔。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM