使用mysql的特定月份的一周范围

时间:2016-07-01 10:16:12

标签: mysql datetime

我希望通过传递一个包含年份和月份的变量来显示星期日期范围,即2016-07中的这个在mysql中如何实现此目的: 例如:如果我通过2016-07,那么我的输出应该是这样的:

2016-07-03 to 2016-07-10 || 2016-07-11 to 2016-07-17

等等

2 个答案:

答案 0 :(得分:0)

当你在MySQL中解决它时,有几个部分可以解决这个问题。

一个是决定你的周是星期日还是星期一开始。这是特定于语言环境的业务规则。在以前属于大英帝国(美国,印度)的地方,通常是星期天。在其他地方,这是星期一。

所以我们需要这样的函数:firstDayOfWeek(date)。更多关于此的内容。

一旦我们有了这个,我们就需要一种方法来获得有关月份的最后一天。这很简单;这是一个built-in MySQL function. LAST_DAY(date)

您已说过,您将使用1941-12这样的字符串指定相关月份。我们需要将其转换为DATE对象。这可以这样做:

  STR_TO_DATE(CONCAT(`1941-12`, `-01'), '%Y-%m-%d')

我们需要一个包含0到4整数的虚拟表。让我们调用该表seq_0_to_4。我们选择整数范围,因为没有几个月有超过五个星期日(或星期一)。稍后会详细介绍。

好的,这些是概念性构建块。我们来使用它们。

从该月的最后一天派生的一周的第一天是

SET @month := '1941-12';
SET @first_day_of_month := STR_TO_DATE(CONCAT(`1941-12`, `-01'), '%Y-%m-%d')
SET @seed : =FirstDayOfWeek(LAST_DAY(first_day_of_month));

然后你需要连续四个星期开始的日子,其中最后一个是@seed。

        SELECT @seed - INTERVAL (7*seq.seq) first_day_of_week
          FROM seq_0_to_4

接下来,您需要将其限制为当月的天数。

SELECT first_day_of_week, 
       first_day_of_week + INTERVAL 6 DAY last_day_of_week
  FROM (
        SELECT @seed - INTERVAL (7*seq.seq) first_day_of_week
          FROM seq_0_to_4
       ) w
 WHERE first_day_of_week >= @first_day_of_month
 ORDER BY first_day_of_week

这会为您提供一个行表,每月一行。如果您要排除上个工​​作日在下个月的周数,请将您的WHERE更改为

 WHERE first_day_of_week >= @first_day_of_month
   AND first_day_of_week + INTERVAL 6 DAY <= @seed

最后,要获得您在问题中指定的确切字符串格式,请将该查询包装在:

SELECT GROUP_CONCAT (
             CONCAT(first_day_of_week, ' to ', last_day_of_week) 
             SEPARATOR ' || '
             ORDER BY first_day_of_week)
  FROM (
        SELECT first_day_of_week, 
               first_day_of_week + INTERVAL 6 DAY last_day_of_week
          FROM (
                SELECT @seed - INTERVAL (7*seq.seq) first_day_of_week
                  FROM seq_0_to_4
               ) w
         WHERE first_day_of_week >= @first_day_of_month
       ) x

就是这样。

我答应描述FirstDayOfWeek(dt)。在这里。

      FROM_DAYS(TO_DAYS(dt) -MOD(TO_DAYS(dt) -1, 7))

这是一个魔法咒语,但它有效。如果您的周数从周一开始,就是这样。

       FROM_DAYS(TO_DAYS(dt) -MOD(TO_DAYS(dt) -2, 7))

我答应描述seq_0_to_4。如果您正在使用MySQL的MariaDB分支,那么它就是内置的。如果您使用的是Sun / Oracle分支,则可以像这样定义它。

 (SELECT 0 AS seq UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) seq_0_to_4

全部放在一起:

SET @month := '1941-12';
SET @first_day_of_month := STR_TO_DATE(CONCAT(`1941-12`, `-01'), '%Y-%m-%d');
SET @seed := FROM_DAYS(TO_DAYS(LAST_DAY(first_day_of_month)) 
                       -MOD(TO_DAYS(LAST_DAY(first_day_of_month)) -1, 7));
SELECT GROUP_CONCAT (
             CONCAT(first_day_of_week, ' to ', last_day_of_week) 
             SEPARATOR ' || '
             ORDER BY first_day_of_week)
  FROM (
        SELECT first_day_of_week, 
               first_day_of_week + INTERVAL 6 DAY last_day_of_week
          FROM (
                SELECT @seed - INTERVAL (7*seq.seq) first_day_of_week
                  FROM      (SELECT 0 AS seq UNION SELECT 1 UNION SELECT 2 
                                             UNION SELECT 3 UNION SELECT 4
                            ) seq
               ) w
         WHERE first_day_of_week >= @first_day_of_month
       ) x

在纯MySQL方言SQL中解决你的问题是不合理的复杂(技术术语是一个怪异的'毛球),但这是可能的。

答案 1 :(得分:0)

可能为时已晚,但我在MYSQL 8.0.2中使用Common Table表达式弄清了这一点

WITH RECURSIVE 
Years(y) AS 
        (
        SELECT 2020
        UNION ALL
        SELECT y + 1 FROM Years WHERE y < 2021
        ),
Days (d) AS
        (
        SELECT 1
        UNION ALL
        SELECT d + 1 FROM Days WHERE d < 366
        )
SELECT 
y AS Year,
MONTH(MakeDate(y,d)) AS Month,
WEEK(MakeDate(y,d))+1 -WEEK(TIMESTAMPADD(MONTH,MONTH(MakeDate(y,d))-1,MakeDate(y,1))) AS Week,
Min(MakeDate(y,d)) AS StartDate,
timestampadd(second,-1,timestampadd(day,1,MAx(MakeDate(y,d)))) AS EndDate
FROM Years,Days
WHERE Year(MakeDate(y,d)) <= y
GROUP BY y, MONTH(MakeDate(y,d)),WEEK(MakeDate(y,d))+1 -WEEK(TIMESTAMPADD(MONTH,MONTH(MakeDate(y,d))-1,MakeDate(y,1)))
ORDER BY 1,2,3