给运营导个 CSV,在 ksql 里用 \copy 就够了

运营要一份订单数据、产品要个对账表,这活儿最后常常落到开发头上。MySQL 里把查询结果导成文件,第一反应是 SELECT ... INTO OUTFILE:可它要 FILE 权限,文件还落在数据库服务器上,开发机上看不到,经常得再登服务器把文件拷出来。

KingbaseES 里有个更顺手的做法------ksql 的 \copy。它是客户端元命令,由 ksql 进程在本机执行:文件直接写在运行 ksql 的这台机器上,普通账号就能用,不碰服务端权限。

这篇在 KingbaseES V009R001C010 环境里验证 \copy 的三件事:把整张表导成 CSV、按条件只导需要的列和行、再把 CSV 导回表;最后用一条不带反斜杠的 COPY 触发报错,说清 \copyCOPY 到底差在哪。演示用普通用户 app_user 连接 app_db 库,表是 app_schema.t_meta_demo

先确认连的是哪里

\copy 跑在当前会话里,用短表名时靠 search_path 找表,所以连进去先把位置和身份确认清楚:

csharp 复制代码
set search_path to app_schema, public;
​
select current_database(), current_user, current_schema();

当前库是 app_db,用户是 app_usersearch_path 第一个 schema 是 app_schema。注意这里是普通用户 ------这一点到第 6 节会很关键,它正是 \copy 能用、而服务端 COPY 用不了的分界线。

导出整张表

t_meta_demo 整张表导成带列名的 CSV:

sql 复制代码
\copy t_meta_demo TO '/tmp/orders_full.csv' WITH (FORMAT CSV, HEADER true);

ksql 返回 COPY 5,表示写出了 5 行。在另一个终端 cat 这个文件:

bash 复制代码
id,order_no,user_name,status,amount,created_at
1,ORD-20240601-001,alice,paid,1280.50,2026-06-12 14:39:09.209084
2,ORD-20240601-002,bob,pending,360.00,2026-06-12 14:39:09.209084
3,ORD-20240601-003,carol,shipped,899.00,2026-06-12 14:39:09.209084
4,ORD-20240601-004,alice,paid,75.20,2026-06-12 14:39:09.209084
5,ORD-20240601-005,dave,refund,2100.00,2026-06-12 14:39:09.209084

几个点:

  • 文件路径 /tmp/orders_full.csv本机路径(运行 ksql 的客户端这台机器),不是数据库服务器上的路径。
  • HEADER true 让第一行输出列名,运营拿到手直接能用。
  • 整表导出会带上所有字段,包括 idcreated_at

MySQL 里做同样的事是 SELECT * FROM ... INTO OUTFILE '/path/orders.csv':文件生成在数据库服务器上,需要 FILE 权限,开发机上还看不到。\copy 没有这些限制------文件落在哪、用什么账号,由运行 ksql 的人决定。另外 \copy 是 ksql 元命令,必须写在一行,不能像普通 SQL 那样折行。

只导需要的列和行

实际场景很少导整表。运营要的往往是「状态 paid 的订单,只要订单号、用户名、金额」。\copy 后面直接跟一段 SELECT 就行:

sql 复制代码
\copy (select order_no, user_name, amount from t_meta_demo where status = 'paid') TO '/tmp/orders_paid.csv' WITH (FORMAT CSV, HEADER true);

返回 COPY 2,文件内容:

复制代码
order_no,user_name,amount
ORD-20240601-001,alice,1280.50
ORD-20240601-004,alice,75.20

括号里是任意 SELECT,WHERE、字段筛选、ORDER BYJOIN 都能写。这是最实用的形式:不用建临时表,需要什么直接查出来写进文件。CSV 的列名取自 SELECT 的字段------加了别名就用别名,做模板化导出时这个细节很有用。

准备一份要导入的 CSV

反过来,运营发来一批要补录的新订单,CSV 格式。先用 !(在 ksql 里执行 shell 命令)造一个文件出来:

bash 复制代码
! printf 'order_no,user_name,status,amount\nORD-20240602-001,eve,paid,560.00\nORD-20240602-002,frank,pending,120.50\nORD-20240602-003,grace,shipped,3200.00\n' > /tmp/orders_import.csv

cat 确认内容:

lua 复制代码
order_no,user_name,status,amount
ORD-20240602-001,eve,paid,560.00
ORD-20240602-002,frank,pending,120.50
ORD-20240602-003,grace,shipped,3200.00

这份 CSV 只给了 order_nouser_namestatusamount 四列,没有 idcreated_at------它们一个走自增序列、一个走 now() 默认值,不需要在文件里提供。

