Featured image of post DuckDB 连接 URI 语法:Ephemeral Attach 让跨库查询更优雅

DuckDB 连接 URI 语法:Ephemeral Attach 让跨库查询更优雅

DuckDB 新引入的 CONNECT 'URI' 语法支持临时附加远程数据库,无需手动 ATTACH/DISCONNECT,简化跨库查询工作流。本文详解用法、场景与最佳实践。

DuckDB 连接 URI 架构

引言

在数据分析的日常工作中,我们经常需要从多个数据源获取数据——PostgreSQL 中的业务表、Parquet 格式的日志文件、Quack 协议的远程数据库,甚至是 S3 上的对象存储。DuckDB 一直通过 ATTACH 语句来连接这些外部数据源,但传统方式有一个痛点:你需要显式地附加数据库、执行查询、然后显式地断开连接。

随着 PR #23555 的合并,DuckDB 引入了一个全新的语法糖——CONNECT 'URI',它让跨数据库查询变得更加优雅和简洁。

传统方式 vs 新语法

传统 ATTACH/DISCONNECT 模式

在过去,你要连接一个 Quack 远程数据库并查询,需要这样写:

-- 附加远程数据库
ATTACH 'quack:localhost' AS remote_db (TOKEN 'my-secret-token');

-- 切换到远程数据库上下文
CONNECT remote_db;

-- 执行查询
SELECT * FROM orders WHERE amount > 1000;

-- 切回主数据库
CONNECT main;

-- 最后记得断开连接
DETACH remote_db;

这段代码虽然功能完整,但步骤繁琐。每次连接都需要四到五步,而且容易忘记最后的 DETACH,导致连接泄漏。

新的 CONNECT ‘URI’ 语法

现在,你可以用一条语句完成同样的操作:

-- 一次性连接、查询、断开
CONNECT 'quack:localhost' (TOKEN 'my-secret-token');

-- 执行查询(自动在远程数据库上下文中)
SELECT * FROM orders WHERE amount > 1000;

-- 完成
DISCONNECT;

或者更进一步,如果你只需要执行一条查询就断开:

-- 连接后立即查询
CONNECT 'quack:localhost' (TOKEN 'my-secret-token');
SELECT COUNT(*) FROM users WHERE active = true;
DISCONNECT;

关键在于,CONNECT 'URI' 是临时的——执行 DISCONNECT 后,不仅断开连接,还会自动清理所有资源,包括之前手动 ATTACH 时可能遗留的上下文状态。

底层工作原理

CONNECT 'URI' 语法在内部等价于以下两步操作的组合:

CONNECT 'quack:localhost' (TOKEN 'qwerty')
-- 等价于
ATTACH 'quack:localhost' AS <auto_name> (TOKEN 'qwerty');
CONNECT <auto_name>;

而 DISCONNECT 则等价于:

DISCONNECT
-- 等价于
DETACH <auto_name>;

这种设计让 DuckDB 在保持向后兼容的同时,为用户提供了一个更简洁的接口。对于开发者来说,这意味着:

  1. 无需管理数据库别名:系统自动生成临时名称
  2. 自动清理:DISCONNECT 同时执行 DETACH,防止连接泄漏
  3. 上下文切换透明:CONNECT 后自动切换到目标数据库

支持的数据库类型

目前 CONNECT 'URI' 主要支持以下两种协议:

协议URI 格式用途
Quackquack:host:portDuckDB 远程协议,跨实例查询
PostgreSQLpostgres://host/dbPostgreSQL 数据库直连

Quack 协议示例

-- 连接到远程 Quack 服务
CONNECT 'quack:analytics-server:5432' (TOKEN 'your-auth-token');

-- 查询远程数据库中的聚合数据
SELECT 
  DATE_TRUNC('month', order_date) AS month,
  SUM(amount) AS total_sales,
  COUNT(DISTINCT customer_id) AS customers
FROM sales
GROUP BY month
ORDER BY month;

-- 断开连接,自动清理
DISCONNECT;

PostgreSQL 直连示例

-- 连接到 PostgreSQL 数据库
CONNECT 'postgres://user:[email protected]/analytics' ();

-- 查询 PostgreSQL 表
SELECT 
    p.product_name,
    SUM(o.quantity) AS total_sold
FROM products p
JOIN orders o ON p.id = o.product_id
GROUP BY p.product_name
ORDER BY total_sold DESC
LIMIT 10;

-- 断开
DISCONNECT;

与传统 ATTACH 方式的对比

特性ATTACH + CONNECTCONNECT 'URI'
代码行数4-5 行2-3 行
需要管理别名是否
自动清理资源否(需手动 DETACH)是(DISCONNECT 自动清理)
连接泄漏风险高低
可读性一般优秀
向后兼容完全兼容新增语法
适用场景长期多数据库会话短期单次查询

实际应用场景

场景一:临时数据导出

当你需要从远程数据库导出数据到本地进行分析时:

-- 连接远程数据库
CONNECT 'quack:remote-server:5432' (TOKEN 'export-token');

-- 导出数据到 Parquet
COPY (SELECT * FROM transactions WHERE year = 2025) 
TO '/tmp/transactions_2025.parquet' (FORMAT PARQUET);

-- 断开
DISCONNECT;

-- 本地分析
SELECT 
    region,
    AVG(amount) AS avg_transaction,
    MAX(amount) AS max_transaction
FROM read_parquet('/tmp/transactions_2025.parquet')
GROUP BY region;

场景二:跨库联合查询

-- 连接外部数据源
CONNECT 'quack:inventory-server:5432' (TOKEN 'inv-token');

-- 联合查询(外部数据 + 本地数据)
SELECT 
    p.product_name,
    i.stock_quantity,
    s.recent_sales
FROM products p
LEFT JOIN (
    SELECT product_id, SUM(quantity) AS recent_sales
    FROM local_sales
    GROUP BY product_id
) s ON p.id = s.product_id
LEFT JOIN inventory i ON p.id = i.product_id
WHERE i.stock_quantity < 10;

DISCONNECT;

场景三:自动化报告生成

import duckdb

conn = duckdb.ATTACH (':memory:')

# 一次性连接远程数据库获取数据
result = conn.execute("""
---

## 本文信息

| 项目 | 内容 |
|------|------|
| DuckDB 版本 | v1.5.x(部分功能基于 v2.0 Preview) |
| 最后验证 | 2026-09-12 |
| 测试环境 | Linux / x86_64 / 16GB RAM |
| 官方文档 | [DuckDB Documentation](https://duckdb.org/docs/) |
| GitHub | [pengzz9527/duckdb-blog](https://github.com/pengzz9527/duckdb-blog) |

如发现错误,欢迎通过 [GitHub Issue](https://github.com/pengzz9527/duckdb-blog/issues) 或邮件 [email protected] 反馈。

📺 Watch video tutorials → Olap Studio YouTube

Subscribe for more DuckDB & AI automation tutorials