如何将SQL Server的Latin1 varchar字符编码适配插入MariaDB UTF8?
我来帮你搞定这个字符编码的坑!你遇到的\x87错误本质是SQL Server的"latin1"(实际是Windows-1252编码)和MariaDB的UTF-8字符集之间的转换问题,尤其是那些C1控制字符或者扩展字符的处理。下面是一步步的解决方案:
1. 修复自定义编码函数,处理Unicode转换和特殊字符
首先,把你的encode4MySql函数改成支持Unicode的版本——因为SQL Server的VARCHAR(latin1)实际用的是Windows-1252编码,转成NVARCHAR才能正确映射到Unicode,再针对性处理特殊字符:
CREATE FUNCTION dbo.encode4MySql(@raw NVARCHAR(4000)) RETURNS NVARCHAR(4000) AS BEGIN DECLARE @replaced NVARCHAR(4000); -- 第一步:替换单引号为MariaDB兼容的双单引号 SET @replaced = REPLACE(@raw, '''', ''''''); -- 第二步:处理Windows-1252里的特殊字符(比如你遇到的\x87) -- \x87在Windows-1252里对应‡(双剑号,Unicode U+2016),替换成可视字符 SET @replaced = REPLACE(@replaced, NCHAR(0x0087), NCHAR(0x2016)); -- 可以继续添加其他需要处理的特殊控制字符,比如: SET @replaced = REPLACE(@replaced, NCHAR(0x0080), NCHAR(0x20AC)); -- 替换€符号 SET @replaced = REPLACE(@replaced, NCHAR(0x0081), ''); -- 移除无效控制字符 RETURN @replaced; END;
2. 修改INSERT生成语句,强制转换字段为NVARCHAR
在生成INSERT的时候,把原来的VARCHAR字段先转成NVARCHAR,这样函数才能拿到正确的Unicode字符:
SELECT CONCAT('INSERT INTO maria_table (id, name, description)', ' VALUES '); SELECT CONCAT( IIF(row_number() OVER (ORDER BY id ) = 1, ' ', ','), '(', id, ',', '''', dbo.encode4MySql(CAST(name AS NVARCHAR(4000))), ''',', '''', dbo.encode4MySql(CAST(description AS NVARCHAR(4000))), '''' ) FROM sql_srv_table WITH (NOLOCK) ORDER BY id; SELECT ';'; SELECT 'COMMIT;';
3. 调整SQLCMD导出参数,确保编码正确转换
之前的-f o:65001只指定了输出UTF-8,但没告诉SQLCMD输入编码是Windows-1252(SQL Server的latin1实际用的是这个),所以加上输入编码参数:
SQLCMD.EXE -i "generate-inserts.sql" -h-1 -S "my-host" -d Tardis -r1 -W -b -f i:1252,o:65001 > inserts.sql
这个参数会让SQLCMD把Windows-1252的输入正确转成UTF-8输出,避免出现无效的UTF-8字节。
4. 确保MariaDB表使用兼容的字符集
MariaDB默认的utf8只支持BMP字符,建议改成utf8mb4来支持所有Unicode字符,包括特殊符号:
ALTER TABLE maria_table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
5. 导入时指定正确编码
用mysql命令导入时,加上--default-character-set=utf8mb4参数,确保导入过程中编码正确:
mysql -u your_username -p --default-character-set=utf8mb4 your_database < inserts.sql
关于找不到\x87的说明
你在导出文件里搜不到\x87是因为它是无效的UTF-8字节,文本编辑器会把它当作乱码或者隐藏字符处理。当你按照上面的步骤转换后,这个字符会变成正常的UTF-8编码的‡,就能正常显示和搜索了。
内容的提问来源于stack exchange,提问作者Adam

