Featured image of post DuckDB 宏(Macros)完全指南:把重复 SQL 片段打包成可复用函数

DuckDB 宏(Macros)完全指南:把重复 SQL 片段打包成可复用函数

DuckDB 宏(Macros)让你把常用 SQL 逻辑打包成带参数的可复用函数,替代复制粘贴的 CTE 碎片。涵盖基础语法、默认参数、结构化返回值、与视图的对比,以及实战变现建议。

DuckDB 宏(Macros)完全指南:把重复 SQL 片段打包成可复用函数

TL;DR:DuckDB 的 CREATE MACRO 让你把反复出现的 SQL 逻辑封装成带参数的函数,一行调用替代整段 CTE。适合报表模板、通用计算、跨项目复用。本文从基础到实战,覆盖参数默认值、结构化返回、与视图的对比,以及如何用宏搭建可变现的数据产品。

DuckDB 宏架构图


一、为什么需要宏?

你在写 SQL 时是否遇到过这些场景:

  • 每个月都要写同样的"工作日计算"逻辑,复制粘贴了 5 次
  • 某个复杂的日期处理表达式散布在 10 个查询里,改一处要动 10 个文件
  • 团队里每个人都有自己的"工具 CTE",版本不一致经常出 bug

传统解决方案是用视图(VIEW),但视图有两个致命缺陷:

  1. 不能带参数——你没法写 SELECT * FROM get_weekly_report('2026-08-01')
  2. 不能组合表达式——视图只能返回整个表,不能返回一个计算值

DuckDB 的 宏(Macro) 就是为了解决这两个问题而生的。


二、基础用法:一行定义,随处调用

2.1 最简单的宏

-- 定义:计算两个日期之间的工作日天数
CREATE MACRO workdays(start_date, end_date) AS
  (end_date::DATE - start_date::DATE + 1)
  - (date_part('dow', start_date::DATE) + date_part('dow', end_date::DATE));

-- 调用
SELECT workdays('2026-08-10', '2026-08-17') AS result;
-- 返回:5(去掉了周六周日)

注意:宏体是一个表达式,不是完整的 SELECT 语句。这让宏可以嵌入到任何 SQL 表达式的位置。

2.2 多表达式宏

-- 定义:判断是否为节假日(简化版)
CREATE MACRO is_holiday(date_val) AS
  CASE date_val::DATE
    WHEN '2026-01-01' THEN true
    WHEN '2026-05-01' THEN true
    WHEN '2026-10-01' THEN true
    ELSE false
  END;

-- 在查询中使用
SELECT
  order_date,
  amount,
  is_holiday(order_date) AS is_holiday_flag
FROM orders
WHERE is_holiday(order_date);

三、高级用法:默认参数与结构化返回

3.1 带默认值的参数

-- 安全除法:除数为 0 时返回默认值
CREATE MACRO safe_divide(a, b, default_val := 0) AS
  CASE WHEN b = 0 THEN default_val ELSE a / b END;

SELECT
  safe_divide(10, 3) AS normal,    -- 3.333...
  safe_divide(10, 0) AS zero;      -- 0

默认参数让你在不传所有参数的情况下也能调用宏,非常灵活。

3.2 返回结构化数据(struct)

-- 把日期拆成年/月/日
CREATE MACRO parse_date(d) AS
  struct_pack(
    year  := extract('year'  FROM d),
    month := extract('month' FROM d),
    day   := extract('day'   FROM d)
  );

SELECT parse_date('2026-08-13'::DATE);
-- 返回:{'year': 2026, 'month': 8, 'day': 13}

-- 解构使用
SELECT
  (parse_date(order_date)).year  AS order_year,
  (parse_date(order_date)).month AS order_month,
  SUM(amount) AS total
FROM orders
GROUP BY order_year, order_month;

3.3 列表宏(LIST 返回)

-- 生成 N 个连续日期
CREATE MACRO date_range(start, count) AS
  generate_series(start, start + INTERVAL (count - 1) DAY, INTERVAL 1 DAY);

SELECT date_range('2026-08-01', 7);
-- 返回:[2026-08-01, 2026-08-02, ..., 2026-08-07]

四、宏 vs 视图 vs CTE:怎么选?

