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
我目前有一个 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