7 个值得复用的 DuckDB SQL 模式
哎我跟你们说…我这两天坐在工位上,脑袋昏昏沉沉的,本来想着喝口咖啡醒醒神,结果刚坐下就有人拍我肩膀问我:“东哥你之前不是老玩 DuckDB 嘛?有没有那种写 SQL 特别顺手、能复用的模式啊?”我当时心里吐槽:你这问题问得,我昨天半夜还在调一个窗口函数…算了算了我整理下我平时最常用的 7 个模式,免得你们又半夜加班。
1. 直接用 CTE 当“数据管道”
我昨天晚上十点多在公司楼下吃凉皮的时候,小李突然在群里问:“哥我这个 SQL 老是越写越长怎么办?” 我说你用 CTE 啊,就是 WITH 开头那个,DuckDB 对 CTE 优化其实挺聪明的,不会瞎重复计算。
大概这样:
WITHrawAS (
SELECT * FROM read_csv_auto('users.csv')
),
clean AS (
SELECTid, LOWER(email) AS email FROMraw
)
SELECT * FROM clean WHERE email LIKE'%gmail%';
就像埋水管一样,一段接一段,越长越不怕乱。
2. 把 Python 当存储过程用
这个是我最爱说的…前天晚上十一点多我还在办公室跟人吹这个。DuckDB 内置 Python UDF 真是爽。比如你要做点复杂清洗:
import duckdbdefnorm(x):
return x.strip().lower()
duckdb.create_function("norm", norm)
duckdb.sql("""
SELECT norm(name) as name_cleaned
FROM read_csv_auto('data.csv')
""")
这模式的好处就是:你不用到处 copy-paste 那些清洗逻辑,像写本地函数一样随便复用。
3. 小表 join 大表,先过滤再连
这点看似老生常谈,但是 DuckDB 的优化器有时候不会替你做“谓词下推”,尤其当 ETL 管道里读了 parquet 又套一堆 view 的时候。 我前几天跑一个 2000w 行的订单表,把过滤提前,速度嗖地就上去了:
WITH f AS (
SELECT user_id FROMusersWHERE country='CN'
)
SELECT o.*
FROM f
JOIN orders o USING(user_id);
反正记住一句话:把能变小的数据尽量提前弄小。
4. 行转列、列转行模式(DuckDB 超强)
我那天在茶水间跟隔壁组的妹子说 DuckDB 的 pivot/unpivot 好用,她愣是以为我在炫技… 其实你看例子就懂:
SELECT *
FROMpivot(
SELECT user_id, event, cnt FROM stats
) ONeventUSINGavg(cnt);
以前要靠复杂 CASE WHEN,现在就特别丝滑。
5. 用 read_parquet / read_csv_auto 做“临时表”
这个模式我基本每天都用。DuckDB 的外部文件扫描是真快,你不需要建库建表,直接查文件:
SELECT *
FROM read_parquet('logs/*.parquet')
WHERElevel = 'ERROR';
我经常在本地调日志,简直爽到没朋友。
6. 使用窗口函数做“组内排名/去重”
前几天凌晨两点多,我在给演示 Demo,突然发现数据重复,我直接来一句窗口函数搞定:
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITIONBY user_id ORDERBY ts DESC) AS rn
FROMevents
)
WHERE rn = 1;
你别看简单,但这个模式几乎能解决 80% 的“最新一条记录”问题。
7. 用 struct_pack / list_agg 写“半结构化结果”
这个模式特别适合直接导给 API 或者给 Notebook 用。 比如你要把用户的订单聚成列表:
SELECT user_id,
list_agg(struct_pack(id, amount)) AS orders
FROM orders
GROUPBY user_id;
我第一次用的时候还以为自己在写 Python,结果特别顺手。
哎我本来还想继续说,但我手机又在响,估计又有同事问我啥 Bug 了。 总之这 7 个模式我真是天天复用,你们只要记住一半都够用很久了。 算了我先去接个电话…等下要是我想起什么别的 DuckDB 小技巧,我再发一条语音。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB