trdsql 1.2.3 常用 CLI 命令与工具详解
trdsql v1.2.3 高频命令实测全回显:文件SQL查询/聚合JOIN、文本类型坑与CAST修正、十余种格式转换与压缩输出、stdin管道/通配符多文件/-q/-t/-a分析器、文件×PostgreSQL库表JOIN与窗口函数、CREATE TABLE AS/INSERT INTO写回SQLite与PostgreSQL、-db命名连接,附9条注意事项与5条排障实录
trdsql 1.2.3 常用 CLI 命令与工具详解
目标:以实测回显逐条讲透 trdsql v1.2.3 的高频命令组合——文件 SQL 查询、格式转换、管道/通配/查询文件、文件×数据库 JOIN、写回 SQLite/PostgreSQL 目标库、文件分析器,并沉淀文本类型、表头行、通配符等真实踩坑实录。参数含义与配置文件机制见前篇《trdsql 1.2.3 配置参数详解》,本文专注"命令怎么用"。
一、环境信息
- 操作系统:Ubuntu 24.04.5 LTS(x86_64),主机名
trdsql-server,内网 10.89.56.2/24 - trdsql:v1.2.3(
/usr/local/bin/trdsql,官方二进制安装,见 《Ubuntu 24.04 安装 trdsql 1.2.3 指南》) - 查询引擎:默认内嵌 SQLite3;PostgreSQL 16.15 数据库服务器
db-server(10.89.56.3,演示账号root/q1w2e3r4,库trdsql,下文 DSN 简写为PGDSN="host=db-server user=root password=q1w2e3r4 dbname=trdsql sslmode=disable") - SQLite 文件库:
testdata/fruits.db(sqlite3 CLI 预建表fruits(id,name,price))、testdata/sales.db(trdsql 导出建表sales) - 命名连接配置:
~/.config/trdsql/config.json(pdb=postgres、sdb/ldb=sqlite3,见 C1 篇 5.2) - 测试数据(
/opt/work/testdata/):
| 文件 | 格式 | 内容 |
|---|---|---|
| test.csv | CSV 无表头 | 1,Orange / 2,Melon / 3,Apple |
| sales.csv | CSV 有表头 | id,name,price,city 共 8 行 |
| user.csv / hist.csv | CSV 无表头 | 用户与访问日期,供 JOIN |
| test.tsv / test.ltsv | TSV/LTSV | 同 test.csv 三行 |
| test.json | JSON 对象 | {"items":[{id,name,price}×3]} |
| test.jsonl | JSON Lines | 3 行对象 |
| test.yaml | YAML | 3 个对象 |
| sales.csv.gz | gzip 压缩 CSV | 同 sales.csv |
| tt1.csv / tt2.csv | CSV 有表头 | 供通配符多文件实测 |
二、工具总览
trdsql 一条命令 = 引擎选择 + 输入选项 + SQL + 输出选项 四段拼装:
trdsql [-driver/-dsn/-db 引擎] [-i* 输入] [-ih/-id/-ijq 等] "SQL(表名=文件名)" [-o* 输出/-out 文件]
- SQL 里文件名即表名:
FROM testdata/sales.csv;标准输入用FROM -或FROM stdin; - 无表头文件的列名自动为
c1,c2,...,加-ih后首行变列名; - 所有选项必须在 SQL 之前(Go flag 机制,C1 篇 FAQ Q1 实测);
- 默认引擎内嵌 SQLite3,
-driver postgres后获得 PostgreSQL 方言与库内表访问能力。
三、前置条件
- trdsql 已安装(
trdsql -version回显trdsql version v1.2.3); - 测试数据就位(上表文件);
- 涉及数据库的章节(4.15–4.18)需 PostgreSQL 服务器可达:
psql "$PGDSN" -c "SELECT 1;"有回显;SQLite 章节需sqlite3客户端(aptsqlite3,3.45.1)。
四、各命令详解
4.1 基础查询:文件名即表名
$ trdsql "SELECT * FROM testdata/test.csv"
1,Orange
2,Melon
3,Apple
无表头时列名是 c1,c2,可显式引用:SELECT c1,c2 FROM testdata/test.csv。
4.2 表头、过滤与聚合:-ih + WHERE/ORDER/GROUP BY
$ trdsql -ih "SELECT name, price, city FROM testdata/sales.csv WHERE price > 200 ORDER BY price DESC"
Cherry,990,Guangzhou
Banana,90,Guangzhou
Grape,680,Shanghai
Orange,380,Beijing
Apple,250,Beijing
Mango,210,Shanghai
$ trdsql -ih "SELECT city, count(*) AS cnt, sum(price) AS total FROM testdata/sales.csv GROUP BY city ORDER BY total DESC"
Guangzhou,2,1080
Shanghai,3,1010
Beijing,3,780
⚠️ 注意:第一条的过滤与排序都不对——
Banana,90不仅通过了> 200,还排在Grape,680之前。原因见 4.3:文本列按字典序比较('90' > '200'成立,'150'/'120'反而不成立)。GROUP BY 的sum(price)恰因 SQLite 对纯数字文本做隐式数值转换而正确(1080/1010/780)。
4.3 文本类型坑与修正:CAST / ::int
trdsql 把 CSV 列全部按 text 装载(-a 实测 Data types 见 4.13),数值比较/排序必须显式转换:
# SQLite 引擎:CAST
$ trdsql -ih "SELECT name, CAST(price AS INTEGER) p FROM testdata/sales.csv WHERE CAST(price AS INTEGER) > 200 ORDER BY p DESC"
name,p
Cherry,990
Grape,680
Orange,380
Apple,250
Mango,210
# PostgreSQL 引擎::: 类型转换
$ trdsql -db pdb -ih "SELECT city, count(*) AS n, SUM(price::int) AS total FROM testdata/sales.csv GROUP BY city ORDER BY total DESC"
city,n,total
Guangzhou,2,1080
Shanghai,3,1010
Beijing,3,780
第一条 Banana,90 已正确消失、排序正确;第二条用 PG 引擎直接输出数值列并带表头(-oh 见 4.6)。
4.4 文件 × 文件 JOIN
$ trdsql "SELECT u.c1,u.c2,h.c2 FROM testdata/user.csv AS u LEFT JOIN testdata/hist.csv AS h ON(u.c1=h.c1)"
1,userA,2026-10-01
2,userB,2026-10-01
2,userB,2026-10-02
两个文件各成一张临时表,JOIN 语法与所选引擎一致。
4.5 标准输入管道:FROM - / FROM stdin
$ cat testdata/test.csv | trdsql -icsv -otsv "SELECT * FROM -"
1 Orange
2 Melon
3 Apple
$ cat testdata/test.csv | trdsql "SELECT * FROM stdin"
1,Orange
2,Melon
3,Apple
表名写 - 或 stdin 均可;管道输入建议显式 -icsv(stdin 无扩展名可猜,实测不写时按 CSV 处理)。
4.6 输出格式转换全家族
# CSV → JSON
$ trdsql -ih -ojson "SELECT name,price FROM testdata/sales.csv LIMIT 2"
[
{
"name": "Orange",
"price": "380"
},
{
"name": "Melon",
"price": "120"
}
]
# CSV → JSON Lines / LTSV
$ trdsql -ih -ojsonl "SELECT name,price FROM testdata/sales.csv LIMIT 2"
{"name":"Orange","price":"380"}
{"name":"Melon","price":"120"}
$ trdsql -ih -oltsv "SELECT name,price FROM testdata/sales.csv LIMIT 2"
name:Orange price:380
name:Melon price:120
# CSV → Markdown 表格 / ASCII 表格 / 垂直格式
$ trdsql -ih -omd "SELECT name,price FROM testdata/sales.csv LIMIT 3"
| name | price |
|--------|-------|
| Orange | 380 |
| Melon | 120 |
| Apple | 250 |
$ trdsql -ih -ovf "SELECT name,price FROM testdata/sales.csv WHERE city='Beijing' LIMIT 2"
---[ 1]------------------------
name | Orange
price | 380
---[ 2]------------------------
name | Apple
price | 250
# CSV → TBLN(注释头风格,实测 v1.2.3 两种引擎一致)
$ trdsql -ih -otbln "SELECT id,name FROM testdata/sales.csv LIMIT 2"
; name: | id | name |
; type: | text | text |
| 1 | Orange |
| 2 | Melon |
# 其他输入格式 → CSV/TSV
$ trdsql -ijson "SELECT * FROM testdata/test.jsonl"
1,Orange,380
2,Melon,120
3,Apple,250
$ trdsql -iltsv -otsv "SELECT * FROM testdata/test.ltsv"
id name price
1 Orange 380
2 Melon 120
3 Apple 250
$ trdsql -iyaml "SELECT * FROM testdata/test.yaml"
1,Orange,380
2,Melon,120
3,Apple,250
输出侧控制项实测:
$ trdsql -otsv -oh "SELECT * FROM testdata/test.csv" # 输出表头
c1 c2
1 Orange
2 Melon
3 Apple
$ trdsql -ih -od ";" "SELECT name,city FROM testdata/sales.csv LIMIT 2" # 换分隔符
Orange;Beijing
Melon;Shanghai
$ trdsql -ih -oaq "SELECT name,city FROM testdata/sales.csv LIMIT 2" # 全字段引号
"Orange","Beijing"
"Melon","Shanghai"
$ trdsql -ih -oraw "SELECT name, price FROM testdata/sales.csv LIMIT 2" # raw 无引号
Orange,380
Melon,120
$ trdsql -ih -inum -omd "SELECT name,price FROM testdata/sales.csv LIMIT 2" # 行号列
| row_number | name | price |
|------------|--------|-------|
| 1 | Orange | 380 |
| 2 | Melon | 120 |
4.7 输出到文件与压缩:-out / -oz
-out 按文件名后缀自动猜格式;-oz gz|bz2|zstd|lz4|xz 叠加压缩:
$ trdsql -ih -out out_sales.json "SELECT name,price FROM testdata/sales.csv LIMIT 2"
$ cat out_sales.json
[
{
"name": "Orange",
"price": "380"
},
{
"name": "Melon",
"price": "120"
}
]
$ trdsql -ih -out out_sales.csv.gz -oz gz "SELECT name,price FROM testdata/sales.csv LIMIT 2"
$ ls -la out_sales.csv.gz
-rw-r--r-- 1 root root 45 Nov 3 04:28 out_sales.csv.gz
$ gunzip -c out_sales.csv.gz
Orange,380
Melon,120
$ trdsql -ih -out out_sales.csv.zst -oz zstd "SELECT name FROM testdata/sales.csv LIMIT 2"
$ ls -la out_sales.csv.zst
-rw-r--r-- 1 root root 26 Nov 3 04:34 out_sales.csv.zst
4.8 压缩输入透明读取
.gz/.bz2/.zst/.lz4/.xz 输入自动解压(-ig 默认开启):
$ trdsql -ih "SELECT * FROM testdata/sales.csv.gz LIMIT 3"
id,name,price,city
1,Orange,380,Beijing
2,Melon,120,Shanghai
3,Apple,250,Beijing
4.9 JSON 层级取数:-ijq 与 表名::".jq表达式"
# 方式一:-ijq 标志
$ trdsql -ijson -ijq ".items" "SELECT * FROM -" < testdata/test.json
1,Orange,380
2,Melon,120
3,Apple,250
# 方式二:表名后缀(jq 表达式必须用双引号包住)
$ trdsql -oat 'SELECT id,name,price FROM testdata/test.json::".items"'
+----+--------+-------+
| id | name | price |
+----+--------+-------+
| 1 | Orange | 380 |
| 2 | Melon | 120 |
| 3 | Apple | 250 |
+----+--------+-------+
⚠️ 注意:官方 README 示例
SELECT c1,c2 FROM test.json::.items直接照抄会报错(实测):
$ trdsql -oltsv "SELECT c1,c2 FROM testdata/test.json::.items"
2026-11-03 04:26:59 export: no such column: c1 [SELECT c1,c2 FROM testdata/test.json::jq1]
jq 取出的对象数组列名即 JSON 键(id/name/price),不是 c1,c2;c1,c2 只适用于"数组的数组"型 JSON。
4.10 通配符多文件
$ trdsql -ih "SELECT * FROM testdata/tt*.csv"
1,test1
2,test2
id,name
3,test3
⚠️ 注意:
-ih只把第一个文件的首行当表头,第二个文件的表头行id,name作为数据混入结果(实测回显第 3 行)。多文件同构场景要么全部无表头,要么用 SQL 过滤WHERE id <> 'id',或逐文件处理。
4.11 整文件格式转换:-t
不写 SQL、整文件转格式时用 -t:
$ trdsql -otsv -t testdata/test.csv
1 Orange
2 Melon
3 Apple
4.12 SQL 存文件:-q
$ printf "SELECT name, price FROM testdata/sales.csv WHERE city = 'Beijing' ORDER BY CAST(price AS INT) DESC;\n" > q.sql
$ trdsql -ih -q q.sql
Orange,380
Apple,250
Peach,150
⚠️ 注意:
-q文件里的 SQL 同样受表头规则约束——实测不加-ih时按列名name引用会直接报错(SQLite 引擎回显export: no such column: name,PostgreSQL 引擎回显pq: column "name" does not exist)。
4.13 文件分析器:-a / -A
拿到陌生文件先 -a 探测:
$ trdsql -a testdata/sales.csv
File name: testdata/sales.csv
File type: CSV
Number of columns: 4
Data types: [text,text,text,text]
Column names: [c1,c2,c3,c4]
Suggested SQL:
SELECT c1,c2,c3,c4 FROM testdata/sales.csv
Data types 全为 text 即 4.3 文本类型坑的官方"自供"。-A 只给建议 SQL 不给探测信息,且是一组命令(实测 4 条,name 因属 SQL 保留字被自动加引号):
$ trdsql -ih -A testdata/sales.csv
trdsql -ih "SELECT id, \"name\", price, city FROM testdata/sales.csv"
trdsql -ih "SELECT id, \"name\", price, city FROM testdata/sales.csv WHERE id = '1'"
trdsql -ih "SELECT id, count(id) FROM testdata/sales.csv GROUP BY id"
trdsql -ih "SELECT id, \"name\", price, city FROM testdata/sales.csv ORDER BY id LIMIT 10"
4.14 多查询串执行:分号分隔
$ trdsql "UPDATE testdata/test.csv SET c2='banana' WHERE c1='3'; SELECT * FROM testdata/test.csv"
1,Orange
2,Melon
3,banana
⚠️ 注意:UPDATE 只改内存临时表,原文件不变(实测执行后
cat testdata/test.csv仍为3,Apple)。多查询共享同一临时表,常用于"清洗→查询"两段式。
4.15 查询已有 SQLite 文件
# fruits.db 由 sqlite3 CLI 预建:CREATE TABLE fruits(id INTEGER PRIMARY KEY, name TEXT, price INTEGER)
$ trdsql -driver sqlite3 -dsn testdata/fruits.db "SELECT name, price FROM fruits WHERE price > 200 ORDER BY price DESC"
Orange,380
Apple,250
DSN 即库文件路径;此时 SQL 跑在真实 .db 文件上,列有真实类型(INTEGER),无 4.3 文本坑。
4.16 导出 CSV 到全新 SQLite 库
$ rm -f testdata/sales.db
$ trdsql -ih -driver sqlite3 -dsn testdata/sales.db "CREATE TABLE sales AS SELECT * FROM testdata/sales.csv"
$ sqlite3 testdata/sales.db ".tables"; sqlite3 testdata/sales.db "SELECT COUNT(*) FROM sales;"
sales
8
导入后列类型仍是 TEXT(CREATE TABLE AS 按源文本推断,实测):
$ sqlite3 testdata/sales.db "PRAGMA table_info(sales);"
0|id|TEXT|0||0
1|name|TEXT|0||0
2|price|TEXT|0||0
3|city|TEXT|0||0
查询时同样要 CAST(实测):
$ trdsql -driver sqlite3 -dsn testdata/sales.db "SELECT city, SUM(CAST(price AS INT)) AS total FROM sales GROUP BY city ORDER BY total DESC"
Guangzhou,1080
Shanghai,1010
Beijing,780
4.17 PostgreSQL 引擎:JOIN 库内表、窗口函数、生成序列
前置:在 db-server 上建参照表(实测):
$ psql "host=db-server user=root password=q1w2e3r4 dbname=trdsql" -c "CREATE TABLE colors(id int, name text); INSERT INTO colors VALUES(1,'orange'),(2,'green'),(3,'red');" -c "SELECT * FROM colors;"
CREATE TABLE
INSERT 0 3
id | name
----+--------
1 | orange
2 | green
3 | red
(3 rows)
文件 × 库内表 JOIN(CSV 临时表与 colors 直接关联,::int 走 PG 方言):
$ trdsql -driver postgres -dsn "$PGDSN" "SELECT t.c1,t.c2,c.name FROM testdata/test.csv AS t LEFT JOIN colors AS c ON (t.c1::int = c.id)"
1,Orange,orange
2,Melon,green
3,Apple,red
窗口函数(SQLite3 引擎不支持 generate_series、窗口写法受限,PG 引擎全量可用):
$ trdsql -driver postgres -dsn "$PGDSN" "SELECT row_number() OVER (ORDER BY c2), c1, c2 FROM testdata/test.csv"
1,1,Orange
2,2,Melon
3,3,Apple
$ trdsql -driver postgres -dsn "$PGDSN" "SELECT generate_series(1,3);"
generate_series
1
2
3
⚠️ 注意:
-driver postgres时FROM testdata/sales.csv的计数含表头行(实测count(*)=9),加-ih后为 8——引擎切换不改变装载语义。
4.18 导入 CSV 到 PostgreSQL 目标库
建表导入(CREATE TABLE ... AS SELECT,列类型由 :: 转换决定):
$ trdsql -driver postgres -dsn "$PGDSN" "CREATE TABLE fruits (id, name) AS SELECT c1::int, c2 FROM testdata/test.csv"
$ psql "host=db-server user=root password=q1w2e3r4 dbname=trdsql" -c "SELECT * FROM fruits;"
id | name
----+--------
1 | Orange
2 | Melon
3 | Apple
(3 rows)
追加导入(先建好带类型目标表,再 INSERT INTO ... SELECT,实测用 -db pdb 命名连接):
$ psql "host=db-server user=root password=q1w2e3r4 dbname=trdsql sslmode=disable" -c "CREATE TABLE sales(id int, name text, price int, city text);"
CREATE TABLE
$ trdsql -db pdb -ih "INSERT INTO sales SELECT id::int, name, price::int, city FROM testdata/sales.csv"
$ psql "host=db-server user=root password=q1w2e3r4 dbname=trdsql sslmode=disable" -c "SELECT city, sum(price) FROM sales GROUP BY city ORDER BY 2 DESC;"
city | sum
-----------+------
Guangzhou | 1080
Shanghai | 1010
Beijing | 780
(3 rows)
price 落表即为 int(sum 无需再转换)——这是 trdsql 相对 csvq/dsq/OctoSQL/textql 的独有强项:一条命令完成"读文本→转换类型→写库"。
4.19 命名连接速查:-db / -dblist
配置文件 ~/.config/trdsql/config.json 就位后(结构见 C1 篇 5.2):
$ trdsql -dblist
pdb:postgres
ldb:sqlite3
sdb:sqlite3
$ trdsql -db pdb "SELECT name FROM fruits ORDER BY id"
Orange
Melon
Apple
$ trdsql -db sdb "SELECT name, price FROM fruits WHERE price > 200"
Orange,380
Apple,250
同名表 fruits 在 pdb(PostgreSQL)与 sdb(SQLite 文件)中各有一份,-db 即路由开关。
五、注意事项
- 选项必须在 SQL 之前:SQL 之后的
-otsv等会被拼进语句报near "-": syntax error(C1 篇 FAQ Q1 实测); - 列默认全 text:数值过滤/排序一律
CAST(x AS INTEGER)(SQLite)或x::int(PostgreSQL),见 4.3; -ih决定计数口径:不加时表头行算数据(count(*)多 1,4.17 实测 9 vs 8);- 通配符多文件只认第一个表头(4.10);
- UPDATE/DELETE 只作用于内存临时表,不回写文件(4.14);
-debug打印 DSN 明文(含密码),日志外发前脱敏(C1 篇 5.3);- config.json 的默认
db会改变报错前缀(export:vspq:),排障时先确认生效引擎(C1 篇 FAQ Q3); - TBLN 输出为注释头风格(
; name:/; type:行,4.6 实测),与部分文档的 YAML 头示例不同,以所用版本实测为准。
六、常见问题
Q1: SELECT ... FROM x.csv 数值条件"不生效"(90 通过了 > 200)?
A: 文本字典序比较(4.2 实测复现、4.3 修正)。加 -ih 不解决问题,必须显式 CAST/::。
Q2: count(*) 比行数多 1?
A: 表头行被当数据装载。-ih 修正(4.17 实测 9→8),或 -is 1 跳过首行(不建列名)。
Q3: test.json::.items 报 no such column: c1?
A: 两个叠加原因(4.9 实测):jq 表达式应写成 ".items"(表名内双引号);且对象数组的列名是 JSON 键(id/name/price),不是 c1,c2。
Q4: 多文件 FROM *.csv 结果里混进 id,name 行?
A: 只有第一个文件的首行被当表头(4.10 实测)。同构多文件建议先合并或 SQL 过滤。
Q5: -db pdb -ih "SELECT name, price FROM fruits" 报 pq: column "price" does not exist?
A: 不是 bug:PostgreSQL 里 4.18 导入的 fruits 表只有 (id, name) 两列,而 SQLite 文件里的 fruits 有 price——换引擎即换表结构(实测报错原样见 C1 篇 FAQ Q3)。先 -db pdb "SELECT * FROM fruits LIMIT 1" 看列再写 SQL。
七、参考链接
- 官方 README Usage/Examples:https://github.com/noborus/trdsql (查询日期 2026-11-03)
- 官方文档站:https://noborus.github.io/trdsql/index.html
- 站内:《trdsql 介绍与选型指南》、《Ubuntu 24.04 安装 trdsql 1.2.3 指南》、《trdsql 1.2.3 配置参数详解》