简体   繁体   English

Oracle SQL-获取指定月份两个日期之间的天数

[英]Oracle SQL - get number of days between two dates for a specified month

I want to get the number of days between two dates but only for a specified month. 我想获取两个日期之间的天数,但仅用于指定的月份。

Sample Data: 样本数据:

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

Expected output for February: 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

Expected output for January with same dates: 具有相同日期的一月份的预期产量:

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

Expected output for March with same dates: 具有相同日期的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

The month will be selected by a parameter (when output into excel via ODBC) by a month number 1 to 12. 月份将由参数(当通过ODBC输出到excel中时)由月份号1到12选择。

February is not a month, it is the generic name of a month in a year. 2月不是一个月,它是一年中一个月的通用名称。 A "month" in the proper sense is February 2016, or February 2017 etc. Based on your desired output, I assume you mean February 2016. 正确的“月份”是2016年2月或2017年2月等。根据您期望的输出,我假设您的意思是2016年2月。

The problem is trivial. 这个问题是微不足道的。 However you define the month, you can identify the first and the last day of the month. 无论您定义月份,您都可以标识该月的第一天和最后一天。 For example, if you input the month as a six-character string: input = '201602' , then you can use something like 例如,如果您将月份输入为六个字符的字符串: input = '201602' ,则可以使用类似

to_date(input, 'yyyymm')                as month_start, 
last_day(to_date(input, 'yyyymm'))      as month_end

and then compute number of days like this: 然后像这样计算天数:

Preparation (in SQLPlus): 准备 (在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';

Query : 查询

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
;

Output : (Note: the numbers in your "desired output" are incorrect) 输出 :(注意:“所需输出”中的数字不正确)

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.

You can expand each date range into its component days, either with a hierarchical query or a recursive CTE ; 您可以使用分层查询或递归CTE将每个日期范围扩展到其组成天数; and then count how many match the specified month: 然后计算与指定月份匹配的数量:

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;

Using 1, 2 and 3 in turn for the bind variable generates: 依次对绑定变量使用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

Alternatively you can generate all the days for the specified month and get the intersect of both sets. 或者,您可以生成指定月份的所有日期,并获取两个集合的相交。

As you're only specifying the month, not the year, you're assuming a range will never span more than a year - or if it does, that you want to count days in the months that appear in both years. 由于您仅指定月份而不是年份,因此假设范围永远不会超过一年-如果是,则要计算两个年份中出现的月份中的天数。

It's also assuming the same date range doesn't appear more than once; 它还假设相同的日期范围不会出现多次; if it does then the grouping and counting will be off. 如果这样做,则分组和计数将关闭。 Presumably your real data has a key that you haven't shown, which could be included in the CTE and the group-by clause to avoid that. 大概您的真实数据具有一个尚未显示的密钥,可以将其包含在CTE和group-by子句中,以避免出现这种情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM