2017-04-07 42 views
0

我想了解下面兩個查詢之間的區別。第二個引發語法錯誤。有人可以幫忙嗎?Oracle sql WITH子句和EXTRACT函數

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; 

錯誤:在間隔值表達被發現語法錯誤 30083. 00000 - 「語法錯誤是在間隔值實測值: ORA-30083表達式「 *原因:在分析間隔值期間發現語法錯誤

+0

請分享消息 – Jens

回答

1

你的問題可以簡化了一下,爲什麼這項工作:

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 

問題是這兩個數字實際上是不同的我nternally。作爲@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型是documentednumber。並在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似乎是一個未公開的內部表示,與甲骨文能 - 國內 - 要轉換的是以一種不能用正常的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; 

有趣的是,the documentation表明,這是不是真的允許 - 「產生間隔值的六個組合在間隔表達式中是有效的,並且日期 - 日期不是one of those six。這可能表明類型14比數字類型更接近間隔。

+0

非常翔實。感謝您花時間解釋各種相關的位。 – Slkrasnodar

1

在第二個查詢中,我s 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 returns a VARCHAR2 value containing the data type code, length in bytes, and internal representation of expr.