MySQL - select 字符串的前 10 个字节
MySQL - select first 10 bytes of a string
聪明的男人和女人,您好,
你如何 select 字符串的前 x 个字节?
用例:我正在优化要上传到亚马逊的产品描述文本,亚马逊按 utf8 字节(不是我之前提到的 latin1)而不是字符来测量字段长度。 MySQL 另一方面,似乎是基于字符操作的。 (例如,函数 left() 是基于字符的,而不是基于字节的)。差异(使用英语、法语、西班牙语和德语)大约为 10%,但差异可能很大。
关于#bytes < 250 的字段的一些测试(详细信息:http://wiki.devliegendebrigade.nl/Format_inventarisbestanden_(Amazon)#Veldlengte):
OK, char_length: 248, byte length latin1: 248, byte length utf8: 248
OK, char_length: 249, byte length latin1: 249, byte length utf8: 249
OK, char_length: 249, byte length latin1: 249, byte length utf8: 249
OK, char_length: 249, byte length latin1: 249, byte length utf8: 249
Not OK, char_length: 250, byte length latin1: 250, byte length utf8: 250
Not OK, char_length: 249, byte length latin1: 249, byte length utf8: 252
Not OK, char_length: 248, byte length latin1: 248, byte length utf8: 252
Not OK, char_length: 249, byte length latin1: 249, byte length utf8: 252
Not OK, char_length: 249, byte length latin1: 249, byte length utf8: 257
插图:
set @tekst="Jantje zag € pruimen hangen";
select
char_length(@tekst), # 27 characters
length(@tekst); # 29 bytes
select left(@tekst, 15) # Result: "Jantje zag € pr"
# Ideally, I'm looking for something like this:
select left_bytes_utf8(@tekst, 15) # Result: "Jantje zag € "
一种方法可能是通过一个迭代调用自身的存储过程,但我怀疑周围有更有效的解决方案。
已经谢谢你了,Jeroen
P.s.: 编辑问题:将 2x "latin1" 更改为 "utf8"。它实际上有点令人困惑:上传应该是 Latin1,但字段大小是使用 utf8
以字节为单位测量的
P.p.s:更新:这些上传适用于英语、法语、西班牙语和德语亚马逊网站。没有比“ø”(直径)、“€”、“è”、“é”、“ü”和“ö”更奇特的字符了。全部在 Latin1 编码中,但在 utf8 中为多字节。
How would you select the first x bytes of a string?
这真的是你想要做的吗?这可能(正如已经指出的那样)通过将多字节字符拆分为垃圾来破坏字符串。
Amazon calculates field lengths by bytes
请提供证据证明这一点。
The difference is roughly 10%, but it can vary widely.
最大值可以是 4 的因数。表情符号和某些汉字需要 4 个字节进行 UTF-8 (utf8mb4) 编码。
如果亚马逊在 latin1
中编码( 与 "by bytes" 不同 ),那么首先你需要检查字符串是否可以以 latin1 编码。西欧文字可以,亚洲文字不行。当然,您可以得到 "bytes",这会导致文本损坏,尤其是当您截断到某个字节而不是字符、边界时。
SELECT CONVERT(CONVERT(@tekst USING latin1) USING utf8) = @tekst;
如果转换有效,将 return 1(真)。
然后你可以将 CONVERT(@tekst USING latin1)
与 LEFT(..., 10)
或其他任何东西一起使用。
更好?
如果亚马逊有效地使用 latin1,那么你使用 latin1。也就是说,声明您的字符串:
for_amazon VARCHAR(10) CHARACTER SET latin1
and/or 联系 SET NAMES latin1
或者你可以有一个更大的领域,然后LEFT(..., 10)
两者都将提供转换(存储前与提取时),以便您提供给亚马逊的字节将是 latin1。
警告:如果您在列中存储中文(或俄语或希腊语等),它将被弄乱。
SELECT CONVERT(LEFT(CONVERT(@tekst USING binary), 15) USING utf8);
将为您提供缩减为 15 个字节的 UTF-8 字符串,只要它仍然是有效的 UTF-8 字符串(MySQL 将拒绝为您提供无效的字符串,例如,如果您将在多字节字符上,并给你 NULL
。)如果这不起作用,你可以通过省略最后一次重新转换为 UTF-8 来获取原始字节,但你必须将它们解码为某些东西对自己有用:
SELECT LEFT(CONVERT(@tekst USING binary), 15);
然而,Rick James 给出了很多好的建议;尽管只有您可以判断它与您和您的具体情况相关的程度。
谢谢@Amadan 和@Rick James!感谢您的输入,我能够想出一个多字节安全的字节左函数:
CREATE DEFINER=`root`@`localhost` FUNCTION `left_byte`(
input_string text,
input_position integer
) RETURNS text CHARSET utf8
BEGIN
# Byte-wise left function
################################################################################
#
# * multibyte-safe for characters of up to 4 bytes (=max # bytes utf8)
# * utf8 Assumed to be the general encoding
return
ifnull
(
ifnull
(
ifnull
(
convert(left(convert(input_string using binary), input_position) using utf8),
convert(left(convert(input_string using binary), input_position-1) using utf8)
),
convert(left(convert(input_string using binary), input_position-2) using utf8)
),
convert(left(convert(input_string using binary), input_position-3) using utf8)
);
END
聪明的男人和女人,您好,
你如何 select 字符串的前 x 个字节?
用例:我正在优化要上传到亚马逊的产品描述文本,亚马逊按 utf8 字节(不是我之前提到的 latin1)而不是字符来测量字段长度。 MySQL 另一方面,似乎是基于字符操作的。 (例如,函数 left() 是基于字符的,而不是基于字节的)。差异(使用英语、法语、西班牙语和德语)大约为 10%,但差异可能很大。
关于#bytes < 250 的字段的一些测试(详细信息:http://wiki.devliegendebrigade.nl/Format_inventarisbestanden_(Amazon)#Veldlengte):
OK, char_length: 248, byte length latin1: 248, byte length utf8: 248
OK, char_length: 249, byte length latin1: 249, byte length utf8: 249
OK, char_length: 249, byte length latin1: 249, byte length utf8: 249
OK, char_length: 249, byte length latin1: 249, byte length utf8: 249
Not OK, char_length: 250, byte length latin1: 250, byte length utf8: 250
Not OK, char_length: 249, byte length latin1: 249, byte length utf8: 252
Not OK, char_length: 248, byte length latin1: 248, byte length utf8: 252
Not OK, char_length: 249, byte length latin1: 249, byte length utf8: 252
Not OK, char_length: 249, byte length latin1: 249, byte length utf8: 257
插图:
set @tekst="Jantje zag € pruimen hangen";
select
char_length(@tekst), # 27 characters
length(@tekst); # 29 bytes
select left(@tekst, 15) # Result: "Jantje zag € pr"
# Ideally, I'm looking for something like this:
select left_bytes_utf8(@tekst, 15) # Result: "Jantje zag € "
一种方法可能是通过一个迭代调用自身的存储过程,但我怀疑周围有更有效的解决方案。
已经谢谢你了,Jeroen
P.s.: 编辑问题:将 2x "latin1" 更改为 "utf8"。它实际上有点令人困惑:上传应该是 Latin1,但字段大小是使用 utf8
以字节为单位测量的P.p.s:更新:这些上传适用于英语、法语、西班牙语和德语亚马逊网站。没有比“ø”(直径)、“€”、“è”、“é”、“ü”和“ö”更奇特的字符了。全部在 Latin1 编码中,但在 utf8 中为多字节。
How would you select the first x bytes of a string?
这真的是你想要做的吗?这可能(正如已经指出的那样)通过将多字节字符拆分为垃圾来破坏字符串。
Amazon calculates field lengths by bytes
请提供证据证明这一点。
The difference is roughly 10%, but it can vary widely.
最大值可以是 4 的因数。表情符号和某些汉字需要 4 个字节进行 UTF-8 (utf8mb4) 编码。
如果亚马逊在 latin1
中编码( 与 "by bytes" 不同 ),那么首先你需要检查字符串是否可以以 latin1 编码。西欧文字可以,亚洲文字不行。当然,您可以得到 "bytes",这会导致文本损坏,尤其是当您截断到某个字节而不是字符、边界时。
SELECT CONVERT(CONVERT(@tekst USING latin1) USING utf8) = @tekst;
如果转换有效,将 return 1(真)。
然后你可以将 CONVERT(@tekst USING latin1)
与 LEFT(..., 10)
或其他任何东西一起使用。
更好?
如果亚马逊有效地使用 latin1,那么你使用 latin1。也就是说,声明您的字符串:
for_amazon VARCHAR(10) CHARACTER SET latin1
and/or 联系 SET NAMES latin1
或者你可以有一个更大的领域,然后LEFT(..., 10)
两者都将提供转换(存储前与提取时),以便您提供给亚马逊的字节将是 latin1。
警告:如果您在列中存储中文(或俄语或希腊语等),它将被弄乱。
SELECT CONVERT(LEFT(CONVERT(@tekst USING binary), 15) USING utf8);
将为您提供缩减为 15 个字节的 UTF-8 字符串,只要它仍然是有效的 UTF-8 字符串(MySQL 将拒绝为您提供无效的字符串,例如,如果您将在多字节字符上,并给你 NULL
。)如果这不起作用,你可以通过省略最后一次重新转换为 UTF-8 来获取原始字节,但你必须将它们解码为某些东西对自己有用:
SELECT LEFT(CONVERT(@tekst USING binary), 15);
然而,Rick James 给出了很多好的建议;尽管只有您可以判断它与您和您的具体情况相关的程度。
谢谢@Amadan 和@Rick James!感谢您的输入,我能够想出一个多字节安全的字节左函数:
CREATE DEFINER=`root`@`localhost` FUNCTION `left_byte`(
input_string text,
input_position integer
) RETURNS text CHARSET utf8
BEGIN
# Byte-wise left function
################################################################################
#
# * multibyte-safe for characters of up to 4 bytes (=max # bytes utf8)
# * utf8 Assumed to be the general encoding
return
ifnull
(
ifnull
(
ifnull
(
convert(left(convert(input_string using binary), input_position) using utf8),
convert(left(convert(input_string using binary), input_position-1) using utf8)
),
convert(left(convert(input_string using binary), input_position-2) using utf8)
),
convert(left(convert(input_string using binary), input_position-3) using utf8)
);
END