我希望通过传递一个包含年份和月份的变量来显示星期日期范围,即2016-07中的这个在mysql中如何实现此目的: 例如:如果我通过2016-07,那么我的输出应该是这样的:
2016-07-03 to 2016-07-10 || 2016-07-11 to 2016-07-17
等等
答案 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