特性宏 (MACRO)视图 (VIEW)CTE
带参数
返回标量值
返回表
跨会话持久✅(临时)/ ✅(持久)
代码复用
性能优化内联展开物化可选每次重算

选择原则

  • 需要参数化的表达式 → 用宏
  • 需要返回整张表且被多处引用 → 用视图
  • 单次查询内的临时逻辑 → 用 CTE
  • 需要跨会话持久化的复杂查询 → 用物化视图

五、实战场景:三个马上能用的宏

5.1 金额格式化宏

CREATE MACRO fmt_money(val, currency := '¥') AS
  currency || ROUND(val, 2);

SELECT fmt_money(1234.5), fmt_money(567.8, '$');
-- 返回:¥1234.50 | $567.80

5.2 同比环比计算宏

CREATE MACRO calc_mom(current_val, prev_val) AS
  ROUND(100.0 * (current_val - prev_val) / NULLIF(prev_val, 0), 2);

-- 在分析查询中使用
SELECT
  month,
  revenue,
  LAG(revenue) OVER (ORDER BY month) AS prev_revenue,
  calc_mom(revenue, LAG(revenue) OVER (ORDER BY month)) AS mom_pct
FROM monthly_sales;

5.3 数据质量检查宏

CREATE MACRO check_null_ratio(table_ref, col_name) AS
  ROUND(
    100.0 * SUM(CASE WHEN {col_name} IS NULL THEN 1 ELSE 0 END)
    / COUNT(*),
    2
  );

-- 使用:检查orders表的user_id列空值率
SELECT check_null_ratio(orders, user_id) AS null_ratio_pct;

六、与传统工具的对比

工具参数化表达式复用学习成本适用场景
DuckDB 宏嵌入式分析、SQL 优先场景
Python 函数复杂逻辑、需要调用外部库
SQL 视图返回固定表结构
Excel 公式小规模数据、非技术人员
存储过程传统数据库、复杂事务

核心优势:宏让你在不离开 SQL 的情况下获得函数式编程的能力,无需切换语言上下文。


七、变现建议:宏能帮你赚多少钱?

7.1 个人提效 → 时间变现

把你在项目中重复写了 10 次的 SQL 逻辑封装成宏库,后续项目直接复用。假设你每周节省 3 小时,时薪 ¥200,每年多赚 ¥31,200

7.2 宏库产品化

把你的常用宏打包成开源库(GitHub),吸引开发者使用。可以通过以下方式变现:

  • GitHub Sponsors:每月 ¥100-500 赞助
  • 付费宏库:在 Gumroad 或小报童上售卖高级宏包(¥99-299)
  • 培训课程:录制"SQL 高级技巧"课程,宏是核心章节

7.3 企业级数据产品

用宏构建可配置的报表模板,面向中小企业收费:

产品定价目标客户
财务报表宏包¥99/月中小企业财务
电商分析宏包¥199/月电商运营
自定义宏开发¥500-2000/次定制需求客户

7.4 技术博客引流

写宏的实战教程发布到 duckdblab.org,通过 SEO 吸引搜索"DuckDB 宏"的用户。一篇高质量教程每月可带来 500-2000 独立访客,转化为付费用户后月增收 ¥500-5000。


八、总结

DuckDB 的宏功能是一个被严重低估的特性。它填补了"视图不能带参数"和"CTE 无法跨查询复用"之间的空白,让你用纯 SQL 就能写出可复用、可配置的计算逻辑。

核心要点

  1. 宏是表达式级别的复用,不是表级别的
  2. 支持默认参数,调用更灵活
  3. 可以返回标量、struct、list 等多种类型
  4. 比视图更适合参数化场景,比 CTE 更适合跨查询复用

下次当你发现自己复制粘贴同一段 SQL 超过 3 次时,停下来想想:这能不能变成一个宏?

📖 更多 DuckDB 宏实战案例 → duckdblab.org

📺 Watch video tutorials → Olap Studio YouTube

Subscribe for more DuckDB & AI automation tutorials

使用 Hugo 构建
主题 StackJimmy 设计

⚠️ 本站为独立社区项目,与 DuckDB 基金会及 DuckDB 官方项目无任何从属、背书或赞助关系。

"DuckDB" 是 DuckDB 基金会的注册商标,本站仅以事实描述方式使用该名称。

本站内容仅供教育与社区推广用途,不构成任何商业服务。