mysql - 如何在 MySQL 存储过程中选择与可变数量参数匹配的值

标签 mysql stored-procedures

我需要编写一个 MySQL 存储过程(从 .NET 调用)来搜索 stoppoints 表并允许我指定许多可能的 stopMode 值进行匹配。

换句话说:

CREATE PROCEDURE getActiveStoppoints(
IN NamePrefix VARCHAR(100),
IN StopModeMatch1 TINYINT(4),
IN StopModeMatch2 TINYINT(4),
IN StopModeMatch3 TINYINT(4),
IN StopModeMatch4 TINYINT(4),
IN StopModeMatch5 TINYINT(4)
)
BEGIN

-- Return all records matching
SELECT sp.* FROM stoppoints sp
WHERE (sp.name LIKE CONCAT(NamePrefix, '%')
AND
(
(sp.stopMode = StopModeMatch1) OR 
(sp.stopMode = StopModeMatch2) OR 
(sp.stopMode = StopModeMatch3) OR 
(sp.stopMode = StopModeMatch4) OR 
(sp.stopMode = StopModeMatch5) 
)
;

END

这种方法看起来非常脆弱 - 例如,如果我需要传入 6 个可能的 stopMode 值,甚至 600 个值怎么办?当我想以类似的方式匹配另外两列时会发生什么?

还有哪些其他可能的方法可以实现这一目标?例如,我可以将数组传递到存储过程中吗?

我最初通过在 VARCHAR 中传递以逗号分隔的值列表来尝试此操作。我最终对这种方法感到非常沮丧,因为:

  • 使用 FIND_IN_SET 匹配逗号分隔的字符串不使用任何索引,因此性能很差,因此不是有效的解决方案。
  • 使用 PREPAREEXECUTECONCAT 等创建准备好的 SQL 语句感觉很脆弱,而且性能也不是很好。首先,如果与字符串匹配,我需要处理在值周围添加引号。而且我还假设每次运行存储过程时都必须重新创建查询计划?
  • 尝试将 CSV 值拆分到临时表中,然后使用子选择确实可以工作,但感觉非常hacky。另外,当您尝试将其分离到存储过程中时,您无法从存储过程中返回表/行;相反,您必须记住临时表名称并首先调用存储过程。其扩展范围仅限于一列。

当我说我花了几个小时研究这个问题但没有结果时,请相信我。如何在 MySQL 中实现这一目标,或者它根本就不是为这种存储过程设计的?

最佳答案

我尝试了一些方法:

  1. 使用常规的 OR 句子(与您的存储过程相同)我获得了一些性能,但编写 600 次比较将是一场噩梦。 (0.023s - 137K 随机记录)

  2. 使用临时表。

    CREATE PROCEDURE getActiveStoppoints(
        IN NamePrefix VARCHAR(100),
        IN StopModeMatch1 TINYINT(4),
        IN StopModeMatch2 TINYINT(4),
        IN StopModeMatch3 TINYINT(4),
        IN StopModeMatch4 TINYINT(4),
        IN StopModeMatch5 TINYINT(4)
    )
    BEGIN
    
        -- drop table tempValues;
        create temporary table if not exists tempValues(
            stoppoint int not null primary key
        ) engine=memory;
        truncate tempValues;
        insert ignore into  tempValues(stoppoint) values (StopModeMatch1);
        insert ignore into  tempValues(stoppoint) values (StopModeMatch2);
        insert ignore into  tempValues(stoppoint) values (StopModeMatch3);
        insert ignore into  tempValues(stoppoint) values (StopModeMatch4);
        insert ignore into  tempValues(stoppoint) values (StopModeMatch5);
    
        -- Return all records matching
        SELECT count(*) -- sp.* 
        FROM stoppoints sp
        WHERE sp.name LIKE CONCAT(NamePrefix, '%')
        and sp.stopMode in (select stoppoint from tempValues);
    
    END$$
    

这适用于 600 多个值,但过程参数有限。 也许使用常规表并在过程之外插入每个(600+)值并运行。 (0.24s - 137K 随机记录)

  • 使用准备好的语句:

    CREATE PROCEDURE getActiveStoppoints(
        IN NamePrefix VARCHAR(100),
        in stopDelimited varchar(255)
    )
    BEGIN
        set @sql = concat("SELECT count(*) FROM stoppoints sp WHERE sp.name LIKE '",NamePrefix,"%'");
        set @sql = concat(@sql," and sp.stopMode in (", stopDelimited ,")" );
    
        PREPARE stmt1 FROM @sql;
        EXECUTE stmt1;
    END$$
    delimiter ;
    
  • 我认为这是最好的解决方案,因为不使用临时表,执行时间与第一种方法相同,并且第二个参数可以配置为接收 600+ csv。

    关于mysql - 如何在 MySQL 存储过程中选择与可变数量参数匹配的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35559823/

    相关文章:

    c# - Entity Framework : Unable to call stored procedure,错误的代码生成

    mysql - 在 mysql 的过程范围内设置事务隔离级别

    sql-server - 以表名作为参数的存储过程

    mysql - 从不同的 Schemas MySQL 调用过程

    mysql - 一张表的数据库选择查询逻辑

    MySql 完全连接(联合)并在多个日期列上排序

    Php 和 AngularJs - 获取数据

    mysql - 触发器声明内的局部变量存在语法错误

    mysql - ruby on Rails before_save 增加表的不同列

    MySQL 复制 : failover scenario