
# 数据存在错误时的导出操作指南
对于导出时数据存在错误的原因，通常为将不满足数据库服务端编码的字符串或二进制数据插入到了数据库中，因此推荐在导出时保持客户端编码与数据库服务端编码保持一致，即可跳过服务端编码的合法性校验，也不会进行数据转码。
#### 导出编码一致性处理原则
1. 当客户端编码（client_encoding）与服务端编码（server_encoding）一致时：
   - 执行原生数据导出；
   
   - 保证数据完整性和原始性；
   
   - 无需进行字符集转换。
    
2. 当客户端编码（client_encoding）与服务端编码（server_encoding）不一致时：
   - 采用客户端编码作为导出文件目标编码标准；
   
   - 内核中对已有数据先基于服务端编码进行编码合法性校验，存在非法编码的数据会进行报错；
   
   - 内核再将数据进行转码处理，对无法转码（源字符集存在码位，目标字符集不存在码位）的字符进行报错。
    
 
#### 非法编码处理方案
当用户的数据库中存在非法编码入库的数据，想要导出时不进行报错，推荐以下两种方案。
首选方案：保持客户端编码与服务端编码保持一致后，将数据以数据库服务端编码进行导出，不进行转码。
1. 查询数据库服务端编码 
   ```
   gaussdb=# show server_encoding;
   ```
   
   
2. 查询数据库客户端编码 
   ```
   gaussdb=# show client_encoding;
   ```
   
   
3. 设置客户端编码与服务端编码一致 
   ```
   gaussdb=# set client_encoding = '{server_encoding}';
   ```
   
   
4. 执行COPY将数据以标准的CSV格式导出到文件中 
   ```
   gaussdb=# COPY test_copy TO '/data/test_copy.csv' CSV;
   ```
   
   
次选方案：需要依赖数据库内核的转码能力，并对非法编码的字节通过占位符（'?'）进行替换，导出的数据内容会发生变化。
1. 查询数据库服务端编码。 
   ```
   gaussdb=# show server_encoding;
   ```
   
   
2. 设置数据库客户端编码为目标编码。 
   ```
   gaussdb=# set client_encoding = {target_encoding};
   ```
   
   
3. 依赖内核转码能力进行导出，将非法编码的字节进行替换。 
   ```
   gaussdb=# COPY test_copy TO '/data/test_copy.csv' CSV COMPATIBLE_ILLEGAL_CHARS;
   ```
   
   
 
![](https://support.huaweicloud.com/centralized-devg-v8-gaussdb/public_sys-resources/notice_3.0-zh-cn.png)
- 使用COMPATIBLE_ILLEGAL_CHARS参数，当数据中存在非法编码的数据时，会将导出的数据进行修正，数据库内的数据不变，请酌情考虑后使用。
- 启用COMPATIBLE_ILLEGAL_CHARS参数的修改规则如下：
  - 非法字符替换：根据convert_illegal_char_mode参数配置的字符进行替换，默认替换为为'?'（U+003F）字符。
  
  - 零字符替换：对于零字符（U+0000）统一替换成空格字符（U+0020），如果不需要进行零字符替换，需要配置不同兼容性下的零字符功能开关。
   
- 关于[COMPATIBLE_ILLEGAL_CHAR....](https://support.huaweicloud.com/centralized-devg-v8-gaussdb/gaussdb-42-0534.html#ZH-CN_TOPIC_0000002558948111__zh-cn_topic_0000002526897836_li179613271195)参数的具体使用约束，请参考COPY语法章节。
 
#### 典型场景使用示例
针对独立零字符的基础处理逻辑相对简单，本文重点展示复合型异常场景的解决方案，即数据流中同时存在零字符（\\0）与非法编码字符的容错处理过程：
1. 构造UTF8的零字符与非法字符数据。
   ```
   gaussdb=# create database db_utf8 encoding='UTF-8' LC_COLLATE='en_US.UTF-8' LC_CTYPE ='en_US.UTF-8' dbcompatibility = 'A';
   CREATE DATABASE
   gaussdb=# \c db_utf8
   Non-SSL connection (SSL connection is recommended when requiring high-security)
   You are now connected to database "db_utf8" as user "omm".
   db_utf8=# create table test_encodings(id int, content text);
   CREATE TABLE
   db_utf8=# insert into test_encodings values(1, dbe_raw.cast_to_varchar2(dbe_raw.concat(dbe_raw.cast_from_varchar2_to_raw('导入'), hextoraw('00'), dbe_raw.cast_from_varchar2_to_raw('导出'))));
   INSERT 0 1
   db_utf8=# insert into test_encodings values(2, dbe_raw.cast_to_varchar2(dbe_raw.concat(hextoraw('2297'), 
   db_utf8=# dbe_raw.cast_from_varchar2_to_raw('导入导出'))));
   INSERT 0 1
   db_utf8=# show client_encoding;
    client_encoding 
   -----------------
    UTF8
   (1 row)
   --在id为1的行中，content包含零字符；在id为2的行中，content含有不属于UTF-8字符集的字符。
   db_utf8=# select *, dbe_raw.cast_from_varchar2_to_raw(content) from test_encodings;
    id |  content  |  cast_from_varchar2_to_raw   
   ----+-----------+------------------------------
     1 | 导入      | E5AFBCE585A500E5AFBCE587BA
     2 | "导入导出 | 2297E5AFBCE585A5E5AFBCE587BA
   (2 rows)
   ```
   
2. 在导出文件时，若选择与服务端相同的字符集，无需进行转码操作，文件默认能够顺利导出。然而，当选择与服务端不同的字符集时，则需要进行转码处理。在转码过程中，一旦识别到UTF-8编码中的非法字符0x97，系统将报错。此时，只需开启compatible_illegal_chars参数，文件便可成功导出。
   ```
   db_utf8=# copy test_encodings to '/home/xy/encodings.txt.utf8' encoding 'utf-8';
   COPY 2
   db_utf8=# copy test_encodings to '/home/xy/encodings.txt.gb18030' encoding 'gb18030';
   ERROR:  invalid byte sequence for encoding "UTF8": 0x97
   db_utf8=# copy test_encodings to '/home/xy/encodings.txt.gb18030' encoding 'gb18030' compatible_illegal_chars;
   COPY 2
   ```
   
3. 使用UTF-8编码打开文件/home/xy/encodings.txt.utf8，当前示例未针对A进行兼容处理，且未开启support_zero_character选项和compatible_illegal_chars参数。以下结果显示：第一行第二列的数据被截断；第二行第二列存在乱码，虽然在显示上未呈现明显异常，但通过hexdump命令可查看出乱码数据。用户可参考此示例进行复现操作，具体数据在此不再详述。
   ```
   1导入
   2"导入导出
   ```
   
4. 使用gb18030编码打开文件/home/xy/encodings.txt.gb18030，以下结果表明：文件中第一行第二列的零字符被替换为空格；第二行第二列的非法字符被替换为"?"。
   ```
   1导入 导出
   2"?导入导出
   ```
   
 
