【问题标题】:Dynamic query in stored procedure with a Date parameter in MySQLMySQL中带有日期参数的存储过程中的动态查询
【发布时间】:2018-04-10 19:43:13
【问题描述】:

我正在 MySQL 中编写一个带有动态查询的存储过程,如下所示:

DROP PROCEDURE IF EXISTS test;
DELIMITER //
CREATE PROCEDURE test(
IN STATUS VARCHAR(20),
IN CREATED_ON1 VARCHAR(10)
) 
BEGIN

SET @query="SELECT STATUS, COUNT(*) FROM CONTROL WHERE ID=ID";

IF STATUS IS NOT NULL THEN
 SET @query=CONCAT(@query, " AND STATUS='",STATUS,"'");
END IF;

IF CREATED_ON1 IS NOT NULL THEN
 SET @query=CONCAT(@query, " AND DATE_FORMAT(CREATED_ON,'%d-%m-%Y')>='", 
CREATED_ON1, "'");
END IF;

SET @query=CONCAT(@query, " GROUP BY STATUS");

PREPARE stmt FROM @query;
EXECUTE stmt; 
DEALLOCATE PREPARE stmt;

END //
DELIMITER ;

我打算用如下所示的签名重写这个过程。这将为比较日期提供正确的逻辑。

DROP PROCEDURE IF EXISTS test;
DELIMITER //
CREATE PROCEDURE test(
IN STATUS VARCHAR(20),
IN CREATED_ON1 DATE
)

主要问题是;如何将带有日期的谓词连接到@query?

【问题讨论】:

    标签: java mysql stored-procedures jpa-2.1


    【解决方案1】:

    请注意,您不必使用动态 SQL。使用普通的存储过程将使您的代码更具可读性和更易于维护:

    CREATE PROCEDURE test(
    IN_STATUS VARCHAR(20),
    IN_CREATED_ON DATE
    ) 
    BEGIN
    
    SELECT STATUS, COUNT(*) 
    FROM CONTROL 
    WHERE STATUS=IFNULL(IN_STATUS, STATUS) AND CREATED_ON>=IFNULL(IN_CREATED_ON,CREATED_ON)
    GROUP BY STATUS;
    
    END
    

    假设您的 STATUS 和 CREATED_ON 将始终包含值。

    【讨论】:

    • 您正在使用 IFNULL 函数隐式构建查询。它运行良好且简洁。
    • @slaakso - 非常巧妙的技巧 - 总是喜欢学习新知识。问题:你知道当NULL 被传递时优化器是否会去掉多余的WHERE 部分?
    • 不太可能。即使变量包含值,将函数与比较操作一起使用也可能会影响优化器索引选择决策。
    • 对于大数据和优化器,更好的 WHERE 子句可能是“WHERE(IN_STATUS 为空或(IN_STATUS 不为空且 STATUS = IN_STATUS))和(IN_CREATED_ON 为空或(IN_CREATED_ON 为不为空且 CREATED_ON=IN_CREATED_ON))”。 MySQL 优化器还有改进的余地。
    【解决方案2】:

    尝试使用 CASE 并传递参数:

    DROP PROCEDURE IF EXISTS test;
    DELIMITER //
    CREATE PROCEDURE test(
    IN IN_STATUS VARCHAR(20),
    IN IN_CREATED_ON1 DATE
    )
    
    BEGIN
    SET @v_and    = ' AND ';
    SET @v_where  = ' WHERE ';
    SET @v_query  = 'SELECT STATUS, COUNT(*) FROM CONTROL';
    SET @v_status = ' STATUS=? ';
    SET @v_date   = ' CREATED_ON >= ? ';
    SET @v_group  = ' GROUP BY STATUS';
    
    CASE 
    
    WHEN IN_STATUS IS NOT NULL && IN_CREATED_ON1 IS NOT NULL THEN
        SET @v_query = CONCAT(@v_query,@v_where,@v_status,@v_and,@v_date,@v_group);
        PREPARE stmt FROM @v_query;
        EXECUTE stmt USING @IN_STATUS, @IN_CREATED_ON1; 
    WHEN IN_STATUS IS NOT NULL THEN
        SET @v_query = CONCAT(@v_query,@v_where,@v_status,@v_group);
        PREPARE stmt FROM @v_query;
        EXECUTE stmt USING @IN_STATUS; 
    WHEN IN_CREATED_ON1 IS NOT NULL THEN
        SET @v_query = CONCAT(@v_query,@v_where,@v_date,@v_group);
        PREPARE stmt FROM @v_query;
        EXECUTE stmt USING @IN_CREATED_ON1; 
    ELSE
        SET @v_query = CONCAT (@v_query,@v_group);
        PREPARE stmt FROM @v_query;
        EXECUTE stmt; 
    END CASE;
    
    DEALLOCATE PREPARE stmt;
    END//
    
    DELIMITER ;
    

    【讨论】:

    • 对不起,我测试的时候运行不好。 ` call test("REALISED",STR_TO_DATE("2017-10-01", "%Y-%m-%d")); ERROR 1064 (42000):您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以了解在 'WHEN STATUS=? 附近使用的正确语法。和 CREATED_ON >= ? GROUP BY STATUS' 在第 1 行`
    猜你喜欢
    • 2018-01-27
    • 2023-03-26
    • 2014-06-04
    • 1970-01-01
    • 2014-11-15
    • 2014-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多