【问题标题】:Oracle SQL query/Procedure to list Months/Quarters between two datesOracle SQL 查询/程序列出两个日期之间的月份/季度
【发布时间】:2018-07-01 13:22:58
【问题描述】:

我有一个要求

START_DATE : 03/01/2018
END_DATE : 28/12/2018

我需要一个查询来列出这两个日期之间的所有月份开始日期和结束日期

StartMonth   EndMonth
03/01/2018  31/01/2018
01/02/2018  28/01/2018
01/03/2018  31/03/2018
01/04/2018  30/04/2018
01/05/2018  31/05/2018
01/06/2018  30/06/2018
01/07/2018  31/07/2018
01/08/2018  31/08/2018
01/09/2018  30/09/2018
01/10/2018  31/10/2018
01/11/2018  30/11/2018
01/12/2018  28/12/2018

我还需要一个查询,它会像这样列出这两个日期之间所有季度的开始日期和结束日期

StartQuarter EndQuarter
03/01/2018   31/03/2018
01/04/2018   30/06/2018
01/07/2018   30/09/2018
01/10/2018   28/12/2018

【问题讨论】:

  • 只能使用 SQL 还是可以使用 PL/SQL?
  • PL/SQL 也可以
  • 您对期望返回的间隔数量是否有任何合理限制?
  • 有限制..通常是 1-3 年..但可能更多..所以我想得到一个不会将其限制在更短时间范围内的解决方案

标签: sql oracle


【解决方案1】:

这是不限于任何特定范围长度的变体:

SELECT TO_CHAR(GREATEST(
         LAST_DAY(ADD_MONTHS(:start_date, LEVEL - 2)) + 1,
         :start_date
       ), 'dd/mm/yyyy') AS StartMonth,
       TO_CHAR(LEAST(
         :end_date, 
         LAST_DAY(ADD_MONTHS(:start_date, LEVEL - 1))
       ), 'dd/mm/yyyy') AS EndMonth
FROM dual
CONNECT BY LAST_DAY(ADD_MONTHS(:start_date, LEVEL - 1)) <= LAST_DAY(:end_date);

编辑:修复开始日期范围后,它看起来非常像接受的答案,但使用不同的日期算术方式。对于季度而言,@GriffeyDog 对TRUNC 的使用可能仍然是最好的选择。

【讨论】:

  • 我的日期从 1 月 3 日开始,到 12 月 28 日结束。但是您的查询从 1 月 1 日到 12 月 31 日结束,这不符合我的要求。似乎巴巴罗斯做对了跨度>
  • 这取决于您是只想列出完整的月份还是部分月份。对于 '03/01/2018' 的输入,我认为不应列出 '01/01/2018' 到 '31/01/2018'。再说一次,这可能只是不清楚的要求。无论哪种方式,您都大致了解了所有这些解决方案是如何制定的
  • 那是因为您应该将日期作为日期而不是字符串传递。也可以更改为将日期作为字符串处理,但为简洁起见,我想省略它
  • 我的意思是结束日期,如果您的参数是字符串,则将其作为 to_date 传递
  • @AlexSavitsky 我已将它们转换为日期,但您的查询仍然得到错误的结果:sqlfiddle.com/#!4/80ffe1/21
【解决方案2】:

这是一个生成每月条目的 CTE(公用表表达式):

WITH
    aset
    AS
        (SELECT DATE '2018-01-03' start_month, DATE '2018-12-28' last_month
           FROM DUAL),
    bset (start_month, end_month, last_month)
    AS
        (SELECT start_month, LEAST (ADD_MONTHS (TRUNC (start_month, 'MM'), 1) - 1, last_month) end_month, last_month
           FROM aset
         UNION ALL
         SELECT ADD_MONTHS (TRUNC (start_month, 'MM'), 1)
              , LEAST (ADD_MONTHS (start_month, 2) - 1, last_month)
              , last_month
           FROM bset
          WHERE end_month < last_month)
SELECT start_month, end_month
  FROM bset;


START_MONTH END_MONTH
----------- ---------
03-JAN-18   31-JAN-18
01-FEB-18   02-MAR-18
01-MAR-18   31-MAR-18
01-APR-18   30-APR-18
01-MAY-18   31-MAY-18
01-JUN-18   30-JUN-18
01-JUL-18   31-JUL-18
01-AUG-18   31-AUG-18
01-SEP-18   30-SEP-18
01-OCT-18   31-OCT-18
01-NOV-18   30-NOV-18
01-DEC-18   28-DEC-18

【讨论】:

    【解决方案3】:

    这些应该可以工作。

    几个月:

        select greatest(:start_date, trunc(add_months( :start_date, level - 1), 'MON')) as startmonth,
               least(:end_date, last_day(add_months( :start_date, level - 1))) as endmonth
          from dual
    connect by level <= trunc(months_between(trunc( :end_date, 'MON'), trunc( :start_date, 'MON'))) + 1;
    

    宿舍:

        select greatest(:start_date, trunc(add_months( :start_date, 3 * (level - 1)), 'Q')) as startmonth,
               least(:end_date, last_day(add_months( :start_date, 3 * (level - 1) + 2))) as endmonth
          from dual
    connect by level <= trunc(months_between(trunc( :end_date, 'Q'), trunc( :start_date, 'Q'))) / 3 + 1;
    

    其中 :start_date 和 :end_date 是您的开始和结束日期。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-17
      • 1970-01-01
      • 1970-01-01
      • 2021-02-15
      • 1970-01-01
      • 2021-03-03
      • 1970-01-01
      • 2019-03-28
      相关资源
      最近更新 更多