MySQL appears MySQL没有数组变量。我应该用什么呢?
似乎有两种选择建议:设置类型标量和临时表。我联系的问题暗示了前者。但是使用这些而不是数组变量是一种好习惯吗?或者,如果我使用集合,那么与foreach
相当的基于集合的习惯用法是什么?
答案 0 :(得分:61)
好吧,我一直在使用临时表而不是数组变量。不是最好的解决方案,但它确实有效。
请注意,您无需正式定义其字段,只需使用SELECT:
创建它们CREATE TEMPORARY TABLE IF NOT EXISTS my_temp_table
SELECT first_name FROM people WHERE last_name = 'Smith';
(另见Create temporary table from select statement without using Create Table。)
答案 1 :(得分:36)
您可以使用WHILE
循环在
SET @myArrayOfValue = '2,5,2,23,6,';
WHILE (LOCATE(',', @myArrayOfValue) > 0)
DO
SET @value = ELT(1, @myArrayOfValue);
SET @myArrayOfValue= SUBSTRING(@myArrayOfValue, LOCATE(',',@myArrayOfValue) + 1);
INSERT INTO `EXEMPLE` VALUES(@value, 'hello');
END WHILE;
编辑:
或者,您可以使用UNION ALL
:
INSERT INTO `EXEMPLE`
(
`value`, `message`
)
(
SELECT 2 AS `value`, 'hello' AS `message`
UNION ALL
SELECT 5 AS `value`, 'hello' AS `message`
UNION ALL
SELECT 2 AS `value`, 'hello' AS `message`
UNION ALL
...
);
答案 2 :(得分:19)
尝试使用MySql的FIND_IN_SET()函数 例如
SET @c = 'xxx,yyy,zzz';
SELECT * from countries
WHERE FIND_IN_SET(countryname,@c);
注意:如果您使用CSV值传递参数,则不必在StoredProcedure中设置变量。
答案 3 :(得分:15)
不了解数组,但有一种方法可以在普通的VARCHAR列中存储以逗号分隔的列表。
当您需要在该列表中找到某些内容时,您可以使用FIND_IN_SET()函数。
答案 4 :(得分:9)
现在使用 JSON数组将是一个明显的答案。
由于这是一个古老但仍然相关的问题,我提出了一个简短的例子。 自mySQL 5.7.x / MariaDB 10.2.3
以来,JSON函数可用我比ELT()更喜欢这个解决方案,因为它更像是一个数组,这个'数组'可以在代码中重用。
但要小心:它(JSON)肯定比使用临时表慢得多。它更方便。伊莫。
以下是如何使用JSON数组:
SET @myjson = '["gmail.com","mail.ru","arcor.de","gmx.de","t-online.de",
"web.de","googlemail.com","freenet.de","yahoo.de","gmx.net",
"me.com","bluewin.ch","hotmail.com","hotmail.de","live.de",
"icloud.com","hotmail.co.uk","yahoo.co.jp","yandex.ru"]';
SELECT JSON_LENGTH(@myjson);
-- result: 19
SELECT JSON_VALUE(@myjson, '$[0]');
-- result: gmail.com
这里有一个小例子来说明它在函数/过程中是如何工作的:
DELIMITER //
CREATE OR REPLACE FUNCTION example() RETURNS varchar(1000) DETERMINISTIC
BEGIN
DECLARE _result varchar(1000) DEFAULT '';
DECLARE _counter INT DEFAULT 0;
DECLARE _value varchar(50);
SET @myjson = '["gmail.com","mail.ru","arcor.de","gmx.de","t-online.de",
"web.de","googlemail.com","freenet.de","yahoo.de","gmx.net",
"me.com","bluewin.ch","hotmail.com","hotmail.de","live.de",
"icloud.com","hotmail.co.uk","yahoo.co.jp","yandex.ru"]';
WHILE _counter < JSON_LENGTH(@myjson) DO
-- do whatever, e.g. add-up strings...
SET _result = CONCAT(_result, _counter, '-', JSON_VALUE(@myjson, CONCAT('$[',_counter,']')), '#');
SET _counter = _counter + 1;
END WHILE;
RETURN _result;
END //
DELIMITER ;
SELECT example();
答案 5 :(得分:4)
DELIMITER $$
CREATE DEFINER=`mysqldb`@`%` PROCEDURE `abc`()
BEGIN
BEGIN
set @value :='11,2,3,1,';
WHILE (LOCATE(',', @value) > 0) DO
SET @V_DESIGNATION = SUBSTRING(@value,1, LOCATE(',',@value)-1);
SET @value = SUBSTRING(@value, LOCATE(',',@value) + 1);
select @V_DESIGNATION;
END WHILE;
END;
END$$
DELIMITER ;
答案 6 :(得分:3)
如果需要关联数组,可能会创建一个包含列(键,值)的临时内存表。有一个内存表是最接近mysql中的数组
答案 7 :(得分:3)
我是这样做的。
首先,我创建了一个函数,用于检查由逗号分隔的值列表中的Long / Integer / any值是否为:
CREATE DEFINER = 'root'@'localhost' FUNCTION `is_id_in_ids`(
`strIDs` VARCHAR(255),
`_id` BIGINT
)
RETURNS BIT(1)
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
DECLARE strLen INT DEFAULT 0;
DECLARE subStrLen INT DEFAULT 0;
DECLARE subs VARCHAR(255);
IF strIDs IS NULL THEN
SET strIDs = '';
END IF;
do_this:
LOOP
SET strLen = LENGTH(strIDs);
SET subs = SUBSTRING_INDEX(strIDs, ',', 1);
if ( CAST(subs AS UNSIGNED) = _id ) THEN
-- founded
return(1);
END IF;
SET subStrLen = LENGTH(SUBSTRING_INDEX(strIDs, ',', 1));
SET strIDs = MID(strIDs, subStrLen+2, strLen);
IF strIDs = NULL or trim(strIds) = '' THEN
LEAVE do_this;
END IF;
END LOOP do_this;
-- not founded
return(0);
END;
现在,您可以在以逗号分隔的ID列表中搜索ID,如下所示:
select `is_id_in_ids`('1001,1002,1003',1002);
你可以在WHERE子句中使用这个函数,如下所示:
SELECT * FROM table1 WHERE `is_id_in_ids`('1001,1002,1003',table1_id);
这是我发现传递&#34;阵列的唯一方法。参数到程序。
答案 8 :(得分:1)
这是我使用包含元素列表的变量的解决方案。 您可以在简单查询中使用它(无需使用存储过程或创建表)。
我在网站的其他地方找到了使用 JSON_TABLE 函数的技巧(它在 mysql 8 中有效,我不知道它在其他版本中是否有效)。
set @x = '1,2,3,4' ;
select c.NAME
from colors c
where
c.COD in (
select *
from json_table(
concat('[',@x,']'),
'$[*]' columns (id int path '$') ) t ) ;
此外,您可能需要管理一个或多个变量设置为 empty_string 的情况。 在这种情况下,我添加了另一个技巧(即使 x、y 或 x 和 y 都是空字符串,查询也不会返回错误) :
set @x = '' ;
set @y = 'yellow' ;
select c.NAME
from colors
where
if(@y = '', 1 = 1, c.NAME = @y)
and if(@x = '', 1, c.COD) in (
select *
from json_table(
concat('[',if(@x = '', 1, @x),']'),
'$[*]' columns (id int path '$') ) t) ;
答案 9 :(得分:1)
查看相同问题的另一种方法。 希望有帮助
DELIMITER $$
CREATE PROCEDURE ARR(v_value VARCHAR(100))
BEGIN
DECLARE v_tam VARCHAR(100);
DECLARE v_pos VARCHAR(100);
CREATE TEMPORARY TABLE IF NOT EXISTS split (split VARCHAR(50));
SET v_tam = (SELECT (LENGTH(v_value) - LENGTH(REPLACE(v_value,',',''))));
SET v_pos = 1;
WHILE (v_tam >= v_pos)
DO
INSERT INTO split
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(v_value,',',v_pos),',', -1);
SET v_pos = v_pos + 1;
END WHILE;
SELECT * FROM split;
DROP TEMPORARY TABLE split;
END$$
CALL ARR('1006212,1006404,1003404,1006505,444,');
答案 10 :(得分:1)
数组的意义不是很有效吗?如果您只是遍历值,那么我认为临时(或永久)表上的游标比查找逗号更有意义,不是吗?也更干净。查找“ mysql DECLARE CURSOR”。
对于随机访问,带有数字索引主键的临时表。不幸的是,您获得的最快访问权限是哈希表,而不是真正的随机访问。
答案 11 :(得分:1)
我知道这是一个迟到的回应,但我最近不得不解决类似的问题,并认为这可能对其他人有用。
<强>背景强>
请考虑以下表格,其中名为&#39; mytable&#39;:
问题是只保留最新的3条记录并删除systemid = 1的任何旧记录(表中可能有许多其他记录与其他systemid值)
如果您只使用声明
就可以做到这一点DELETE FROM mytable WHERE id IN (SELECT id FROM `mytable` WHERE systemid=1 ORDER BY id DESC LIMIT 3)
然而,MySQL尚未支持此功能,如果您尝试此操作,则会出现类似
的错误...doesn't yet support 'LIMIT & IN/ALL/SOME subquery'
因此需要一种解决方法,即使用变量将值数组传递给IN选择器。但是,由于变量需要是单个值,我需要模拟数组。 诀窍是将数组创建为逗号分隔的值列表(字符串),并将其分配给变量,如下所示
SET @myvar := (SELECT GROUP_CONCAT(id SEPARATOR ',') AS myval FROM (SELECT * FROM `mytable` WHERE systemid=1 ORDER BY id DESC LIMIT 3 ) A GROUP BY A.systemid);
存储在@myvar中的结果是
5,6,7
接下来,FIND_IN_SET选择器用于从模拟数组中选择
SELECT * FROM mytable WHERE FIND_IN_SET(id,@myvar);
综合最终结果如下:
SET @myvar := (SELECT GROUP_CONCAT(id SEPARATOR ',') AS myval FROM (SELECT * FROM `mytable` WHERE systemid=1 ORDER BY id DESC LIMIT 3 ) A GROUP BY A.systemid);
DELETE FROM mytable WHERE FIND_IN_SET(id,@myvar);
我知道这是一个非常具体的案例。但是,它可以被修改以适应变量需要存储值数组的任何其他情况。
我希望这会有所帮助。
答案 12 :(得分:1)
使用集合的两个版本都不适用于我(使用MySQL 5.5测试)。函数ELT()返回整个集合。考虑到WHILE语句仅在PROCEDURE上下文中可用,我将其添加到我的解决方案中:
DROP PROCEDURE IF EXISTS __main__;
DELIMITER $
CREATE PROCEDURE __main__()
BEGIN
SET @myArrayOfValue = '2,5,2,23,6,';
WHILE (LOCATE(',', @myArrayOfValue) > 0)
DO
SET @value = LEFT(@myArrayOfValue, LOCATE(',',@myArrayOfValue) - 1);
SET @myArrayOfValue = SUBSTRING(@myArrayOfValue, LOCATE(',',@myArrayOfValue) + 1);
END WHILE;
END;
$
DELIMITER ;
CALL __main__;
说实话,我认为这不是一个好习惯。即使它真的有必要,这几乎是不可读的,也很慢。
答案 13 :(得分:1)
这适用于值列表:
SET @myArrayOfValue = '2,5,2,23,6,';
WHILE (LOCATE(',', @myArrayOfValue) > 0)
DO
SET @value = ELT(1, @myArrayOfValue);
SET @STR = SUBSTRING(@myArrayOfValue, 1, LOCATE(',',@myArrayOfValue)-1);
SET @myArrayOfValue = SUBSTRING(@myArrayOfValue, LOCATE(',', @myArrayOfValue) + 1);
INSERT INTO `Demo` VALUES(@STR, 'hello');
END WHILE;
答案 14 :(得分:0)
受ELT函数(索引号,string1,string2,string3,...)的启发,我认为以下示例可用作数组示例:
set @i := 1;
while @i <= 3
do
insert into table(val) values (ELT(@i ,'val1','val2','val3'...));
set @i = @i + 1;
end while;
希望有所帮助。
答案 15 :(得分:0)
在5.7.x之后的MYSQL版本中,您可以使用JSON类型来存储数组。您可以通过MYSQL通过键获取数组的值。
答案 16 :(得分:0)
我很惊讶没有答案提到ELT / FIELD。
ELT / FIELD的工作原理与数组非常相似,尤其是在您具有静态数据的情况下。
FIND_IN_SET也可以类似,但是没有内置的补充 功能,但编写一个就足够简单了。
mysql> select elt(2,'AA','BB','CC');
+-----------------------+
| elt(2,'AA','BB','CC') |
+-----------------------+
| BB |
+-----------------------+
1 row in set (0.00 sec)
mysql> select field('BB','AA','BB','CC');
+----------------------------+
| field('BB','AA','BB','CC') |
+----------------------------+
| 2 |
+----------------------------+
1 row in set (0.00 sec)
mysql> select find_in_set('CC','AA,BB,CC');
+------------------------------+
| find_in_set('BB','AA,BB,CC') |
+------------------------------+
| 2 |
+------------------------------+
1 row in set (0.00 sec)
mysql> SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('AA,BB,CC',',',2),',',-1);
+-----------------------------------------------------------+
| SUBSTRING_INDEX(SUBSTRING_INDEX('AA,BB,CC',',',2),',',-1) |
+-----------------------------------------------------------+
| BB |
+-----------------------------------------------------------+
1 row in set (0.01 sec)
答案 17 :(得分:0)
这是MySQL的一个示例,用于循环以逗号分隔的字符串。
global.school = () => {
faker.seed(123) // Seeding faker means you get the same details every time
return {
_id: faker.random.number(),
name: `${faker.address.city()} School`,
slug: faker.lorem.word(),
email: 'demo@ccc.me',
password: 'password'
}
}
这使用此处定义的const s = school() // Get a fresh object that looks like a school
函数:https://stackoverflow.com/a/59666211/3068233
答案 18 :(得分:0)
我问,因为我最初是在这里想要添加一个数组作为MySQL表变量的。我对数据库设计还比较陌生,并试图思考如何以一种典型的编程语言方式来完成它。
但是数据库不同。我认为我想要一个数组作为变量,但是事实证明,这并不是常见的MySQL数据库实践。
数组的另一种解决方案是添加一个附加表,然后用外键引用原始表。
作为一个例子,让我们想象一个应用程序,该应用程序跟踪一个家庭中每个人想要在商店购买的所有商品。
我最初设想的用于创建表的命令应如下所示:
#doesn't work
CREATE TABLE Person(
name VARCHAR(50) PRIMARY KEY
buy_list ARRAY
);
我想我将buy_list设想为以逗号分隔的一串项目或类似内容。
但是MySQL没有数组类型字段,所以我确实需要这样的东西:
CREATE TABLE Person(
name VARCHAR(50) PRIMARY KEY
);
CREATE TABLE BuyList(
person VARCHAR(50),
item VARCHAR(50),
PRIMARY KEY (person, item),
CONSTRAINT fk_person FOREIGN KEY (person) REFERENCES Person(name)
);
在这里,我们定义了一个名为fk_person的约束。它说BuyList中的“ person”字段是外键。换句话说,它是另一个表中的主键,特别是Person表中的“名称”字段,这是REFERENCES表示的。
我们还将人和项的组合定义为主键,但是从技术上讲这不是必需的。
最后,如果要获取某人列表中的所有项目,则可以运行以下查询:
SELECT item FROM BuyList WHERE person='John';
这为您提供了约翰列表中的所有项目。不需要数组!
答案 19 :(得分:0)
如果我们有一张这样的桌子
mysql> select * from user_mail;
+------------+-------+
| email | user |
+------------+-------+-
| email1@gmail | 1 |
| email2@gmail | 2 |
+------------+-------+--------+------------+
和数组表:
mysql> select * from user_mail_array;
+------------+-------+-------------+
| email | user | preferences |
+------------+-------+-------------+
| email1@gmail | 1 | 1 |
| email1@gmail | 1 | 2 |
| email1@gmail | 1 | 3 |
| email1@gmail | 1 | 4 |
| email2@gmail | 2 | 5 |
| email2@gmail | 2 | 6 |
我们可以使用CONCAT函数将第二个表的行选择为一个数组:
mysql> SELECT t1.*, GROUP_CONCAT(t2.preferences) AS preferences
FROM user_mail t1,user_mail_array t2
where t1.email=t2.email and t1.user=t2.user
GROUP BY t1.email,t1.user;
+------------+-------+--------+------------+-------------+
| email | user | preferences |
+------------+-------+--------+------------+-------------+
|email1@gmail | 1 | 1,3,2,4 |
|email2@gmail | 2 | 5,6 |
+------------+-------+--------+------------+-------------+
答案 20 :(得分:-1)
我想我可以改进这个答案。试试这个:
参数'Pranks'是CSV。即。 '1,2,3,4 .....等等'
CREATE PROCEDURE AddRanks(
IN Pranks TEXT
)
BEGIN
DECLARE VCounter INTEGER;
DECLARE VStringToAdd VARCHAR(50);
SET VCounter = 0;
START TRANSACTION;
REPEAT
SET VStringToAdd = (SELECT TRIM(SUBSTRING_INDEX(Pranks, ',', 1)));
SET Pranks = (SELECT RIGHT(Pranks, TRIM(LENGTH(Pranks) - LENGTH(SUBSTRING_INDEX(Pranks, ',', 1))-1)));
INSERT INTO tbl_rank_names(rank)
VALUES(VStringToAdd);
SET VCounter = VCounter + 1;
UNTIL (Pranks = '')
END REPEAT;
SELECT VCounter AS 'Records added';
COMMIT;
END;
这种方法使得循环的每次迭代都会逐渐缩短搜索到的CSV值字符串,我相信这对于优化会更好。
答案 21 :(得分:-2)
我会为多个集合尝试类似的方法。我是MySQL的初学者。很抱歉,函数名称无法确定最好的名称。
delimiter //
drop procedure init_
//
create procedure init_()
begin
CREATE TEMPORARY TABLE if not exists
val_store(
realm varchar(30)
, id varchar(30)
, val varchar(255)
, primary key ( realm , id )
);
end;
//
drop function if exists get_
//
create function get_( p_realm varchar(30) , p_id varchar(30) )
returns varchar(255)
reads sql data
begin
declare ret_val varchar(255);
declare continue handler for 1146 set ret_val = null;
select val into ret_val from val_store where id = p_id;
return ret_val;
end;
//
drop procedure if exists set_
//
create procedure set_( p_realm varchar(30) , p_id varchar(30) , p_val varchar(255) )
begin
call init_();
insert into val_store (realm,id,val) values (p_realm , p_id , p_val) on duplicate key update val = p_val;
end;
//
drop procedure if exists remove_
//
create procedure remove_( p_realm varchar(30) , p_id varchar(30) )
begin
call init_();
delete from val_store where realm = p_realm and id = p_id;
end;
//
drop procedure if exists erase_
//
create procedure erase_( p_realm varchar(30) )
begin
call init_();
delete from val_store where realm = p_realm;
end;
//
call set_('my_array_table_name','my_key','my_value');
select get_('my_array_table_name','my_key');
答案 22 :(得分:-2)
除了将数据保存为数组或一行之外,您应该为每个接收到的值创建不同的行。这将使理解起来比将它们放在一起要容易得多。
答案 23 :(得分:-4)
您是否尝试过使用PHP的serialize()? 这允许您将变量数组的内容存储在PHP理解的字符串中,并且对数据库是安全的(假设您已经先将其转义)。
$array = array(
1 => 'some data',
2 => 'some more'
);
//Assuming you're already connected to the database
$sql = sprintf("INSERT INTO `yourTable` (`rowID`, `rowContent`) VALUES (NULL, '%s')"
, serialize(mysql_real_escape_string($array, $dbConnection)));
mysql_query($sql, $dbConnection) or die(mysql_error());
您也可以在没有编号数组的情况下完成相同的操作
$array2 = array(
'something' => 'something else'
);
或
$array3 = array(
'somethingNew'
);