MySQL:计算月中剩余的销售天数

时间:2016-03-25 22:35:24

标签: mysql sql datediff date-difference

我有一个每日收入目标计算,说明我每天需要多少收入来实现每月收入目标:

'Revenue_Goal' / 'Selling_Days'

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

我想要完成的是根据已经过了多少天来获得调整后的每日目标。我部分完成了这个:

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

然而,我无法弄清楚的部分只是计算调整公式中的卖出天数。我需要使用上面的公式(或类似的东西),只计算几天:

  • 周一至周五= 1个销售日
  • 星期六= 0.5销售日
  • 星期日= 0

所以,如果今天是3/25/16,我的DATEDIFF计算将返回6,但我的目标是让它返回4.5。

感谢任何帮助!

2 个答案:

答案 0 :(得分:2)

这是计算此方法的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;

答案 1 :(得分:1)

将此作为存储函数编写是有意义的。我可以想到三种方法,每种方法都有所改进(虽然方法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')); 

这很简单,但效率低下。这些日子越来越远,需要更长的时间。请注意,一个电话仍然会比一个眨眼更快,但对大多数开发人员来说,它会略微冒犯。幸运的是,我们可以做得更好。

方法2:每个完整的一周是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.(说明:计算不在{​​{1}}和day_start之间的星期几,然后从一周中减去它们总)

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

如何处理银行假期:如果您将它们放在一个表格中,您只需day_end两个日期之间的行数。从你的答案中减去那个。简单! (假设银行假期永远不会在周末。)有些网站列出了各个国家的银行假期,甚至可以为将来生成日期列表的网站,这样可以轻松构建假期表。