【问题标题】:Return value from DATE_SUB/DATE_ADD in stored function存储函数中 DATE_SUB/DATE_ADD 的返回值
【发布时间】:2025-12-12 21:55:01
【问题描述】:

我正在尝试编写一个存储函数,该函数采用正确的 ISO 格式 (yyyy-mm-dd) 并从中减去一定数量的工作日。基于this question here,我已经尝试了接受的答案以及一些不同的答案,但是,他们都只是在说如何编写纯sql,并没有一个例子函数。

我目前拥有的是这样的:

delimiter //
CREATE DEFINER=`root`@`localhost` FUNCTION `WEEKDATE_SUB` (days TINYINT, date_val VARCHAR(16))
RETURNS DATE DETERMINISTIC
BEGIN
    DECLARE SUBVAL INT;
    DECLARE dow INT;
    CASE
            WHEN dow=1 THEN SET SUBVAL = (days +(FLOOR((days-0.5)/5)+1)*2 - 1);
            WHEN dow=2 THEN SET SUBVAL = (days +(FLOOR((days-0.5)/5)+1)*2);
            WHEN dow=3 THEN SET SUBVAL = (days-1 +(FLOOR(((days-1)-0.5)/5)+1)*2 + 1);
            WHEN dow=4 THEN SET SUBVAL = (days-2 +(FLOOR(((days-2)-0.5)/5)+1)*2 + 2);
            WHEN dow=5 THEN SET SUBVAL = (days-3 +(FLOOR(((days-3)-0.5)/5)+1)*2 + 3);
            WHEN dow=6 THEN SET SUBVAL = (days-4 +(FLOOR(((days-4)-0.5)/5)+1)*2 + 4);
            WHEN dow=7 THEN SET SUBVAL = (days-5 +(FLOOR(((days-5)-0.5)/5)+1)*2 + 5);
    END CASE
    RETURN DATE_SUB(date_val, INTERVAL SUBVAL DAY);
END;//

当我尝试添加它时,我得到一个模糊的错误(因为 mysql 喜欢提供): 您的 SQL 语法有错误;查看与您的 MariaDB 服务器版本相对应的手册,了解在 'RETURN DATE_SUB(date_val, INTERVAL SUBVAL DAY); 附近使用的正确语法; END' 在第 14 行

我已经尝试了几种返回的变体,包括尝试为 Date sub 定义一个变量并返回它,但这几乎是相同的错误。

在函数之外,我知道这是可行的,所以看起来我应该能够返回它。

SELECT DATE_SUB("2016-01-01", INTERVAL 4 DAY);

【问题讨论】:

    标签: mysql sql sql-function


    【解决方案1】:

    文档建议您需要END CASE,而不是END

    此外,如果您使用 CASE expr WHEN value2 THEN .... WHEN value2 THEN ... END CASE 版本,存储的函数可能会执行得更快一些,因为它不必重复 DAYOFWEEK 函数调用可能 7 次。

    【讨论】:

    • 我给了你的建议。我很高兴你抓住了 DAYOFWEEK 的事情。但它仍然给出同样的错误。
    • 知道了。你让我走上正轨。除了 END CASE,我还需要一个 ;
    【解决方案2】:

    你也可以使用类似的东西

    delimiter //
    CREATE DEFINER=`root`@`localhost` FUNCTION `WEEKDATE_SUB` (date_val VARCHAR(10), days TINYINT)
    RETURNS VARCHAR(10) DETERMINISTIC
    BEGIN
        RETURN  date_val - INTERVAL
                FLOOR(days/5)*7 + 
                IF(DAYOFWEEK(date_val)-1 <= days - FLOOR(days/5)*5
                  , (days - FLOOR(days/5)*5)+2
                  , days - FLOOR(days/5)*5
               ) DAY;
    END;//
    

    样本

    mysql> SELECT WEEKDATE_SUB('2017-02-06',1);
    +------------------------------+
    | WEEKDATE_SUB('2017-02-06',1) |
    +------------------------------+
    | 2017-02-03                   |
    +------------------------------+
    1 row in set (0,00 sec)
    
    mysql> SELECT WEEKDATE_SUB('2017-02-07',1);
    +------------------------------+
    | WEEKDATE_SUB('2017-02-07',1) |
    +------------------------------+
    | 2017-02-06                   |
    +------------------------------+
    1 row in set (0,00 sec)
    
    mysql> SELECT WEEKDATE_SUB('2017-02-07',2);
    +------------------------------+
    | WEEKDATE_SUB('2017-02-07',2) |
    +------------------------------+
    | 2017-02-03                   |
    +------------------------------+
    1 row in set (0,00 sec)
    
    mysql>
    

    【讨论】:

      【解决方案3】:

      这是我最终需要的。注意,;以及我使用 CASE 语句的方式的变化。

      delimiter //
      CREATE DEFINER=`root`@`localhost` FUNCTION `WEEKDATE_SUB` (date_val VARCHAR(10), days TINYINT)
      RETURNS VARCHAR(10) DETERMINISTIC
      BEGIN
          DECLARE SUBVAL INT;
          DECLARE dow INT;
          SET dow = DAYOFWEEK(date_val);
          CASE dow
                  WHEN 1 THEN SET SUBVAL = (days   +(FLOOR((days-0.5)/5)+1)*2 - 1);
                  WHEN 2 THEN SET SUBVAL = (days   +(FLOOR((days-0.5)/5)+1)*2);
                  WHEN 3 THEN SET SUBVAL = (days-1 +(FLOOR(((days-1)-0.5)/5)+1)*2 + 1);
                  WHEN 4 THEN SET SUBVAL = (days-2 +(FLOOR(((days-2)-0.5)/5)+1)*2 + 2);
                  WHEN 5 THEN SET SUBVAL = (days-3 +(FLOOR(((days-3)-0.5)/5)+1)*2 + 3);
                  WHEN 6 THEN SET SUBVAL = (days-4 +(FLOOR(((days-4)-0.5)/5)+1)*2 + 4);
                  WHEN 7 THEN SET SUBVAL = (days-5 +(FLOOR(((days-5)-0.5)/5)+1)*2 + 5);
                  ELSE SET SUBVAL = days;
          END CASE;
          RETURN DATE_SUB(date_val, INTERVAL SUBVAL DAY);
      END;//
      

      【讨论】: