【问题标题】:How to find out which months in a year range that's having 5 Saturdays between two given dates as per the inputs?如何根据输入找出两个给定日期之间有 5 个星期六的年份范围内的哪些月份?
【发布时间】:2019-07-08 16:30:02
【问题描述】:

只能通过查询方式,没有执行块方式。

我尝试了以下但没有运气

SELECT
    TO_CHAR(dt, 'mon'),
    COUNT(*) AS noofsat
FROM
    (
        SELECT
            from_date + 1 AS dt
        FROM
            dual
        CONNECT BY
            level <= (
                SELECT
                    TO_DATE - from_date
                FROM
                    dual
            )
    )
WHERE
    TO_CHAR(dt, 'd') = 7
GROUP BY
    TO_CHAR(dt, 'mon')
HAVING
    COUNT(*) = 5

我只需要像 2019 年 8 月这样的有五个星期六的月份和年份

【问题讨论】:

  • from_dateto_date 来自哪里;以及它们将具有什么样的值 - 年份开始/结束,或任何恰好相隔一年的日期,或任何两个日期? (to_date 无论如何都会给你带来问题,因为这是一个函数名......)

标签: sql oracle oracle18c


【解决方案1】:

将您的日期来源更改为:

    SELECT
        TO_DATE('20190101','YYYYMMDD') + (LEVEL - 1) AS dt
    FROM
        dual
    CONNECT BY
        level <= 365

【讨论】:

    【解决方案2】:

    这是一个非常好的问题,在我的情况下,我需要完全相同的东西,但在 SQL Server 中,我就是这样做的,我将为那些最终在这里寻找 SQL Server 解决方案的人发布答案。

    我最终在我的数据仓库中构建了一个日期维度并对其进行了查询:

    DROP TABLE IF EXISTS ##Dates
    
    
     CREATE TABLE ##Dates(DateValue Date, 
                          YearValue INT, 
                          MonthValue INT, 
                          DayValue INT,
                          WeekDayValue INT)
    
    
      DECLARE @start DATE = '2019-01-01'
      WHILE @start < '2019-12-31'
      BEGIN
        INSERT INTO  ##Dates(DateValue,
                             YearValue, 
                             MonthValue, 
                             DayValue,
                             WeekDayValue)
        VALUES(@start,
                DATEPART(YY,@start), 
                DATEPART(mm,@start), -- In oracle TO_CHAR(dt, 'mon')
                DATEPART(dd,@start),
                DATEPART(dw,@start)) --In oracle  TO_CHAR(dt, 'd')
    
        SET @start = DATEADD(dd,1,@start)
      END
    
    
    
      SELECT MonthValue, SUM(CASE WHEN WeekDayValue = 7 THEN 1 ELSE 0 END) AS NumOfSaturdays FROM ##Dates
    WHERE DateValue BETWEEN '2019-01-01' AND '2019-12-01'
    GROUP BY MonthValue
    HAVING SUM(CASE WHEN WeekDayValue = 7 THEN 1 ELSE 0 END) > 4
    

    输出:

    MonthValue  NumOfSaturdays
    3            5
    6            5
    8            5
    11           5
    

    【讨论】:

      【解决方案3】:

      您可以获得您的日期范围所涵盖的每个月的开始时间;这使用了一对虚构的固定值,因为不清楚你从哪里得到你的范围:

      select add_months(trunc(date '2019-03-15', 'MM'), level - 1) as month_start
      from dual
      connect by level <= ceil(months_between(date '2019-11-21', date '2019-03-15'));
      
      MONTH_STAR
      ----------
      2019-03-01
      2019-04-01
      2019-05-01
      2019-06-01
      2019-07-01
      2019-08-01
      2019-09-01
      2019-10-01
      2019-11-01
      
      9 rows selected. 
      

      然后,您可以通过各种方式操作这些日期。您可以使用next_day() 函数查找该月的第一个星期六 - 基于上一个 日,以防当月的第 1 日本身是星期六。如果再加上 28 天,则得到该月的第五个星期六——或者,对于只有四个星期六的月份,下个月的第一个星期六。因此,如果您比较那些生成的星期六,如果它们在同一个月,那么该月实际上必须有五个星期六。

      select month_start,
        next_day(month_start - 1, 'SATURDAY') as first_sat,
        next_day(month_start - 1, 'SATURDAY') + 28 as poss_fifth_sat,
        extract(month from month_start) as month_num,
        extract(month from (next_day(month_start - 1, 'SATURDAY') + 28)) as poss_fifth_sat_month
      from (
        select add_months(trunc(date '2019-03-15', 'MM'), level - 1) as month_start
        from dual
        connect by level <= ceil(months_between(date '2019-11-21', date '2019-03-15'))
      );
      
      MONTH_STAR FIRST_SAT  POSS_FIFTH  MONTH_NUM POSS_FIFTH_SAT_MONTH
      ---------- ---------- ---------- ---------- --------------------
      2019-03-01 2019-03-02 2019-03-30          3                    3 -- same
      2019-04-01 2019-04-06 2019-05-04          4                    5
      2019-05-01 2019-05-04 2019-06-01          5                    6
      2019-06-01 2019-06-01 2019-06-29          6                    6 -- same
      2019-07-01 2019-07-06 2019-08-03          7                    8
      2019-08-01 2019-08-03 2019-08-31          8                    8 -- same
      2019-09-01 2019-09-07 2019-10-05          9                   10
      2019-10-01 2019-10-05 2019-11-02         10                   11
      2019-11-01 2019-11-02 2019-11-30         11                   11 -- same
      
      9 rows selected. 
      

      您实际上不需要查看这些值,我只是在展示其工作原理。但是您可以使用最后两个作为过滤器:

      select month_start
      from (
        select add_months(trunc(date '2019-03-15', 'MM'), level - 1) as month_start
        from dual
        connect by level <= ceil(months_between(date '2019-11-21', date '2019-03-15'))
      )
      where extract(month from month_start)
        = extract(month from (next_day(month_start - 1, 'SATURDAY') + 28));
      
      MONTH_STAR
      ----------
      2019-03-01
      2019-06-01
      2019-08-01
      2019-11-01
      

      然后,您可以随意格式化这些日期。


      提到了一个可能的问题(我相信还有其他问题)in the documentation

      参数 char 必须是会话日期语言中的星期几,可以是全名或缩写。

      因此,只要您的会话日期语言是英语,上述代码就可以工作。如果您可以 100% 确定运行此程序的每个人将始终使用相同的会话设置,那么您使用该固定值(或任何合适的语言)。

      如果您无法控制它,那么您就不能像使用其他一些功能那样覆盖日期语言。作为一种解决方法,您可以使用已知的星期六日期来获取当前日期语言的名称或星期六的缩写,而无需知道该语言实际上是什么:

      alter session set nls_date_language = 'Spanish';
      
      select month_start
      from (
        select add_months(trunc(date '2019-03-15', 'MM'), level - 1) as month_start
        from dual
        connect by level <= ceil(months_between(date '2019-11-21', date '2019-03-15'))
      )
      where extract(month from month_start)
        = extract(month from (next_day(month_start - 1, to_char(date '2000-01-01', 'Dy')) + 28));
      
      MONTH_STAR
      ----------
      2019-03-01
      2019-06-01
      2019-08-01
      2019-11-01
      

      【讨论】:

        猜你喜欢
        • 2023-03-23
        • 1970-01-01
        • 1970-01-01
        • 2020-12-07
        • 1970-01-01
        • 2016-02-29
        • 2014-06-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多