【SQL Server】清除数据库表中的右截断字符

      这里说的右截断字符,是指类型为char或varchar的字段中,最后一个双字节字符(如汉字)由于字段宽度不够被切断为只剩下1个字节的字符,这个字符会被显示为乱码。SQL Server 2000及更早的版本,碰到要保存的字符串长度大于字段长度时,会把字符串截成字段的长度再保存,这个过程会出现右截断字符。数据库升级到新版本后,这些右截断字符仍然存在,会妨碍数据导入/导出、BCP in/out等操作,因此需要把它们清除掉。

 清除右截断字符的sql脚本如下所示。运行该脚本后生会产生清除右截断字符的SQL命令,而不是直接进行清除右截断字符的操作。在正式执行这些SQL命令前,可以先评估核实一下。

DECLARE @dbname VARCHAR(128) = 'TargetDB';
IF NOT EXISTS (SELECT * FROM sys.databases WHERE NAME=@dbname)
BEGIN
  PRINT 'Error: Database ''' + @dbname + ''' Not Exists.';
  RETURN;
END;

SET NOCOUNT ON;
DECLARE @tabname VARCHAR(128), @colname VARCHAR(128), @sql NVARCHAR(4000);
DECLARE @count INT;
IF OBJECT_ID('tempdb..#tmp', 'U') IS NOT NULL
  DROP TABLE #tmp;

CREATE TABLE #tmp (tabname VARCHAR(256), colname VARCHAR(256));
SET @sql = 'insert into #tmp ';
SET @sql = @sql + 'SELECT tabname=b.name, colname=a.name'
SET @sql = @sql + ' FROM ' + @dbname + '.sys.columns a, '+ @dbname + '.sys.tables b'
SET @sql = @sql + ' WHERE a.object_id = b.object_id AND b.is_ms_shipped=0'
SET @sql = @sql + ' AND a.system_type_id IN (SELECT system_type_id FROM '+ @dbname+ '.sys.types WHERE name IN (''char'', ''varchar''))';
--PRINT @sql;
EXEC (@sql);
CREATE CLUSTERED INDEX index_tmp ON #tmp (tabname, colname);

SET @tabname = (SELECT MIN(tabname) FROM #tmp);
WHILE @tabname IS NOT NULL
BEGIN
  SET @colname = (SELECT MIN(colname) FROM #tmp WHERE tabname=@tabname);
  WHILE @colname IS NOT NULL
  BEGIN
    SET @sql = 'SELECT @count=COUNT(*) FROM ' + @dbname + '.dbo.' + @tabname + ' ';
      SET @sql = @sql + ' WHERE ' + @colname + ' IS NOT NULL';
      SET @sql = @sql + ' AND ' + @colname + ' <> ''''';
      SET @sql = @sql + ' AND RIGHT(' + @colname + ', 1) <> '' ''';
      SET @sql = @sql + ' AND CAST(CAST(RIGHT(' + @colname + ', 1) AS VARBINARY) AS VARCHAR) = ''''';
    --PRINT @sql;
    EXEC sp_executesql @sql, N'@count int output', @count OUTPUT;
    IF @count > 0
    BEGIN
      SET @sql = 'UPDATE ' + @dbname +'.dbo.' + @tabname + ' SET ';
      SET @sql = @sql + @colname + '= RTRIM(CAST(CAST(' + @colname + ' AS VARBINARY(8000)) AS VARCHAR(8000)))';
      SET @sql = @sql + ' WHERE ' + @colname + ' IS NOT NULL';
      SET @sql = @sql + ' AND ' + @colname + ' <> ''''';
      SET @sql = @sql + ' AND RIGHT(' + @colname + ', 1) <> '' ''';
      SET @sql = @sql + ' AND CAST(CAST(RIGHT(' + @colname + ', 1) AS VARBINARY) AS VARCHAR) = ''''';
      PRINT @sql;
    END;
    SET @colname = (SELECT MIN(colname) FROM #tmp WHERE tabname=@tabname AND colname>@colname);
  END;
  SET @tabname = (SELECT MIN(tabname) FROM #tmp WHERE tabname>@tabname);
END;
SET NOCOUNT OFF;

 

<think>我们面对的问题:在Linux Shell中将包含中文的CSV文件导入TiDB时出现乱码。 原因分析:乱码通常由字符编码不匹配引起。TiDB默认使用UTF-8编码,如果CSV文件不是UTF-8编码(如GBK、GB2312等),则会出现乱码。 解决方案: 1. 将CSV文件转换为UTF-8编码(推荐使用iconv工具) 2. 确保TiDB数据库以及连接客户端的字符集设置正确 3. 在LOAD DATA语句中显式指定字符集 具体步骤: 步骤1:检查并转换CSV文件编码 使用file命令检查文件编码: file -i your_file.csv 如果输出不是utf-8(例如:charset=iso-8859-1或gbk),则需要转换。 转换命令(例如将GBK转换为UTF-8): iconv -f GBK -t UTF-8 your_file.csv -o your_file_utf8.csv 步骤2:在LOAD DATA语句中指定字符集 在导入时,可以在LOAD DATA语句中指定文件字符集为UTF8: LOAD DATA LOCAL INFILE 'your_file_utf8.csv' INTO TABLE your_table CHARACTER SET utf8mb4 FIELDS TERMINATED BY ',' ... 步骤3:确保数据库字符集为UTF-8 检查字符集: SHOW CREATE TABLE your_table; 如果字符集不是utf8mb4,则需要修改或创建时指定。 创建示例: CREATE TABLE your_table ( ... ) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; 步骤4:确保客户端连接使用UTF-8 在连接TiDB时,可以指定默认字符集: mysql -h host -P port -u user -p --default-character-set=utf8mb4 在LOAD DATA命令中,我们可以在SQL语句前设置字符集: SET NAMES utf8mb4; LOAD DATA ... 完整脚本示例: 假设原始CSV文件是GBK编码,我们将其转换为UTF-8,然后导入。 ```bash #!/bin/bash # 解决中文乱码的导入脚本 # 配置参数 HOST="127.0.0.1" PORT=4000 USER="root" PASSWORD="your_password" DATABASE="your_database" TABLE="your_table" ORIG_CSV="/path/to/original.csv" # 原始CSV文件(GBK编码) CONVERTED_CSV="/tmp/converted_utf8.csv" # 转换后的UTF-8文件 DELIMITER="," # 1. 检查原始文件是否存在 if [ ! -f "$ORIG_CSV" ]; then echo "错误:原始CSV文件不存在 - $ORIG_CSV" >&2 exit 1 fi # 2. 转换编码(假设原始编码为GBK,如果不是,请替换-f后的编码) echo "正在转换文件编码为UTF-8..." iconv -f GBK -t UTF-8 "$ORIG_CSV" -o "$CONVERTED_CSV" if [ $? -ne 0 ]; then echo "转换编码失败!" >&2 exit 1 fi # 3. 导入数据 echo "开始导入数据..." mysql -h $HOST -P $PORT -u $USER -p$PASSWORD --local-infile=1 --default-character-set=utf8mb4 <<EOF SET NAMES utf8mb4; SET tidb_dml_batch_size = 20000; USE $DATABASE; LOAD DATA LOCAL INFILE '$CONVERTED_CSV' INTO TABLE $TABLE CHARACTER SET utf8mb4 FIELDS TERMINATED BY '$DELIMITER' IGNORE 1 LINES; -- 如果第一行是标题,则忽略 EOF # 4. 检查导入结果 if [ $? -eq 0 ]; then echo "导入成功!" # 删除临时转换文件(可选) # rm -f "$CONVERTED_CSV" else echo "导入失败!" >&2 exit 1 fi ``` 注意事项: 1. 转换编码前,请确认原始文件的准确编码。可以使用`file -i`或`enca`等工具检测。 2. 如果CSV文件中包含特殊字符(如分隔符、换行符等),需要正确处理,可以使用`OPTIONALLY ENCLOSED BY`等选项。 3. 在TiDB中,推荐使用`utf8mb4`字符集,以支持完整的Unicode字符(包括emoji等)。 如果问题仍然存在,可以检查TiDB服务器的字符集配置: SHOW VARIABLES LIKE 'character_set%'; SHOW VARIABLES LIKE 'collation%'; 确保服务器端配置也是以utf8mb4为主。 引用: [1] TiDB官方文档中关于字符集和排序规则的说明:https://docs.pingcap.com/zh/tidb/stable/character-set-and-collation [2] 关于LOAD DATA语句的详细语法:https://dev.mysql.com/doc/refman/8.0/en/load-data.html §§相关问题§§ 1. 如何自动检测CSV文件的原始编码? 2. 除了GBK,还有哪些常见的中文字符编码? 3. TiDB的utf8mb4和MySQL的utf8mb4有什么区别? 4. 在导入过程中如何跳过或处理包含非法字符的行? 5. 如何优化大容量中文CSV文件的导入速度?</think>以下是解决 Linux Shell 导入 CSV 文件到 TiDB 出现中文乱码的完整解决方案,包含详细步骤和脚本示例: ### 中文乱码的根本原因 1. **字符编码不匹配**:TiDB 默认使用 UTF-8 编码,若 CSV 文件使用 GBK/GB2312 等编码会导致乱码[^1] 2. **客户端连接设置错误**:MySQL 客户端连接时未指定正确编码 3. **文件 BOM 头问题**:某些编辑器添加的 BOM 头干扰解析 --- ### 解决方案(分步骤操作) #### 步骤 1:检查并转换文件编码 ```bash # 安装编码检测工具 sudo apt-get install enca # Debian/Ubuntu sudo yum install enca # CentOS/RHEL # 检测文件编码 enca -L zh_CN your_file.csv # 转换编码为 UTF-8(无BOM) iconv -f GBK -t UTF-8 your_file.csv -o converted_utf8.csv ``` #### 步骤 2:验证文件内容 ```bash # 查看文件前10行 head -n 10 converted_utf8.csv | hexdump -C # 理想输出(无BOM头): 00000000 e4,b8,ad,e6,96,87,2c,31,2c,32,0a |....,1,2.| ``` #### 步骤 3:修改导入脚本(关键修正) ```bash #!/bin/bash # 修正中文乱码的导入脚本 HOST="127.0.0.1" PORT=4000 USER="root" PASSWORD="your_password" DATABASE="your_db" TABLE="your_table" CSV_FILE="converted_utf8.csv" # 使用转换后的文件 DELIMITER="," mysql -h $HOST -P $PORT -u $USER -p$PASSWORD --default-character-set=utf8mb4 --local-infile=1 <<EOF SET NAMES utf8mb4; # 显式设置连接编码 SET tidb_dml_batch_size = 20000; USE $DATABASE; LOAD DATA LOCAL INFILE '$CSV_FILE' INTO TABLE $TABLE CHARACTER SET utf8mb4 # 关键:指定文件编码 FIELDS TERMINATED BY '$DELIMITER' OPTIONALLY ENCLOSED BY '\"' # 处理带引号的中文字段 ESCAPED BY '' # 禁用转义符处理 IGNORE 1 LINES; EOF # 检查结果 if [ $? -eq 0 ]; then echo "导入成功!" # 验证中文数据 mysql -h $HOST -P $PORT -u $USER -p$PASSWORD -e "SELECT * FROM $DATABASE.$TABLE WHERE column LIKE '%中文%' LIMIT 5;" else echo "导入失败!错误码: $?" >&2 fi ``` #### 步骤 4:TiDB 服务端配置(可选) 在 TiDB 配置文件 `tidb.toml` 中添加: ```toml [instance] character_set_server = "utf8mb4" collation_server = "utf8mb4_bin" ``` --- ### 常见问题处理 1. **混合编码问题**: ```bash # 使用 sed 清除非UTF-8字符 sed -i 's/[^\x00-\x7F]//g' converted_utf8.csv ``` 2. **BOM 头问题**: ```bash # 移除UTF-8 BOM头 sed -i '1s/^\xEF\xBB\xBF//' converted_utf8.csv ``` 3. **字段截断错误**: 在 LOAD DATA 语句中添加: ```sql LINES TERMINATED BY '\r\n' -- Windows换行 LINES TERMINATED BY '\n' -- Linux换行 ``` --- ### 完整工作流示例 ```mermaid graph TD A[原始CSV文件] --> B{编码检测} B -->|GBK/GB2312| C[iconv转UTF-8] B -->|UTF-8| D[移除BOM头] C --> D D --> E[验证文件内容] E --> F[执行导入脚本] F --> G[验证中文数据] ``` > **重要提示**:TiDB 5.3+ 版本已优化中文支持,建议升级到最新版本[^1]。对于超大型文件(>10GB),建议使用 TiDB Lightning 工具,其内置编码转换功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值