如何在MySQL中模拟数组变量?

How can I simulate an array variable in MySQL?

提问人:einpoklum 提问时间:8/29/2012 最后编辑:Communityeinpoklum 更新时间:6/3/2022 访问量:441934

问:

MySQL似乎没有数组变量。我应该用什么来代替?


似乎提出了两种替代方案:集合型标量临时表。我链接到的问题暗示了前者。但是,使用这些变量而不是数组变量是好的做法吗?或者,如果我使用集合,基于集合的成语相当于什么?foreach

mysql 数组变量 set temp-tables

评论


答:

49赞 Omesh 8/29/2012 #1

您可以在 MySQL 中使用循环来实现这一点: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
 ...
);

评论

4赞 einpoklum 8/29/2012
循环不是只在存储过程中才有可能吗?
2赞 Omesh 8/29/2012
是的,它可能在存储过程、函数和触发器中。
1赞 einpoklum 8/29/2012
所以我不能使用你提供的代码......我需要一些更普遍适用的东西。
0赞 Omesh 8/29/2012
您可以编写一个示例存储过程和它。CALL
1赞 Omesh 8/30/2012
我认为不需要数组。您可以使用临时表或不使用过程轻松完成。UNION ALL
19赞 wormhit 8/31/2012 #2

不知道数组,但有一种方法可以将逗号分隔的列表存储在普通的 VARCHAR 列中。

当您需要在该列表中查找内容时,您可以使用 FIND_IN_SET() 函数。

评论

0赞 Akshay Vishnoi 4/29/2013
如果我想在一个集合中找到子集,有什么办法吗?
0赞 wormhit 4/30/2013
不好意思!我不确定这是否可能。
98赞 einpoklum 12/22/2012 #3

好吧,我一直在使用临时表而不是数组变量。这不是最好的解决方案,但它有效。

请注意,您不需要正式定义它们的字段,只需使用 SELECT 创建它们:

DROP TEMPORARY TABLE IF EXISTS my_temp_table;
CREATE TEMPORARY TABLE my_temp_table
    SELECT first_name FROM people WHERE last_name = 'Smith';

(另请参阅在不使用 Create Table 的情况下从 select 语句创建临时表

评论

1赞 Igbanam 12/17/2014
哦:o我不知道SQL有这个!!这些表仅在正在运行的 al 查询范围内处于活动状态。整洁!
2赞 Pacerier 4/16/2015
@Yasky,前提是您不重复使用连接。因为它确实会持续整个会议。
4赞 einpoklum 5/30/2015
@John:是的,好吧,你可以重用它,但不能在同一个查询中。
1赞 Aurast 11/18/2020
如果要重用连接,通常应 DROP TEMPORARY TABLE IF EXISTS my_temp_table;在创建它之前。
-7赞 Magictallguy 3/23/2013 #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'
);

评论

8赞 einpoklum 3/24/2013
我没有使用PHP,所以这对我来说并不重要。
2赞 Rick James 1/30/2016
使用 JSON 而不是序列化。它更通用且与语言无关。
3赞 Pavle Lekic 8/1/2013 #5

如果您想要关联数组,可以创建一个带有列(键、值)的临时内存表。拥有内存表是最接近于 mysql 中拥有数组的事情

评论

0赞 einpoklum 8/1/2013
嗯,我不想要关联数组,只想要数组。
0赞 Pavle Lekic 8/2/2013
您可以使用只有一列的临时内存表,然后使用游标遍历值,这与在非声明式编程语言中使用数组和 for/while 循环最接近
0赞 einpoklum 8/3/2013
该语言实际上具有此功能,也就是说,没有语法原因,您不应该像在变量中选择标量一样选择向量。
2赞 user2664904 8/8/2013 #6

这适用于值列表:

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;
1赞 jmmeier 10/30/2013 #7