把 CSV 导进表

导入时用括号指定 CSV 的列对应表里的哪几列:

sql 复制代码
\copy t_meta_demo (order_no, user_name, status, amount) FROM '/tmp/orders_import.csv' WITH (FORMAT CSV, HEADER true);

返回 COPY 3。查一下结果(这里开了展开模式,字段竖排更好认):

vbnet 复制代码
select * from t_meta_demo order by id;

原来的 5 条加上新导入的 3 条,一共 8 条。新数据的 id 由序列自动生成、created_at 自动填入导入时刻的时间------CSV 里没提供这两列,它们各自走了默认值。

两个关键点:

  • 列名括号 (order_no, user_name, status, amount) :告诉 ksql CSV 里的数据按顺序填进这几列,没列出来的列走默认值。不写这个括号时,ksql 会按表的全部列顺序去对 CSV,列数对不上就报错。
  • HEADER true 在导入时的作用和导出相反:导出时它让第一行输出列名;导入时它告诉 ksql「第一行是列名,跳过、别当数据」。同一个选项,两个方向各管一头。

MySQL 里对应的是 LOAD DATA INFILE,同样在服务端执行、文件要在服务器上,除非加 LOCAL 关键字走客户端。\copy 不挑这些,文件在 ksql 所在的机器上就能读。

\copy 和 COPY 只差一个反斜杠,执行的人完全不同

把前面成功的命令去掉反斜杠,用同一个 app_user 再跑一次:

sql 复制代码
COPY t_meta_demo TO '/tmp/test_server_copy.csv' WITH (FORMAT CSV, HEADER true);

这次直接报错:

vbnet 复制代码
ERROR:  must be superuser or a member of the sys_write_server_files role to COPY to a file
HINT:  Anyone can COPY to stdout or from stdin. ksql's \copy command also works for anyone.

同一张表、同样的路径和选项,加不加反斜杠,结果天差地别:

  • COPY(不带反斜杠)是 SQL 命令,由数据库服务端执行。 它要往文件写,路径是服务器上的路径 ,需要超级用户或 sys_write_server_files 角色。app_user 是普通用户,直接被拦下。
  • \copy(带反斜杠)是 ksql 元命令,由 ksql 进程在本机执行。 路径是客户端本机路径,用的是当前操作系统账号的文件权限,跟数据库里有没有特权无关------这正是前面几节普通用户都能跑通的原因。

报错的 HINT 把这层关系说得很直白:Anyone can COPY to stdout or from stdin. ksql's \copy command also works for anyone.------谁都能用 \copy

小结

记一条就够:看反斜杠

  • \copy:客户端命令,文件在你运行 ksql 的机器 上,用你的系统账号权限,普通数据库用户就能用。日常给运营导数据、补录数据,用它。
  • COPY:服务端命令,文件在数据库服务器 上,要超级用户或 sys_write_server_files 角色。它是 DBA 在服务端做批量迁移时用的。

开发日常最顺手的一条,是 \copy (SELECT ...) TO '文件' WITH (FORMAT CSV, HEADER true):条件、字段自己挑,HEADER true 带上列名,一条命令把运营要的数据落到本地。

最后一个编码上的坑:如果 CSV 是 Windows 下 Excel 导出的,多半是 GBK 编码,直接导入导出中文会乱码。在 WITH 里加 ENCODING 'GBK' 指定文件编码即可,不用先转码。

这也是 \copy 和 MySQL SELECT INTO OUTFILE / LOAD DATA INFILE 最本质的区别:不是语法不同,而是执行的那一端不同------一个在客户端,一个在服务端。想清楚文件该落在哪、用谁的权限,选哪个就清楚了。

相关推荐
ciqingloveless1 小时前
Oracle打开数据库加密
数据库·oracle
m0_462605222 小时前
大模型week02
数据库
DBA_G2 小时前
GBase 8a数据库集群多维度资源管控策略矩阵解析
数据库·oracle
逐米时代3 小时前
智能招聘与人岗匹配:可解释匹配让录用依据有迹可循
大数据·数据库·人工智能
承渊政道3 小时前
KES专业技能包发布:覆盖数据库开发、迁移与运维全流程
运维·数据库·gitee·数据库开发·金仓数据库
IvorySQL3 小时前
PostgreSQL 日报| RI 快速路径并发读取缺陷(8 月 13 日)
数据库·postgresql
highreport4 小时前
HighReport报表工具定时调度和邮件定时发送
运维·服务器·数据库
01_ice4 小时前
数据库基础
数据库
deepdata_cn4 小时前
向量数据库是非结构化数据的AI检索底座
数据库·人工智能