<p>应使用 psql 的 COPY 命令而非 pg_dump 导出纯 CSV:psql -d mydb -c "COPY (SELECT * FROM users) TO STDOUT WITH (FORMAT CSV, HEADER true, ENCODING 'UTF8', NULL '')",支持变量替换 schema 名、避免前缀污染与 SQL 注入,且输出为原始数据流。</p>
导出 CSV 时 pg_dump 默认带库名前缀怎么办
直接用 pg_dump --table=users --format=csv 会失败,因为 pg_dump 根本不支持 --format=csv;它只支持 plain(sql)、custom、directory 和 tar。想导出纯 csv,得绕开 pg_dump,改用 psql 的 \copy 或 -c + copy ... to stdout。
常见错误现象:pg_dump: unrecognized option '--format=csv',或者导出 SQL 文件后手动删 public.users 前缀——这在多租户场景下极易误删其他 schema 的同名表。
- 用
psql -d mydb -c "COPY (SELECT * FROM users) TO STDOUT WITH CSV HEADER",输出就是无 schema 前缀的纯数据 - 如果表在非
publicschema(比如tenant_123.users),必须显式写全名,但导出内容本身不包含tenant_123.字符串——COPY只读数据,不写元信息 - 别用
\copy交互命令导出到文件再传给下游服务:它依赖客户端路径权限,容器或 CI 环境常因Permission denied失败
多租户下 schema 名动态切换但导出语句不能硬编码
硬写 COPY (SELECT * FROM tenant_abc.users) 意味着每次换租户都要改 SQL 字符串,容易注入且难维护。关键是把 schema 名从 SQL 体中剥离,交给参数控制。
使用场景:定时任务按租户轮询导出,或 API 接收 tenant_id 后触发导出。
- 用
psql的变量机制:psql -v schema_name=tenant_456 -d mydb -c "COPY (SELECT * FROM :schema_name.users) TO STDOUT WITH CSV HEADER",:schema_name会被安全替换,不会触发 SQL 注入 - PostgreSQL 不允许变量用于 schema 名以外的标识符(如表名),所以
:table_name不能直接用;固定表结构前提下,只变量 schema 最稳妥 - 若租户表名也不同(如
tenant_789.cust_profiles),就得拼接字符串——必须用format()+quote_ident()在服务端生成 SQL,再传给psql -c,不能靠 psql 变量
COPY ... TO STDOUT 导出中文乱码或 NULL 处理异常
默认导出可能把 UTF-8 字节当 Latin-1 解,或把数据库里的 NULL 写成空字段而非 \N,下游解析时错行。
性能影响:加 ENCODING 'UTF8' 几乎无开销,但缺了它会导致整个文件不可用;NULL 表示方式不一致则 Excel 或 pandas 读取时列数错位。
- 强制指定编码:
COPY (SELECT ...) TO STDOUT WITH (FORMAT CSV, HEADER true, ENCODING 'UTF8', NULL ''),NULL ''表示用空字符串代替\N,适配大多数前端解析器 - 避免用
DELIMITER ','显式声明——CSV 标准分隔符就是逗号,额外声明反而在某些 psql 版本引发语法错误 - 如果字段含换行符或双引号,
WITH CSV自动处理转义,无需额外加QUOTE或ESCAPE参数
为什么不用 pg_dump --inserts 再 sed 删 schema 前缀
看似能用 pg_dump --inserts --table=users 生成 INSERT INTO public.users ...,再用 sed 's/public\.//' 清洗——但多租户环境下这很危险。
容易踩的坑:一个租户的表叫 logs,另一个叫 public_logs,sed 一删就变成 INSERT INTO _logs,直接语法错误;更糟的是,pg_dump 输出里还有 SET search_path = public, pg_catalog; 这类控制语句,删漏一行就导致后续 INSERT 执行在错 schema。
- 文本清洗本质是脆弱的模式匹配,不是语义解析;只要 schema 名是其他标识符子串(如
tenant_api包含api),就可能误伤 -
pg_dump --inserts生成的 SQL 是为重载设计的,不是为解析设计的;它的字段顺序、类型转换、默认值展开都和原始表可能不一致 - 真正需要“纯净数据”时,唯一可靠路径是走
COPY——它跳过所有 DDL 和权限逻辑,只吐 SELECT 结果集的原始字节流
最易被忽略的一点:COPY ... TO STDOUT 的输出是未缓冲的流式字节,如果上游管道卡住(比如下游进程崩溃),PostgreSQL 会立刻中断连接并报 server closed the connection unexpectedly——这意味着导出脚本必须处理 SIGPIPE,或确保接收端始终 ready。

