使用集合的两个版本都不适合我(使用 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__;

老实说,我不认为这是一个好的做法。即使它确实有必要,它也几乎不可读且速度很慢。

-2赞 user2288580 12/26/2013 #8

我想我可以改进这个答案。试试这个:

参数“恶作剧”是 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 值字符串随着循环的每次迭代而逐渐缩短,我认为这更适合优化。

评论

0赞 einpoklum 12/26/2013
你指的是什么“这个答案”?此外,您没有 CSV 文件。
0赞 user2288580 7/10/2020
我指的不是CSV文件,我指的是CSV值,例如“1,2,3,4...等等'
3赞 chuckedw 10/8/2014 #9

这是我是如何做到的。

首先,我创建了一个函数,用于检查用逗号分隔的值列表中是否存在 Long/Integer/whatever 值:

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);

这是我发现的将“数组”参数传递给 PROCEDURE 的唯一方法。

7赞 Sagar Gangwal 1/29/2016 #10
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 ;

评论

2赞 einpoklum 1/29/2016
请解释如何使用此代码以及它如何回答问题。
0赞 Sagar Gangwal 1/30/2016
在这里,您制作了一个简单的过程,该过程给出了该特定字符串的一个元素,这些元素在 oracle 中用作数组。
0赞 einpoklum 1/30/2016
神谕?这个问题与甲骨文无关。此外,您似乎正在过程中定义一个数组。
0赞 Sagar Gangwal 1/31/2016
请检查 Syntex 仅适用于 mysql
0赞 Eagle_Eye 3/6/2019
不要错过最后一个逗号!
0赞 nick darn 9/29/2016 #11

在 5.7.x 之后的 MYSQL 版本中,可以使用 JSON 类型来存储数组。您可以通过 MYSQL 通过键获取数组的值。

评论

9赞 einpoklum 9/29/2016
你能举个例子说明我该怎么做吗?
44赞 Himalaya Garg 2/14/2017 #12

尝试使用 MySql 的 FIND_IN_SET() 函数 例如

SET @c = 'xxx,yyy,zzz';

SELECT * from countries 
WHERE FIND_IN_SET(countryname,@c);

注意:如果要传递带有 CSV 值的参数,则不必在 StoredProcedure 中设置变量。

评论

0赞 Nemo 1/8/2018
注意长度限制,长度限制可能非常低:stackoverflow.com/q/2567000/1333493
0赞 Francesco 9/5/2022
我喜欢这个答案,因为我与删除查询结合使用 SET @Ids = 'e6cf8a49-0143-4286-9528-819ccd951c9e,cc34bde0-e361-4e57-a51c-ebdac0cd4bd6';从my_table中删除 其中 find_in_set(id, @Ids);
0赞 edward 2/2/2018 #13

受函数ELT(index number, 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;

希望对你有所帮助。

35赞 SeparateReality 2/16/2018 #14

如今,使用 JSON 数组将是一个显而易见的答案。

由于这是一个古老但仍然相关的问题,我举了一个简短的例子。 JSON 函数从 mySQL 5.7.x / MariaDB 10.2.3 开始可用

我更喜欢这个解决方案而不是 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();

评论

0赞 Kanagavelu Sugumar 7/20/2018
你是说ELT更慢还是JSON更慢?
2赞 SeparateReality 9/15/2018
@Kanagavelu Sugumar:在撰写本文时,JSON肯定比较慢。我编辑了答案以使其更清晰。
0赞 Tyler 6/17/2023
在 MySQL 8 中,对于 JSON_VALUE 函数,您不能使用变量代替第二个参数(即 JSON 路径)的字符串文字。在此处查看我的替代解决方案。
15赞 Clinton 5/18/2018 #15

我知道这有点晚了,但我最近不得不解决一个类似的问题,并认为这可能对其他人有用。

背景

请考虑下面名为“mytable”的表格:

Starting table

问题是只保留最新的 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);

我知道这是一个非常具体的案例。但是,可以对其进行修改以适应变量需要存储值数组的任何其他情况。

我希望这会有所帮助。

评论

2赞 Daniil 4/18/2022
你做得很好!多亏了你,我发现了大约 4 个新的 SQL 函数!
1赞 Clinton 4/19/2022
很高兴能够提供帮助。
1赞 Amaigus 9/27/2018 #16

数组的意义不在于高效吗?如果您只是遍历值,我认为临时(或永久)表上的光标比寻找逗号更有意义,不是吗?也更干净。查找“mysql DECLARE CURSOR”。

对于随机访问,具有数字索引主键的临时表。不幸的是,您将获得的最快访问是哈希表,而不是真正的随机访问。

