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

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

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

这篇在 KingbaseES V009R001C010 环境里验证 \copy 的三件事:把整张表导成 CSV、按条件只导需要的列和行、再把 CSV 导回表;最后用一条不带反斜杠的 COPY 触发报错,说清 \copy 和 COPY 到底差在哪。演示用普通用户 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_user,search_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 让第一行输出列名,运营拿到手直接能用。
  • 整表导出会带上所有字段,包括 id 和 created_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 BY、JOIN 都能写。这是最实用的形式:不用建临时表,需要什么直接查出来写进文件。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_no、user_name、status、amount 四列,没有 id 和 created_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 最本质的区别:不是语法不同,而是执行的那一端不同------一个在客户端,一个在服务端。想清楚文件该落在哪、用谁的权限,选哪个就清楚了。

相关推荐
雨落在了我的手上4 分钟前
MySQL数据库基础(12):事务
数据库·mysql
D姐聊门店7 分钟前
门店收银系统断网兜底怎么设计:本地账本、离线续传与日终对账的工程实现
数据库·saas
用户86629784800825 分钟前
抓包看 KingbaseES 内部协议?官方给了个现成工具
数据库
wefg130 分钟前
【Redis】安装与启动
数据库·redis·缓存
大圣编蚕1 小时前
常见的 Redis 面试题(高频整理)
数据库·redis·缓存
派小心.1 小时前
网站统计采样数据的误差核对
大数据·服务器·前端·数据库·人工智能·小程序·数据分析
龙腾AI白云2 小时前
数字孪生、具身智能、世界模型三者交叉
数据库·人工智能·机器学习·flask·pygame
SelectDB技术团队2 小时前
Apache Doris 基于 Lance 的向量检索深度解析
数据库·数据仓库·数据分析·湖仓一体·apache doris·lakehouse·多模湖仓
这个DBA有点耶2 小时前
从INDEX到JOIN_ORDER:MySQL 8.0 Hint语法体系与五类实战场景
数据库·mysql·架构
数字新视界2 小时前
机房动环监控助力智能管理
大数据·服务器·数据库·动环监控系统·动力与环境监控系统