MySQL:计算当月剩余销售天数

MySQL: Calculate Remaining Selling Days in Month

我有一个每日收入目标计算,其中说明我每天需要赚取多少收入才能实现每月收入目标:

'Revenue_Goal' / 'Selling_Days'

(注:'Selling_Days'是数据集中已有的一个整体。)

我想要完成的是根据过去的天数调整每日目标。我部分完成了这个:

('Revenue_Goal' - SUM('Revenue')) / DATEDIFF(MAX('Date'),NOW())

但是,我想不通的部分只是在调整后的公式中计算销售天数。我需要使用上面的公式(或类似的东西),它只计算天数:

所以,如果今天是 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_startdow_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_startdow_end (让我们回到之前的情况)。像以前一样计算它,但然后从 5.5 中减去答案并加 1。(解释:计算一周中不在 day_startday_end 之间的天数,然后从一周的总数中减去它们。)

就像我说的那样我没有编写或测试那个,所以它可能不完全正确。

如何处理银行假日: 如果您将它们放在 table 中,您可以简单地 COUNT 两个日期之间的行数。从你的答案中减去它。简单! (假设银行假期从不在周末。)有些网站列出了各个国家/地区的银行假期,甚至可以为您生成一份未来日期列表,从而轻松构建假期 table.