评论

0赞 einpoklum 9/27/2018
这是评论,不是答案。我没有指出这就是我想要对数组执行的操作。
3赞 Jongab 11/3/2018 #17

令我惊讶的是,没有一个答案提到 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('BB','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)
-2赞 Dave 1/25/2019 #18

我会为多个集合尝试这样的事情。我是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');

评论

0赞 einpoklum 1/25/2019
我想我理解你的建议,但这非常笨拙,而且可能也非常慢......
0赞 Dave 1/25/2019
如果没有压力测试,我不能认可或驳回它。它基本上是在临时表(或普通表)上查找和插入主键。我会使用它,直到我遇到问题或找到更好的方法;但我做一些奇怪的事情,比如完全用 Oracle PL/SQL 编写编译器和游戏。
1赞 Walter Azevedo 9/28/2019 #19

另一种看待相同问题的方法。 希望对您有所帮助

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,');

评论

0赞 einpoklum 9/28/2019
实际上,不清楚你在这里说什么。7年前的这个问题有几个相关的答案。考虑删除您的答案或解释您想直接/笼统地告诉我们的内容,而不是通过示例。
0赞 Ulad Kasach 1/10/2020 #20

这是MySQL循环使用逗号分隔字符串的示例。

DECLARE v_delimited_string_access_index INT;
DECLARE v_delimited_string_access_value VARCHAR(255);
DECLARE v_can_still_find_values_in_delimited_string BOOLEAN;

SET v_can_still_find_values_in_delimited_string = true;
SET v_delimited_string_access_index = 0;
WHILE (v_can_still_find_values_in_delimited_string) DO
  SET v_delimited_string_access_value = get_from_delimiter_split_string(in_array, ',', v_delimited_string_access_index); -- get value from string
  SET v_delimited_string_access_index = v_delimited_string_access_index + 1;
  IF (v_delimited_string_access_value = '') THEN
    SET v_can_still_find_values_in_delimited_string = false; -- no value at this index, stop looping
  ELSE
    -- DO WHAT YOU WANT WITH v_delimited_string_access_value HERE
  END IF;
END WHILE;

这使用此处定义的函数:https://stackoverflow.com/a/59666211/3068233get_from_delimiter_split_string

评论

0赞 einpoklum 1/10/2020
逗号分隔的字符串已经有人提出 - 几年前。
0赞 Ulad Kasach 1/10/2020
@einpoklum是的 - 这是与他们互动的另一种方式
-3赞 chirag mathur 4/14/2020 #21

与其将数据保存为数组或仅在一行中保存数据,不如为收到的每个值创建不同的行。这将使它更容易理解,而不是把所有的东西放在一起。

评论

0赞 Nico Haase 4/14/2020
请分享如何做到这一点
3赞 kraftydevil 5/19/2020 #22

数组变量真的有必要吗?

我问是因为我最初来到这里想添加一个数组作为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 表中的“name”字段,这就是 REFERENCES 的表示。

我们还将人员和物品的组合定义为主键,但从技术上讲,这不是必需的。

最后,如果要获取人员列表中的所有项目,可以运行以下查询:

SELECT item FROM BuyList WHERE person='John';

这为您提供了 John 列表中的所有项目。无需阵列!

评论

0赞 einpoklum 5/19/2020
我接受的解决方案使用临时表。
0赞 kraftydevil 5/19/2020
答案是肯定的。然后,我为像我这样寻找创建数组类型的方法的人提供了这个答案 - 他们最初不明白为什么数组不是MySQL中的类型。这似乎是设计使然。这里没有介绍一般情况,所以我包括了我学到的东西,以便其他人了解通常不需要数组。我不指望你选择我的答案。这取决于用例。对于特定用例,您有公认的答案,我将为一般用例提供此答案。
1赞 aitor 6/20/2020 #23

如果我们有一张这样的表

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         |
+------------+-------+--------+------------+-------------+

评论

0赞 einpoklum 6/20/2020
我想是克林顿答案的重复。
3赞 Matteo 3/26/2021 #24

这是我使用包含元素列表的变量的解决方案。 您可以在简单的查询中使用它(无需使用存储过程或创建表)。

我在网站上的其他地方找到了使用 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) ;