一招减少 80% 的 SQL 代码:COLUMNS()、EXCLUDE 和 REPLACE

还在手动敲 50 个字段名?DuckDB 的 COLUMNS()、EXCLUDE 和 REPLACE 让你一行代码操作整组列,宽表查询从此告别重复劳动。

一、问题场景:你的键盘在抗议

每个数据分析师都经历过这个场景:面对一张 50 列的大宽表,你只是想:

  1. 查询除了 idcreated_at 之外的所有字段
  2. 把所有 VARCHAR 列统一转成 INTEGER 做批量导入
  3. 给符合某个命名模式的列都应用同样的转换

没有 DuckDB 的列表达式快捷方式,你只有三条路:手动敲 50 个字段名(累)、写脆弱的动态 SQL(险)、或者复制粘贴改到手软(烦)。

传统做法——逐个列出字段:

SELECT
    name, age, salary, department, hire_date, email, phone,
    address, city, state, zip, country, manager_id, team_id,
    -- ... 再来 30 个字段 ...
    last_login, status, notes
FROM employees;

打错一个字段名,查询就炸。改一次表结构,所有查询都得改。

二、解决方案:DuckDB 的列表达式三件套

DuckDB 提供了三个 SQL 扩展,让字段管理从体力活变成一行代码。

1. SELECT * EXCLUDE —— 一句话排除不需要的列

-- 不用列 50 个字段名,只需要排除 2 个:
SELECT * EXCLUDE (id, created_at) FROM employees;

宽表查询的救命稻草:「除了这几列,我全都要」。

2. SELECT * REPLACE —— 原地替换,不破不立

需要清洗个别字段,但不想破坏整个 SELECT 结构?

SELECT * REPLACE (
    COALESCE(email, 'no-email@example.com') AS email,
    UPPER(name) AS name
)
FROM employees;

* 展开所有字段,REPLACE 把指定列替换成你处理后的版本——列顺序保持不变。

3. COLUMNS() —— 按模式批量操作列

这是真正的杀手锏。COLUMNS() 接受正则表达式或 lambda 表达式,对所有匹配的列执行同一操作:

-- 把所有以 "price_" 开头的列转成 DOUBLE
SELECT COLUMNS('price_.*')::DOUBLE FROM transactions;

-- 对全部数值列求和
SELECT SUM(COLUMNS(c -> c::DOUBLE)) FROM mixed_types;

-- 对所有文本列统一转大写
SELECT COLUMNS(c -> UPPER(c::VARCHAR)) FROM messy_data;

还可以按数据类型过滤:

-- 统计所有 INTEGER 类型的非空值
SELECT COLUMNS(c -> COUNT(c::INTEGER)) FROM wide_table;

💡 实战技巧: COLUMNS() 的 lambda 参数接收每个列的结构体 {name, data, type},所以你可以根据列名、数据类型甚至数据内容来做条件过滤。

组合使用:一个查询搞定一切

来看一个真实的 ETL 场景——如果用传统 SQL 写,至少需要 20 行:

SELECT
    * EXCLUDE (id, _metadata, raw_payload),
    COLUMNS('price_')::DECIMAL(18,2),
    COLUMNS('qty_')::INTEGER,
    COLUMNS('date_')::DATE
REPLACE (
    COALESCE(email, '未知') AS email
)
FROM staging_products
WHERE COLUMNS('flag_')::BOOLEAN IS NOT NULL;

一条查询,零个手动列出的字段名,而且表结构变了也自动适配。

三、效果量化

场景改造前(敲了多少字)改造后节省比例
从 50 列中选 48 列~500 字符,逐一列举* EXCLUDE (id, created_at) = 35 字符93% 的按键减少
12 个 price 列转 DECIMAL~300 字符,12 行重复操作COLUMNS('price_')::DECIMAL(18,2) = 34 字符89% 代码精简
20 个文本列批量大写~600 字符,复制粘贴改到手软COLUMNS(c -> UPPER(c)) = 26 字符96% 缩减
表结构新增 3 个字段手动更新每个查询无需修改——查询自动适配维护时间趋近于零

在一条包含 15 张宽表、40+ 查询的生产数据管道中,改用 EXCLUDE/COLUMNS 后:

  • 删除了 3,000+ 行 重复的字段列举代码
  • 每月节省约 6 小时 的维护时间
  • 新增字段再也不需要改动查询语句

四、兼容性说明

这些是 DuckDB 专属 的 SQL 扩展(PostgreSQL 有部分 EXCLUDE 支持,通过 TABLE 语法)。

这是 DuckDB 的设计哲学:大多数分析查询是手写或由工具生成的,开发者的编码体验比严格的 SQL 标准兼容更重要。如果日后需要迁移数据库,只需要调整这些列表达式部分——其余 SQL 完全不变。

五、总结

如果你经常和宽表打交道(谁不是呢?),EXCLUDEREPLACECOLUMNS() 会是你在其他数据库中最想念的三个功能。它们让 DuckDB 从"又一个 SQL 引擎"变成了一个真正能提升开发效率的环境。

今天就试试:打开你最乱的 ETL 查询,数一数能用 COLUMNS() 消灭多少字段名——我猜至少 40%。


订阅 DuckDB Lab,每周三获取一篇能立刻用上的实战技巧——零理论,百分百可执行。

本文是周三快讯系列的一部分。想看深度长文?周六见。