美文网首页
MySQL 计算本月的第一个和最后一个周一

MySQL 计算本月的第一个和最后一个周一

作者: 只是甲 | 来源:发表于2020-11-04 14:38 被阅读0次

    备注:测试数据库版本为MySQL 8.0

    一.需求:

    计算当前月的第一个星期一及最后一个星期一的日期。

    二.解决方案

    这里选用了Monday和当前月,也可以将该解决方案应用于其他日子和月份。
    由于每个相同的周内日期的间隔都是7天,所以知道第一个后,加7就能知道第二个,加14天就能知道第三个。
    同样,如果制动啊某个月的最后一个指定的周期内日期,则减7就能得到第三个,再减7就能得到第二个。

    使用函数adddate,找到当月的第一天。
    得到当前月的第一天之后,可以使用简单的算法和表示星期几的数学值(星期日-星期六分别对应1-7),
    以获得当前月的第一个和最后一个星期一:

    select first_monday,
           case month(adddate(first_monday,28))
                when mth then adddate(first_monday,28)
                         else adddate(first_monday,21)
           end last_monday
      from (
    select case sign(dayofweek(dy) -2)
                when 0 then dy
                when -1 then adddate(dy,abs(dayofweek(dy) -2))
                when 1  then adddate(dy,(7-(dayofweek(dy) -2)))
           end first_monday,
           mth
       from (
    select adddate(adddate(current_date,-day(current_date)),1) dy,
           month(current_date) mth
           ) x
           ) y
    

    测试记录

    mysql> select first_monday,
        ->        case month(adddate(first_monday,28))
        ->             when mth then adddate(first_monday,28)
        ->                      else adddate(first_monday,21)
        ->        end last_monday
        ->   from (
        -> select case sign(dayofweek(dy) -2)
        ->             when 0 then dy
        ->             when -1 then adddate(dy,abs(dayofweek(dy) -2))
        ->             when 1  then adddate(dy,(7-(dayofweek(dy) -2)))
        ->        end first_monday,
        ->        mth
        ->    from (
        -> select adddate(adddate(current_date,-day(current_date)),1) dy,
        ->        month(current_date) mth
        ->        ) x
        ->        ) y
        -> ;
    +--------------+-------------+
    | first_monday | last_monday |
    +--------------+-------------+
    | 2020-10-05   | 2020-10-26  |
    +--------------+-------------+
    1 row in set (0.00 sec)
    

    这样看起来很复杂,可以拆解开来
    adddate(adddate(current_date,-day(current_date)),1) dy -- 当月第一天
    month(current_date) -- 当月月份
    dayofweek('2020-10-01') -- 计算当前星期数
    (星期日-星期六分别对应1-7)

    -- 接下来就是一个算法了 因为星期一对应的是2,所以来比对大小
    sign(dayofweek(dy) -2) 括号里面是整数 返回1,是0 返回0,是负数 返回 -1
    当为0的时候 本月第一个星期1就是dy
    当为-1的时候 代表第一个星期1在本个礼拜,那么此时只要求出 距离星期1(2)的差距
    adddate(dy,abs(dayofweek(dy) -2))
    其实就是加上 2-dayofweek(dy) 即可求到本月的第一个 礼拜一

    当为1的时候 代表本月一号的星期数其实是大于要求的星期数,下一个星期1在下个礼拜
    adddate(dy,(7-(dayofweek(dy) -2))) dayofweek('2020-10-01') 是星期四,返回5
    7 - (5-2) = 7 - 3 = 4 5+4-7=2 所以这个地方就是要求出这个差距4
    这个地方我也想了下, 本周是5,其实我要求的是2 2<5 所以求的是2+7,2+7与5的间隔就是要增加的
    其实就是 7 +2 - dayofweek

    case month(adddate(first_monday,28))
    when mth then adddate(first_monday,28)
    else adddate(first_monday,21)
    end last_monday
    这段代码其实就是判断本月有3个还是4个星期1,根据增加了21、28来计算是否本月来计算

    用with语句封装下,看起来程序逻辑更为直观

    with tmp1 AS
    (
    select adddate(current_date,-dayofmonth(current_date)+1) dy,month(current_date) mth,dayofweek(current_date) wk
    ),
    tmp2 AS
    (
    SELECT dy,mth,wk,case sign(wk -2) when 0 then dy
                       when -1 then adddate(dy,abs(wk -2))
                       when 1 then adddate(dy,7+2 - wk)
                  end as first_monday
    from tmp1
    )
    SELECT first_monday,
           case  month(adddate(first_monday,28)) when   mth then adddate(first_monday,28)
                 else adddate(first_monday,21) 
           end as last_monday
    from tmp2
    

    测试记录

    mysql> with tmp1 AS
        -> (
        -> select adddate(current_date,-dayofmonth(current_date)+1) dy,month(current_date) mth,dayofweek(current_date) wk
        -> ),
        -> tmp2 AS
        -> (
        -> SELECT dy,mth,wk,case sign(wk -2) when 0 then dy
        ->                    when -1 then adddate(dy,abs(wk -2))
        ->                    when 1 then adddate(dy,7+2 - wk)
        ->               end as first_monday
        -> from tmp1
        -> )
        -> SELECT first_monday,
        ->        case  month(adddate(first_monday,28)) when   mth then adddate(first_monday,28)
        ->              else adddate(first_monday,21)
        ->        end as last_monday
        -> from tmp2
        -> ;
    +--------------+-------------+
    | first_monday | last_monday |
    +--------------+-------------+
    | 2020-10-05   | 2020-10-26  |
    +--------------+-------------+
    1 row in set (0.00 sec)
    
    

    相关文章

      网友评论

          本文标题:MySQL 计算本月的第一个和最后一个周一

          本文链接:https://www.haomeiwen.com/subject/ralopktx.html