我需要编写一个 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
匹配逗号分隔的字符串不使用任何索引,因此性能很差,因此不是有效的解决方案。 - 使用
PREPARE
、EXECUTE
、CONCAT
等创建准备好的 SQL 语句感觉很脆弱,而且性能也不是很好。首先,如果与字符串匹配,我需要处理在值周围添加引号。而且我还假设每次运行存储过程时都必须重新创建查询计划? - 尝试将 CSV 值拆分到临时表中,然后使用子选择确实可以工作,但感觉非常hacky。另外,当您尝试将其分离到存储过程中时,您无法从存储过程中返回表/行;相反,您必须记住临时表名称并首先调用存储过程。其扩展范围仅限于一列。
当我说我花了几个小时研究这个问题但没有结果时,请相信我。如何在 MySQL 中实现这一目标,或者它根本就不是为这种存储过程设计的?
最佳答案
我尝试了一些方法:
使用常规的
OR
句子(与您的存储过程相同)我获得了一些性能,但编写 600 次比较将是一场噩梦。 (0.023s - 137K 随机记录)使用临时表。
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/