2015-12-21 43 views
0

我有我的SQL查詢的條件,使用Oracle 11g數據庫,在一個財政年度取決於計劃的開始或結束有:SQL計算和重新使用SQL查詢財政年度計算

(BUSPLAN.START_DATE BETWEEN (:YEAR || '-04-01') AND (:YEAR+1 || '-03-31')) OR 
(BUSPLAN.END_DATE BETWEEN (:YEAR || '-04-01') AND (:YEAR+1 || '-03-31')) 

現在,我作爲參數傳入YEAR。它可以被計算爲(僞):

IF CURRENT MONTH IN (JAN, FEB, MAR): 
    USE CURRENT YEAR // e.g. 2015 
ELSE: 
    USE CURRENT YEAR + 1 // e.g. 2016 

有沒有一種方法,我可以在計算機的SQL查詢中的:YEAR參數,並重新使用它的:YEAR參數?

+0

這是很容易做到的CTE - 你需要一個例子 – Hogan

+0

@Hogan是,請,我m不知道如何使用CTE – dave

+0

您的邏輯似乎是如果當前年份是2015年,當前月份是「2015-04-01」到「2016-03-31」的會計年度的一月,二月,三月之一?如果當年是2015年,月份大於3月份,您希望'2016年4月1日到2017年3月31日'?或**下一個**會計年度。你在找什麼? –

回答

1

CTE很容易,你可以在飛行中製作小桌子。隨着1行的表,你剛剛跨過加入它,然後你必須提供該值的每一行:

WITH getyear as 
(
    SELECT 
     CASE WHEN to_char(sysdate,'mm') in ('01','02','03') THEN 
      EXTRACT(YEAR FROM sysdate) 
      ELSE 
      EXTRACT(YEAR FROM sysdate) + 1 
     END as ynum from dual 
), mydates as 
(
    SELECT getyear.ynum || '-04-01' as startdate, 
     getyear.ynum+1 || '-03-31' as enddate 
    from getyear 
) 
select 
    -- your code here 
from BUSPLAN, mydates -- this is a cross join 
where 
    (BUSPLAN.START_DATE BETWEEN mydates.startdate AND mydates.enddate) OR 
    (BUSPLAN.END_DATE BETWEEN mydates.startdate AND mydates.enddate) 

注意到,價值觀說法是,如果Oracle有值,那麼第一CTE看起來像這樣可能會更好:

VALUES(CASE WHEN to_char(sysdate,'mm') in ('01','02','03') THEN 
      EXTRACT(YEAR FROM sysdate) 
      ELSE 
      EXTRACT(YEAR FROM sysdate) + 1) 

我沒有訪問Oracle的權限,所以我可能有錯誤的錯別字等,因爲我沒有測試。

+0

謝謝,作品精美。我在'getyear' case statement中檢查'EXTRACT(MONTH FROM SYSDATE)> 3'做了一個很小的改動 – dave

+0

@dave - 我真的想過要這樣做,但是想想如果你有更多的情況這樣更容易轉換到一般情況。 – Hogan

1

在您分享的代碼中存在問題和潛在的問題。

問題隱式轉換爲不帶格式字符串的日期。

(BUSPLAN.START_DATE BETWEEN (:YEAR || '-04-01') AND (:YEAR+1 || '-03-31'))兩個字符串正在形成,然後轉換爲日期。到日期的轉換將根據NLS_DATE_FORMAT的值而發生變化。確保字符串轉換正確to_date(:YEAR || '-04-01', 'YYYY-MM-DD')

潛在的問題,邊界在年底時間<>午夜。

Oracle的date類型包含日期和時間。像這樣的測試將會遺漏在endDate午夜後發生的所有記錄。一個簡單的解決方案,排除someDate上索引的使用是trunc(someDate) between startDate and endDate

更常用的方法是定義日期範圍和關閉的開放時間間隔。 lowerBound <= aDate < upperBound其中lowerBound is the same as startDate above and upperBound is endDate`加一天。

注:使用Oracle日期列,日期,總有一些應用商店午夜,如果你的應用程序之類的,那麼這是沒有問題的。檢查像check (trunc(dateColumn) = dateColumn)這樣的約束將確保它保持這種狀態。


現在,回答實際提出的問題。

使用子查詢因子分解(Oracle的術語)/公用表表達式(SQL Server的術語)可以避免重複查詢。

代替計算出適當的年份,然後使用字符串彙總日期,下面的代碼從當前日曆年的午夜開始,即1月1日開始,trunc(sysdate, 'YEAR'))。然後它在幾個月內增加一個抵消。當月份是1月,2月,3月,本財年在去年4月1日開始,即今年開始前的9個月開始。偏移量是-9。否則,本財年開始今年的4/1,今年開始加上三個月。

不是結束日期,而是計算上限,類似於下限,但偏移量大於下限的12,以在下一年獲得4/1。

with current_fiscal_year as (select add_months(trunc(sysdate, 'YEAR') 
      , case when extract(month from sysdate) <= 3 then -9 else 3 end) as LowerBound 
     , add_months(trunc(sysdate, 'YEAR') 
      , case when extract(month from sysdate) <= 3 then 3 else 15 end) as UpperBound 
    from dual) 
select * 
from busplan 
cross join current_fiscal_year CFY 
where (CFY.LowerBound <= busplan.start_date and busplan.start_date < CFY.UpperBound) 
or (CFY.LowerBound <= busplan.end_date and busplan.end_date < CFY.UpperBound) 

並且還更不請自來的建議。

我不得不處理財政年度的事情,避免查詢內重複的時間是低掛果。財務年度計算在許多查詢中保持一致和正確,這纔是工作的本質。所以我建議開發一個集中財務計算的開發PL/SQL包。它可能包括一個功能,如:

create or replace function GetFiscalYearStart(v_Date in date default sysdate) 
    return date 
as begin 
    return add_months(trunc(v_Date, 'YEAR') 
     , case when extract(month from v_Date) <= 3 then -9 else 3 end); 
end GetFiscalYearStart; 

然後上面的查詢變爲:

select * 
from busplan 
where (GetFiscalYearStart() <= busplan.start_date 
    and busplan.start_date < add_months(GetFiscalYearStart(), 12)) 
or (GetFiscalYearStart() <= busplan.end_date 
    and busplan.end_date < add_months(GetFiscalYearStart(), 12)) 
+0

我不同意函數的使用 - 大多數sql優化器對重複查詢函數的部分以創建優化版本要快樂得多。 – Hogan

+0

@Hogan取決於函數和查詢。 –

+0

真的嗎?你確定?我從來沒有見過(或可以想到)一個「減少重複」功能可以使查詢更快的情況。我知道(並且使用過)更快的函數,當它們被調用到編譯庫時,它們提供了SQL中不提供的功能,或者針對特定功能(通常是解析或模式匹配)優化的庫。但只是一些標準的SQL,如case語句或truncate?我很難相信這一點。我期待着一個例子證明我錯了。只是說「依賴」是不夠的 - 證明它。 – Hogan