我希望获得两个日期之间的天数,但仅限指定的月份。
示例数据:
datefrom dateto
28/1/2016 15/2/2016
10/2/2016 3/3/2016
5/2/2016 16/2/2016
20/1/2016 10/3/2016
2月的预期产出:
datefrom dateto numofdays
28/1/2016 15/2/2016 15
10/2/2016 3/3/2016 19
5/2/2016 16/2/2016 11
20/1/2016 10/3/2016 29
具有相同日期的1月的预期产出:
datefrom dateto numofdays
28/1/2016 15/2/2016 4
10/2/2016 3/3/2016 0
5/2/2016 16/2/2016 0
20/1/2016 10/3/2016 11
具有相同日期的3月预期产量:
datefrom dateto numofdays
28/1/2016 15/2/2016 0
10/2/2016 3/3/2016 3
5/2/2016 16/2/2016 0
20/1/2016 10/3/2016 10
月份将通过参数(通过ODBC输出到excel时)按1到12个月选择。
答案 0 :(得分:3)
2月不是一个月,它是一年中一个月的通用名称。适当意义上的“月份”是2016年2月,或2017年2月等。根据您的预期输出,我假设您的意思是2016年2月。
这个问题很简单。无论您如何定义月份,都可以识别该月份的第一天和最后一天。例如,如果您将月份输入为六个字符的字符串:input = '201602'
,那么您可以使用类似
to_date(input, 'yyyymm') as month_start,
last_day(to_date(input, 'yyyymm')) as month_end
然后像这样计算天数:
准备(在SQLPlus中):
SQL> variable input varchar2(30)
SQL> exec :input := '201602';
PL/SQL procedure successfully completed.
SQL> alter session set nls_date_format = 'dd/mm/yyyy';
<强>查询强>:
with
test_dates ( datefrom, dateto ) as (
select to_date('28/1/2016', 'dd/mm/yyyy'), to_date('15/2/2016', 'dd/mm/yyyy') from dual union all
select to_date('10/2/2016', 'dd/mm/yyyy'), to_date('3/3/2016' , 'dd/mm/yyyy') from dual union all
select to_date('5/2/2016' , 'dd/mm/yyyy'), to_date('16/2/2016', 'dd/mm/yyyy') from dual union all
select to_date('20/1/2016', 'dd/mm/yyyy'), to_date('10/3/2016', 'dd/mm/yyyy') from dual
)
-- end of test data; solution (SQL query) begins below this line
select t.datefrom, t.dateto, to_char(to_date(:input, 'yyyymm'), 'MON yyyy') as month,
case when t.datefrom > m.month_end or t.dateto < m.month_start then 0
else least(t.dateto, m.month_end) - greatest(t.datefrom, m.month_start) + 1
end as number_of_days
from test_dates t cross join
( select to_date(:input, 'yyyymm') as month_start,
last_day(to_date(:input, 'yyyymm')) as month_end
from dual) m
;
输出 :(注意:“所需输出”中的数字不正确)
DATEFROM DATETO MONTH NUMBER_OF_DAYS
---------- ---------- -------- --------------
28/01/2016 15/02/2016 FEB 2016 15
10/02/2016 03/03/2016 FEB 2016 20
05/02/2016 16/02/2016 FEB 2016 12
20/01/2016 10/03/2016 FEB 2016 29
4 rows selected.
答案 1 :(得分:1)
您可以使用分层查询或recursive CTE将每个日期范围扩展到其组成日期;然后计算与指定月份匹配的数量:
with r (datefrom, dateto, dateday) as (
select datefrom, dateto, datefrom
from t
union all select datefrom, dateto, dateday + 1
from r
where dateday < dateto
)
select datefrom, dateto,
to_char(to_date(:monthnum, 'MM'), 'MON', 'NLS_DATE_LANGUAGE=ENGLISH') as month,
count(case when to_char(dateday, 'MM') = :monthnum then dateday end) as numofdays
from r
group by datefrom, dateto
order by datefrom, dateto;
依次使用1,2和3生成绑定变量:
DATEFROM DATETO MONTH NUMOFDAYS
--------- --------- ------------ ----------
20-JAN-16 10-MAR-16 JAN 12
28-JAN-16 15-FEB-16 JAN 4
05-FEB-16 16-FEB-16 JAN 0
10-FEB-16 03-MAR-16 JAN 0
DATEFROM DATETO MONTH NUMOFDAYS
--------- --------- ------------ ----------
20-JAN-16 10-MAR-16 FEB 29
28-JAN-16 15-FEB-16 FEB 15
05-FEB-16 16-FEB-16 FEB 12
10-FEB-16 03-MAR-16 FEB 20
DATEFROM DATETO MONTH NUMOFDAYS
--------- --------- ------------ ----------
20-JAN-16 10-MAR-16 MAR 10
28-JAN-16 15-FEB-16 MAR 0
05-FEB-16 16-FEB-16 MAR 0
10-FEB-16 03-MAR-16 MAR 3
或者,您可以生成指定月份的所有日期并获得两组的交叉。
由于您只是指定月份,而不是年份,因此您假设范围永远不会超过一年 - 或者如果确实如此,您希望计算两年中出现的月份的天数。
它还假设相同的日期范围不会出现多次;如果是,则分组和计数将关闭。据推测,您的真实数据有一个未显示的密钥,可以包含在CTE和group-by子句中以避免这种情况。