我的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
参数?
答案 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))