MySQL:计算当月剩余销售天数
MySQL: Calculate Remaining Selling Days in Month
我有一个每日收入目标计算,其中说明我每天需要赚取多少收入才能实现每月收入目标:
'Revenue_Goal' / 'Selling_Days'
(注:'Selling_Days'是数据集中已有的一个整体。)
我想要完成的是根据过去的天数调整每日目标。我部分完成了这个:
('Revenue_Goal' - SUM('Revenue')) / DATEDIFF(MAX('Date'),NOW())
但是,我想不通的部分只是在调整后的公式中计算销售天数。我需要使用上面的公式(或类似的东西),它只计算天数:
- 周一至周五 = 1 个销售日
- 周六 = 0.5 卖出日
- 星期日 = 0
所以,如果今天是 2016 年 3 月 25 日,我的 DATEDIFF 计算结果将是 return 6,但我的目标是达到 return 4.5。
感谢任何帮助!
这是 n 种计算方法之一
SELECT
SUM(
ELT( DAYOFWEEK(CONCAT( DATE_FORMAT(now(), '%Y-%m-'),nr)),
'0','1','1','1','1','1','0.5') ) calc_days
FROM (
SELECT d2.a*10+d1.a AS nr FROM (
SELECT 0 a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT
5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS d1
CROSS JOIN (
SELECT 0 a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 ) AS d2
) AS x
WHERE nr BETWEEN DAY(NOW()) AND DAY(LAST_DAY(NOW()))
ORDER BY nr;
把它写成存储函数是有意义的。我可以想到三种方法,每一种都比前一种有所改进(尽管方法 2 可能已经足够好了。)
方法 1: 从第一个日期开始,递增到第二个日期。对于每个日期,调用 DAYOFWEEK()。如果星期天则加 0,如果星期六则加 0.5,否则加 1。
DROP FUNCTION IF EXISTS `count_days_goal`;
DELIMITER $$
CREATE FUNCTION `count_days_goal`(start_date DATE, end_date DATE) RETURNS FLOAT
DETERMINISTIC
BEGIN
DECLARE sell_days FLOAT;
DECLARE dow INT;
SET sell_days = 0.0;
WHILE start_date <= end_date DO
SET dow = DAYOFWEEK(start_date);
IF (dow > 1) THEN
IF (dow = 7) THEN SET sell_days = sell_days + 0.5;
ELSE SET sell_days = sell_days + 1.0;
END IF;
END IF;
SET start_date = start_date + INTERVAL 1 DAY;
END WHILE;
RETURN (sell_days);
END$$
DELIMITER ;
一些测试(请注意,我的实施包括计算天数中的最后一天):
select count_days_goal(DATE('2016-03-21'), DATE('2016-03-27'));
select count_days_goal(DATE('2016-03-01'), DATE('2016-03-27'));
这很简单,但效率很低。天数越远,运行需要的时间越长。请注意,一个电话仍然比眨眼要快,但对大多数开发人员来说这会有点冒犯。幸运的是我们可以做得更好。
方法二:每个完整的一周是5.5个销售日。所以将日期之间的天数除以7,暂时忽略余数,乘以5.5。
例子:32天是(4周+4天),就是22个卖出天加上4个余数。
剩下的就是事情变得棘手的地方。这 4 天可能包括周六,也可能不包括。最简单的解决方案是在最后几天使用方法 1。它永远不会循环超过 6 天,所以它很快。
所以现在代码如下所示:
DROP FUNCTION IF EXISTS `count_days_goal`;
DELIMITER $$
CREATE FUNCTION `count_days_goal`(start_date DATE, end_date DATE) RETURNS FLOAT
DETERMINISTIC
BEGIN
DECLARE sell_days FLOAT;
DECLARE dow INT;
DECLARE whole_weeks INT;
SET whole_weeks = FLOOR(DATEDIFF(end_date, start_date)/7);
SET start_date = start_date + (7 * whole_weeks);
SET sell_days = whole_weeks * 5.5;
WHILE start_date <= end_date DO
SET dow = DAYOFWEEK(start_date);
IF (dow > 1) THEN
IF (dow = 7) THEN SET sell_days = sell_days + 0.5;
ELSE SET sell_days = sell_days + 1.0;
END IF;
END IF;
SET start_date = start_date + INTERVAL 1 DAY;
END WHILE;
RETURN (sell_days);
END$$
DELIMITER ;
方法 3: 您也可以使用方法 2,但要完全摆脱循环。要了解它是如何完成的,在您面前放一个日历会很有帮助。 (我没有费心实施这个;方法 2 非常好。)
不是循环,而是在第一个和最后一个日期调用 DAYOFWEEK
。我们称它们为 dow_start
和 dow_end
.
如果dow_start < dow_end
那么那些烦人的剩余日子都在一周之内,它们不会从周六回到周日。所以你需要做的就是:
SET answer = DATEDIFF(dow_end, dow_start) + 1;
IF dow_start = 1 (i.e. Sunday) THEN subtract 1 from answer
IF dow_end is 7 (i.e. Saturday) THEN subtract 0.5 from answer
如果 dow_start > dow_end
那么那些讨厌的剩余日子就会结束。我相信在那种情况下你可以交换 dow_start
和 dow_end
(让我们回到之前的情况)。像以前一样计算它,但然后从 5.5 中减去答案并加 1。(解释:计算一周中不在 day_start
和 day_end
之间的天数,然后从一周的总数中减去它们。)
就像我说的那样我没有编写或测试那个,所以它可能不完全正确。
如何处理银行假日: 如果您将它们放在 table 中,您可以简单地 COUNT
两个日期之间的行数。从你的答案中减去它。简单! (假设银行假期从不在周末。)有些网站列出了各个国家/地区的银行假期,甚至可以为您生成一份未来日期列表,从而轻松构建假期 table.
我有一个每日收入目标计算,其中说明我每天需要赚取多少收入才能实现每月收入目标:
'Revenue_Goal' / 'Selling_Days'
(注:'Selling_Days'是数据集中已有的一个整体。)
我想要完成的是根据过去的天数调整每日目标。我部分完成了这个:
('Revenue_Goal' - SUM('Revenue')) / DATEDIFF(MAX('Date'),NOW())
但是,我想不通的部分只是在调整后的公式中计算销售天数。我需要使用上面的公式(或类似的东西),它只计算天数:
- 周一至周五 = 1 个销售日
- 周六 = 0.5 卖出日
- 星期日 = 0
所以,如果今天是 2016 年 3 月 25 日,我的 DATEDIFF 计算结果将是 return 6,但我的目标是达到 return 4.5。
感谢任何帮助!
这是 n 种计算方法之一
SELECT
SUM(
ELT( DAYOFWEEK(CONCAT( DATE_FORMAT(now(), '%Y-%m-'),nr)),
'0','1','1','1','1','1','0.5') ) calc_days
FROM (
SELECT d2.a*10+d1.a AS nr FROM (
SELECT 0 a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT
5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS d1
CROSS JOIN (
SELECT 0 a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 ) AS d2
) AS x
WHERE nr BETWEEN DAY(NOW()) AND DAY(LAST_DAY(NOW()))
ORDER BY nr;
把它写成存储函数是有意义的。我可以想到三种方法,每一种都比前一种有所改进(尽管方法 2 可能已经足够好了。)
方法 1: 从第一个日期开始,递增到第二个日期。对于每个日期,调用 DAYOFWEEK()。如果星期天则加 0,如果星期六则加 0.5,否则加 1。
DROP FUNCTION IF EXISTS `count_days_goal`;
DELIMITER $$
CREATE FUNCTION `count_days_goal`(start_date DATE, end_date DATE) RETURNS FLOAT
DETERMINISTIC
BEGIN
DECLARE sell_days FLOAT;
DECLARE dow INT;
SET sell_days = 0.0;
WHILE start_date <= end_date DO
SET dow = DAYOFWEEK(start_date);
IF (dow > 1) THEN
IF (dow = 7) THEN SET sell_days = sell_days + 0.5;
ELSE SET sell_days = sell_days + 1.0;
END IF;
END IF;
SET start_date = start_date + INTERVAL 1 DAY;
END WHILE;
RETURN (sell_days);
END$$
DELIMITER ;
一些测试(请注意,我的实施包括计算天数中的最后一天):
select count_days_goal(DATE('2016-03-21'), DATE('2016-03-27'));
select count_days_goal(DATE('2016-03-01'), DATE('2016-03-27'));
这很简单,但效率很低。天数越远,运行需要的时间越长。请注意,一个电话仍然比眨眼要快,但对大多数开发人员来说这会有点冒犯。幸运的是我们可以做得更好。
方法二:每个完整的一周是5.5个销售日。所以将日期之间的天数除以7,暂时忽略余数,乘以5.5。
例子:32天是(4周+4天),就是22个卖出天加上4个余数。
剩下的就是事情变得棘手的地方。这 4 天可能包括周六,也可能不包括。最简单的解决方案是在最后几天使用方法 1。它永远不会循环超过 6 天,所以它很快。
所以现在代码如下所示:
DROP FUNCTION IF EXISTS `count_days_goal`;
DELIMITER $$
CREATE FUNCTION `count_days_goal`(start_date DATE, end_date DATE) RETURNS FLOAT
DETERMINISTIC
BEGIN
DECLARE sell_days FLOAT;
DECLARE dow INT;
DECLARE whole_weeks INT;
SET whole_weeks = FLOOR(DATEDIFF(end_date, start_date)/7);
SET start_date = start_date + (7 * whole_weeks);
SET sell_days = whole_weeks * 5.5;
WHILE start_date <= end_date DO
SET dow = DAYOFWEEK(start_date);
IF (dow > 1) THEN
IF (dow = 7) THEN SET sell_days = sell_days + 0.5;
ELSE SET sell_days = sell_days + 1.0;
END IF;
END IF;
SET start_date = start_date + INTERVAL 1 DAY;
END WHILE;
RETURN (sell_days);
END$$
DELIMITER ;
方法 3: 您也可以使用方法 2,但要完全摆脱循环。要了解它是如何完成的,在您面前放一个日历会很有帮助。 (我没有费心实施这个;方法 2 非常好。)
不是循环,而是在第一个和最后一个日期调用 DAYOFWEEK
。我们称它们为 dow_start
和 dow_end
.
如果dow_start < dow_end
那么那些烦人的剩余日子都在一周之内,它们不会从周六回到周日。所以你需要做的就是:
SET answer = DATEDIFF(dow_end, dow_start) + 1;
IF dow_start = 1 (i.e. Sunday) THEN subtract 1 from answer
IF dow_end is 7 (i.e. Saturday) THEN subtract 0.5 from answer
如果 dow_start > dow_end
那么那些讨厌的剩余日子就会结束。我相信在那种情况下你可以交换 dow_start
和 dow_end
(让我们回到之前的情况)。像以前一样计算它,但然后从 5.5 中减去答案并加 1。(解释:计算一周中不在 day_start
和 day_end
之间的天数,然后从一周的总数中减去它们。)
就像我说的那样我没有编写或测试那个,所以它可能不完全正确。
如何处理银行假日: 如果您将它们放在 table 中,您可以简单地 COUNT
两个日期之间的行数。从你的答案中减去它。简单! (假设银行假期从不在周末。)有些网站列出了各个国家/地区的银行假期,甚至可以为您生成一份未来日期列表,从而轻松构建假期 table.