把它写成一个存储函数是有意义的。我能想到三种方法,每种方法都比前一种方法有所改进(尽管方法 2 可能已经足够好了。)
方法一:从第一个日期开始,直到第二个日期递增。对于每个日期,调用 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_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 之间的天数,然后从一周的总数中减去它们。)
就像我说的那样,我没有编写或测试那个,所以它可能不完全正确。
如何处理银行假日:如果您将它们放在一个表格中,您可以简单地COUNT 两个日期之间的行数。从你的答案中减去。简单的! (假设银行假期从不在周末。)有些网站列出了各个国家/地区的银行假期,甚至有一个网站会为您生成一个遥远的未来日期列表,从而可以轻松构建假期表。