MySQL (Aurora-MySql) 中 CONCAT 的最大字符串长度是多少?

What is the maximum length of string for CONCAT in MySQL (Aurora-MySql)?

我目前有一个 MySQL(Aurora MySQL 基于 MySQL 5.7.x)PROCEDURE 将 ID 的字符串化列表作为 TEXT 并使用 CONCAT 函数创建准备执行的语句。

虽然该过程大部分时间都有效,但我在对它的几次调用中遇到了截断错误(可能有很多 ID)。错误消息本质上是:

com.mysql.jdbc.MysqlDataTruncation Data truncation: Data too long for column 'stuffIds' at row 1

我最初的想法是我们为 PROCEDURE 参数 stuffIds 选择的类型太小,但当我检查时,我发现它的类型是 TEXT。我们的服务似乎真的不太可能达到这种类型的极限, 据我所知,它被限制在 2^31 个字符(或 2GB 的数据)。我们只是在任何列表中都没有足够的 ID 来达到该限制。

我的下一个想法是,这可能与我们用来创建预准备语句的 CONCAT 函数有关?但是,虽然我已经看到很多关于 GROUP_CONCAT 及其最大长度(以及相关配置)的问题和答案,但我无法找到有关 MySQL 支持的字符串最大长度的任何信息的 CONCAT 函数。

我唯一能想到的是,它可能与 AWS Aurora 对 MySQL 的实施特别相关?我认为 Aurora 应该非常接近 vanilla MySQL,但也许这是一个边缘案例?

PROCEDURE 本身(名称已更改以保护无辜者)引发此错误看起来像:

DELIMITER //
CREATE PROCEDURE GetThingsByStuffIds(stuffIds TEXT)

BEGIN

SET @stmt = CONCAT('SELECT ThingId FROM ThingTable WHERE StuffId IN (', stuffIds, ')');

PREPARE insert_stm FROM @stmt;
EXECUTE insert_stm;
DEALLOCATE PREPARE insert_stm;


END //
DELIMITER ;

如有任何见解,我们将不胜感激。谢谢!


UPDATE/NOTE/WARNING!

正如@Schwern 所指出的,我的 MySQL TEXT 类型信息有误!

which to my knowledge is limited at 2^31 chars (or 2GB of data)

我的那部分问题非常不正确!与 PostgreSQL 不同,正如下面@Schwern 所述,MySQL 具有三种大小的变体:TEXT - TEXT(65,535 个字符)、MEDIUMTEXT(16,777,215 个字符)、LONGTEXT (4,294,967,295 个字符)。我误解了 TEXT 的长度限制,因此我的呼叫超出了限制。 MySQL is here 中有关这些类型的完整信息。添加此注释是为了让我在问题中的错误信息不会误导任何人!

text 不能大于 2^16 字节 或 65,535。根据字符集,字符可以占用多个字节,但如果是简单的 UTF-8,则每个字符可能占用一个字节。

您可以使用 mediumtext(2^24 字节)或 longtext(2^32 字节),但实际上您的 table 需要重新设计。 将列表存储为逗号分隔的字符串效率极低、复杂且容易出错。相反,使用 many-to-many join table.

create thing_stuff (
  thing_id int not null references thing(id),
  stuff_id int not null references stuff(id)
);

-- Thing 1 has Stuff 2 and 5.
insert into thing_stuff (thing_id, stuff_id) values
  (1, 2), (1, 5);

-- Get all the Stuff associated with Thing 1.
select stuff.*
from stuff s
join thing_stuff ts on ts.stuff_id = s.id
where thing_id = 1;

-- Thing 1 no longer has Stuff 5.
delete from thing_stuff
where thing_id = 1 and stuff_id = 5;

您会遇到一个问题,因为 IN 子句对其可以容纳的值的数量有限制,并且它直接链接到 max_allowed_packet size

更好的解决方案是拆分 id 字符串并将其添加到临时 table 并加入两者,对于大量元素这也更快

我借用的分割函数形式

CREATE TABLE ThingTable(StuffId int,ThingId int)
INSERT INTO ThingTable VALUES (1,1),(2,2),(3,3),(5,5)
CREATE FUNCTION SPLIT_STR(
  x VARCHAR(255),
  delim VARCHAR(12),
  pos INT
)
RETURNS VARCHAR(255)
RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
       LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
       delim, '');
CREATE PROCEDURE GetThingsByStuffIds(stuffIds TEXT)

BEGIN
      DECLARE a INT Default 0 ;
      DECLARE str TEXT;
      DROP TEMPORARY TABLE IF EXISTS my_temp_table;
      CREATE TEMPORARY TABLE my_temp_table(aint int);
      simple_loop: LOOP
         SET a=a+1;
         SET str=SPLIT_STR(stuffIds,",",a);
         IF str='' THEN
            LEAVE simple_loop;
         END IF;
         #Do Inserts into temp table here with str going into the row
         insert into my_temp_table values (str);
   END LOOP simple_loop;

SELECT ThingId FROM ThingTable t1 INNER JOIN  my_temp_table t2 ON t1.StuffId = t2.aint;



END
CALL GetThingsByStuffIds('1,2,3,4,5,6')
| ThingId |
| ------: |
|       1 |
|       2 |
|       3 |
|       5 |

✓

db<>fiddle here