SQL计算和重用sql查询中的会计年度计算

时间:2015-12-21 18:19:08

标签: sql oracle

我的SQL查询中有一个条件,使用Oracle 11g数据库,这取决于在会计年度开始或结束的计划:

(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参数?

2 个答案:

答案 0 :(得分: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,因此我可能会因为没有测试而错误输入错误。

答案 1 :(得分: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类型包含日期和时间。像someDate between startDate and endDate这样的测试会遗漏在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))