2011-11-16 41 views
40

我需要传递一个字符串数组作为参数到一个MySQL存储例程。该数组可能很长,其元素数量不固定。然后我想将字符串值放入一列内存表中,这样我就可以处理数据。我不知道这是否可以在MySQL中完成。也许肮脏的解决方法是必要的。传递数组到MySQL存储例程

例如,我有串香蕉苹果。现在我想从我的MySQL Fruits表中获取这些水果的数据。伪代码:

create function GetFruits(Array fruitArray) 
    declare @temp table as 
     fruitName varchar(100) 
    end 

    @temp = convert fruitArray to table 
    select * from Fruits where Name in (select fruitName from @temp) 
end 

微软SQL Server允许您使用TEXT数据类型,并提交阵列作为XML字符串,迅速建立在内存中的表。但是,我不认为这种技术在MySQL中是可行的。

任何帮助如何做到这一点,将不胜感激!

回答

55

您可以传递一个字符串与您的列表并使用prepared statements来运行查询,例如, -

DELIMITER $$ 

CREATE PROCEDURE GetFruits(IN fruitArray VARCHAR(255)) 
BEGIN 

    SET @sql = CONCAT('SELECT * FROM Fruits WHERE Name IN (', fruitArray, ')'); 
    PREPARE stmt FROM @sql; 
    EXECUTE stmt; 
    DEALLOCATE PREPARE stmt; 

END 
$$ 

DELIMITER ; 

如何使用:我拿出我的问题的尴尬,但实用的解决方案

SET @fruitArray = '\'apple\',\'banana\''; 
CALL GetFruits(@fruitArray); 
+18

请问这种方法容易受到SQL注入攻击? – pixelfreak

+1

我认为这与执行简单的INSERT,SELECT或其他命令相同。 – Devart

+2

这是哈克。我会说要使用临时表的连接。 – bobobobo

2

。它适用于一维阵列(多个维度将是棘手的),并装配到一个varchar输入:

declare pos int;   -- Keeping track of the next item's position 
    declare item varchar(100); -- A single item of the input 
    declare breaker int;  -- Safeguard for while loop 

    -- The string must end with the delimiter 
    if right(inputString, 1) <> '|' then 
    set inputString = concat(inputString, '|'); 
    end if; 

    DROP TABLE IF EXISTS MyTemporaryTable; 
    CREATE TEMPORARY TABLE MyTemporaryTable (columnName varchar(100)); 
    set breaker = 0; 

    while (breaker < 2000) && (length(inputString) > 1) do 
    -- Iterate looking for the delimiter, add rows to temporary table. 
    set breaker = breaker + 1; 
    set pos = INSTR(inputString, '|'); 
    set item = LEFT(inputString, pos - 1); 
    set inputString = substring(inputString, pos + 1); 
    insert into MyTemporaryTable values(item); 
    end while; 

例如,对于这个码输入可以是字符串Apple|Banana|OrangeMyTemporaryTable将分别填充有三行,分别包含字符串Apple,BananaOrange

我认为字符串处理速度慢会使这种方法无用,但速度足够快(对于1,000个条目数组只有几分之一秒)。

希望这有助于某人。

+0

太多的字符串黑客入侵! – bobobobo

19

只需使用FIND_IN_SET这样的:

mysql> SELECT FIND_IN_SET('b','a,b,c,d'); 
     -> 2 

所以你可以做:

select * from Fruits where FIND_IN_SET(fruit, fruitArray) > 0 
+1

唯一的缺点是它比存储过程选项 – kellogs

+1

慢@kellogs - 你是什么意思?你可以把它放在存储过程 –

+0

我不清楚,对不起。这不是关于SP与非SP,而是关于WHERE IN与FIND_IN_SET。第一种方法是赢家,不管是否在SP中。 – kellogs

1

这模拟的字符数组,但你可以替换SUBSTR为ELT来模拟一个字符串数组

declare t_tipos varchar(255) default 'ABCDE'; 
declare t_actual char(1); 
declare t_indice integer default 1; 
while t_indice<length(t_tipos)+1 do 
    set t_actual=SUBSTR(t_tipos,t_indice,1); 
     select t_actual; 
     set t_indice=t_indice+1; 
end while; 
+0

不知道'ELT()'函数。但是,你如何声明字符串的“数组”变量,在这种情况下是't_tipos'?如何指定三串香蕉,苹果,橙子? – Gruber

9

使用与临时表的联接。您不需要将临时表传递给函数they are global

create temporary table ids(id int) ; 
insert into ids values (1),(2),(3) ; 

delimiter // 
drop procedure if exists tsel // 
create procedure tsel() -- uses temporary table named ids. no params 
READS SQL DATA 
BEGIN 
    -- use the temporary table `ids` in the SELECT statement or 
    -- whatever query you have 
    select * from Users INNER JOIN ids on userId=ids.id ; 
END // 
DELIMITER ; 

CALL tsel() ; -- call the procedure 
3

如果你不想在这里使用临时表是分割字符串像功能,您可以使用

SET @Array = 'one,two,three,four'; 
SET @ArrayIndex = 2; 
SELECT CASE 
    WHEN @Array REGEXP CONCAT('((,).*){',@ArrayIndex,'}') 
    THEN SUBSTRING_INDEX(SUBSTRING_INDEX(@Array,',',@ArrayIndex+1),',',-1) 
    ELSE NULL 
END AS Result; 
  • SUBSTRING_INDEX(string, delim, n)返回前n
  • SUBSTRING_INDEX(string, delim, -1)返回最后只能
  • REGEXP '((delim).*){n}'检查是否有n个分隔符(即您处于边界内)
7

这有助于为我做的条件 希望这将帮助你..

CREATE PROCEDURE `test`(IN Array_String VARCHAR(100)) 
BEGIN 
    SELECT * FROM Table_Name 
    WHERE FIND_IN_SET(field_name_to_search, Array_String); 

END//; 

呼叫:

call test('3,2,1');