Article

数据计算 DuckDB

更新于:2026-07-12

第1章:DuckDB 入门概览

初识 DuckDB,了解其定位、特点与适用场景。

1.1 什么是 DuckDB

概念名称说明注意事项
DuckDB嵌入式分析型数据库管理系统(Analytical DBMS),专为 OLAP 工作负载设计,支持 SQL,以列式存储和向量化执行引擎为核心。不适用于高并发 OLTP 场景(如 Web 应用后端写密集操作)。
嵌入式数据库直接嵌入到应用程序中运行,无需独立服务器进程,类似 SQLite。数据库文件本地化,适合单机或边缘计算场景。
分析型数据库(OLAP)优化用于复杂查询、聚合分析、大规模数据扫描,而非频繁的增删改操作。查询性能远高于传统行式数据库在分析任务中的表现。
开源协议使用 MIT 许可证,允许商业使用、修改和分发。可自由集成至闭源项目中。

1.2 DuckDB 与 SQLite、PostgreSQL、Pandas 的对比

对比维度DuckDBSQLitePostgreSQLPandas
类型嵌入式分析型数据库(OLAP)嵌入式事务型数据库(OLTP)客户端-服务器关系型数据库(通用)内存数据处理库(Python)
存储模式列式存储为主,优化聚合查询行式存储行式存储(支持列存扩展)内存中按列组织(DataFrame)
执行引擎向量化执行,CPU 缓存友好解释执行解释/ JIT 编译向量化函数(NumPy 底层)
并发支持单写多读(MVCC),轻量级并发单连接写,其余只读高并发多用户支持GIL 限制,非线程安全
使用场景快速数据分析、ETL、嵌入式 BI、替代 Pandas 处理大文件移动应用、配置存储、小型 Web 后端Web 后端、企业级应用、复杂事务数据清洗、探索性分析(中小数据集)
性能特点极快的 SELECT + 聚合查询,尤其对 Parquet/CSV快速点查与事务强一致性与复杂功能支持灵活但内存消耗大,>10GB 易崩溃
语言绑定Python, R, C/C++, Java, JS 等广泛支持广泛支持Python 专属
是否需要服务进程否(嵌入式)是(常驻进程)
文件格式原生支持CSV, Parquet, JSON, Arrow需扩展或导入需扩展(如 file_fdw)需加载到内存

1.3 DuckDB 的核心特性(嵌入式、列式存储、向量化执行)

特性名称说明注意事项
嵌入式架构无须安装服务,直接通过库调用使用,启动快,部署简单。不适合多用户共享访问或远程连接需求。
列式存储数据按列存储,提升 I/O 效率,仅读取相关列,压缩率高。插入/更新代价较高,不适合频繁写入场景。
向量化执行每次处理一批数据(vector of values),减少函数调用开销,提升 CPU 利用率。对现代 CPU SIMD 指令优化,性能随批大小提升。
内置文件支持原生读写 Parquet、CSV、JSON 等格式,无需中间转换。可直接查询外部文件,如 SELECT * FROM 'data.parquet'
SQL 兼容性支持标准 SQL(包括窗口函数、CTE、JOIN 等),语法接近 PostgreSQL。不支持部分高级特性如存储过程。
扩展机制支持加载扩展(如 httpfs, json, parquet)以增强功能。扩展需显式加载(LOAD ...)。
内存管理自动管理内存,支持外部排序/聚合(spill to disk)。即使数据超过内存也可处理,但速度下降。

1.4 安装与环境配置(Python、CLI、R 等)

安装方式语法/命令用途代码示例注意事项
Python 安装pip install duckdb在 Python 环境中安装 DuckDB 包pip install duckdb
pip install duckdb[parquet]
pip install duckdb[icu]
推荐添加 [parquet] 支持 Parquet 文件读写
CLI 安装brew install duckdb (macOS)
apt install duckdb (Linux)
或从官网下载二进制
使用命令行交互工具duckdb
duckdb mydb.duckdb
CLI 提供交互式 SQL 环境
R 安装install.packages(“duckdb”)在 R 中使用 DuckDBinstall.packages("duckdb")
library(duckdb)
支持 dplyr 接口集成
Node.js 安装npm install duckdb在 JavaScript/Node.js 中使用npm install duckdb功能较 Python 版有限
Docker 镜像docker pull duckdb/duckdb使用容器化环境docker run -it duckdb/duckdb适合测试或 CI/CD 环境
验证安装import duckdb
print(duckdb.version)
检查 Python 安装是否成功import duckdb
print(duckdb.__version__)
若报错请检查 Python 路径与虚拟环境

第2章:基础操作与连接管理

掌握如何连接、创建数据库、管理连接与执行基本命令。

2.1 连接 DuckDB(内存模式与文件模式)

方法名称语法用途代码示例注意事项
duckdb.connect()duckdb.connect(database=':memory:' or 'path.db', read_only=False)创建一个到 DuckDB 数据库的连接conn = duckdb.connect() # 内存数据库
conn = duckdb.connect('mydata.duckdb') # 文件数据库
默认 :memory:,关闭连接后数据丢失;指定路径则持久化
内存模式database=':memory:' 或不传参临时数据库,速度快,重启丢失conn = duckdb.connect() # 等价于 ':memory:'适合一次性分析、测试
文件模式database='filename.duckdb'持久化数据库,数据保存在磁盘conn = duckdb.connect('sales.duckdb')文件自动创建,支持跨会话使用
只读模式read_only=True打开数据库为只读,防止误写conn = duckdb.connect('data.duckdb', read_only=True)提升安全性,适合共享数据文件
自动提交默认开启DML 操作自动提交,无需手动 commitconn.execute("CREATE TABLE t(x INT)") # 自动生效可通过 conn.commit()/rollback() 控制事务

2.2 断开与关闭连接

方法名称语法用途代码示例注意事项
close()conn.close()关闭数据库连接,释放资源conn = duckdb.connect()
conn.close()
关闭后不可再执行语句,否则抛异常
上下文管理器with duckdb.connect() as conn:自动管理连接生命周期with duckdb.connect() as conn:
conn.execute("CREATE TABLE t(a INT)") # 自动关闭
推荐做法,确保连接正确释放
del conndel conn删除连接对象引用del conn不保证立即关闭,仍建议显式 close()
多连接管理多个 conn 实例并行操作不同数据库或隔离会话conn1 = duckdb.connect('a.duckdb')
conn2 = duckdb.connect('b.duckdb')
注意文件锁:同一文件多写会冲突

2.3 执行 SQL 语句的基本方法

方法名称语法用途代码示例注意事项
execute()conn.execute(sql)执行一条 SQL 语句,返回结果对象conn.execute("CREATE TABLE test(i INTEGER)")
conn.execute("INSERT INTO test VALUES (1), (2)")
最常用方法,支持所有 SQL 语句
executescript()conn.executescript(sql_script)执行多条 SQL 语句(脚本)conn.executescript("CREATE TABLE t(x INT); INSERT INTO t VALUES (1); INSERT INTO t VALUES (2);")类似 SQLite,不返回中间结果
df() 方法duckdb.sql(query).df()直接执行 SQL 并返回 Pandas DataFrameresult_df = duckdb.sql("SELECT * FROM test").df()无需先创建连接,适合快速分析
arrow() 方法duckdb.sql(query).arrow()执行 SQL 返回 Arrow Tableresult_arrow = duckdb.sql("SELECT * FROM test").arrow()零拷贝,适合与 Arrow 生态集成
from_query()duckdb.from_query(query, alias='')将查询封装为可复用表对象tbl = duckdb.from_query("SELECT i*2 AS j FROM test", 'aliased')
duckdb.sql("SELECT * FROM aliased")
便于构建复杂查询链

2.4 获取执行结果(fetch 与 iterate)

方法名称语法用途代码示例注意事项
fetchone()cursor.fetchone()获取下一行结果,返回元组或 Noneconn.execute("SELECT i FROM test")
row = conn.fetchone() # (1,)
每次调用获取一行,适合逐行处理
fetchall()cursor.fetchall()获取所有剩余行,返回元组列表rows = conn.execute("SELECT * FROM test").fetchall() # [(1,), (2,)]全部加载到内存,大数据慎用
fetchmany(n)cursor.fetchmany(size=n)获取最多 n 行,返回列表some_rows = conn.execute("...").fetchmany(5)可用于分页处理,避免内存溢出
fetchdf()cursor.fetchdf()获取结果为 Pandas DataFramedf = conn.execute("SELECT * FROM test").fetchdf()方便与 Pandas 交互,自动推断类型
fetch_arrow_table()cursor.fetch_arrow_table()获取结果为 PyArrow Tableat = conn.execute("...").fetch_arrow_table()高效,支持零拷贝,适合大数据
df()(结果对象)result.df()从 sql() 结果转为 DataFramedf = duckdb.sql("...").df()更简洁,推荐用于直接分析
iterate()不适用(流式处理)通过 fetchone() 实现逐行迭代res = conn.execute("SELECT * FROM test")
# while row := res.fetchone(): print(row)
内存友好,适合超大结果集

提示: 推荐使用 fetchdf()fetch_arrow_table() 进行数据分析;对大型结果集,优先考虑流式 fetchone()fetchmany()

第3章:数据定义语言(DDL)

学习如何创建、修改和删除表结构。

3.1 创建表(CREATE TABLE)

方法/语法语法格式用途代码示例注意事项
CREATE TABLECREATE TABLE table_name (column_def, ...)创建新表并定义列结构CREATE TABLE employees (id INTEGER, name VARCHAR, salary DOUBLE, hire_date DATE);必须指定列名和数据类型
CREATE TABLE IF NOT EXISTSCREATE TABLE IF NOT EXISTS ...避免表已存在时报错CREATE TABLE IF NOT EXISTS logs (ts TIMESTAMP, msg VARCHAR);推荐用于脚本中防止重复创建
带默认值column_name TYPE DEFAULT expr为列设置默认值CREATE TABLE products (id INTEGER PRIMARY KEY, name VARCHAR, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);DEFAULT 可使用常量或函数(如 NOW()
主键约束column_name TYPE PRIMARY KEY定义主键,确保唯一性CREATE TABLE users (user_id INTEGER PRIMARY KEY, email VARCHAR);DuckDB 支持主键语法,但不强制唯一性检查(元数据用途)
NOT NULL 约束column_name TYPE NOT NULL约束列不允许为空CREATE TABLE orders (order_id INTEGER NOT NULL, amount DOUBLE NOT NULL);可用于数据质量控制
使用查询结果建表CREATE TABLE AS SELECT ...基于查询结果创建表CREATE TABLE high_earners AS SELECT * FROM employees WHERE salary > 100000;自动推断列名和类型,非常方便
临时表CREATE TEMPORARY TABLE ...创建会话级临时表CREATE TEMPORARY TABLE temp_stats AS SELECT AVG(salary) AS avg_sal FROM employees;仅当前连接可见,断开后自动删除

3.2 修改表结构(ALTER TABLE)

方法/语法语法格式用途代码示例注意事项
ADD COLUMNALTER TABLE table_name ADD COLUMN col_def添加新列ALTER TABLE employees ADD COLUMN department VARCHAR;新列对已有行的值为 NULL
RENAME TABLEALTER TABLE old_name RENAME TO new_name重命名表ALTER TABLE employees RENAME TO staff;表名更改,不影响数据
RENAME COLUMNALTER TABLE table_name RENAME COLUMN old TO new重命名列ALTER TABLE employees RENAME COLUMN name TO full_name;需注意查询中引用的列名同步更新
SET DEFAULTALTER TABLE table_name ALTER COLUMN col SET DEFAULT expr设置列默认值ALTER TABLE employees ALTER COLUMN department SET DEFAULT 'Unknown';影响后续 INSERT 操作
DROP DEFAULTALTER TABLE table_name ALTER COLUMN col DROP DEFAULT移除列默认值ALTER TABLE employees ALTER COLUMN department DROP DEFAULT;恢复为无默认值状态
支持的数据类型修改?不支持直接 MODIFY COLUMN修改列类型DuckDB 不支持直接修改列数据类型,需通过创建新表 + 数据迁移实现

3.3 删除表(DROP TABLE)

方法/语法语法格式用途代码示例注意事项
DROP TABLEDROP TABLE table_name删除表及其所有数据DROP TABLE employees;操作不可逆,请谨慎使用
DROP TABLE IF EXISTSDROP TABLE IF EXISTS table_name避免表不存在时报错DROP TABLE IF EXISTS temp_results;推荐用于脚本中安全删除
CASCADE(暂不支持)DROP TABLE ... CASCADE删除表并自动删除依赖对象DuckDB 当前不支持 CASCADE 选项
RESTRICT(默认)DROP TABLE ... RESTRICT仅当无依赖时才允许删除默认行为,若存在视图等依赖会报错

3.4 查看表信息(PRAGMA 和 INFORMATION_SCHEMA)

方法/语法语法格式用途代码示例注意事项
PRAGMA table_info()PRAGMA table_info(table_name)查看表的列信息(名称、类型、是否主键等)PRAGMA table_info(employees);类似 SQLite 的 table_info,最常用
PRAGMA show()PRAGMA show('table_name')显示表的全部数据(等价于 SELECT *PRAGMA show('employees');快速预览表内容
PRAGMA column_list()PRAGMA column_list('table_name')列出表的所有列名PRAGMA column_list('employees');仅返回列名
查询 INFORMATION_SCHEMASELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '...'使用标准 SQL 获取元数据SELECT column_name, data_type FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'employees';标准化方式,兼容性强
duckdb_tables()SELECT * FROM duckdb_tables()系统函数:列出所有表SELECT * FROM duckdb_tables();查看当前数据库中所有表
duckdb_columns()SELECT * FROM duckdb_columns()系统函数:列出所有列信息SELECT * FROM duckdb_columns() WHERE table_name = 'employees';比 PRAGMA 更灵活,可加 WHERE 条件

第4章:数据操作语言(DML)

掌握数据的插入、查询、更新与删除。

4.1 插入数据(INSERT INTO)

方法/语法语法格式用途代码示例注意事项
INSERT INTO … VALUESINSERT INTO table VALUES (v1, v2, ...)插入单行或多行常量值INSERT INTO employees VALUES (1, 'Alice', 75000, '2023-01-15');
INSERT INTO employees VALUES (2, 'Bob', 80000, '2023-02-01'), (3, 'Charlie', 70000, '2023-02-10');
值的顺序必须与表列顺序一致
INSERT INTO … (cols) VALUESINSERT INTO table (col1, col2) VALUES (...)指定列插入,其余列用默认值或 NULLINSERT INTO employees (id, name, salary) VALUES (4, 'Diana', 85000);更安全,避免顺序错乱
INSERT INTO … SELECTINSERT INTO table SELECT ...从查询结果插入数据INSERT INTO high_earners SELECT * FROM employees WHERE salary > 80000;批量迁移或聚合数据常用
INSERT OR REPLACE不支持冲突时替换DuckDB 不支持 INSERT OR REPLACE 语法
支持 UPSERT?不支持 ON CONFLICT插入或更新(UPSERT)当前版本不支持 ON CONFLICT 子句,需手动判断或使用临时表

4.2 查询数据(SELECT 基础)

方法/语法语法格式用途代码示例注意事项
SELECT * FROMSELECT * FROM table查询表中所有列SELECT * FROM employees;简单快速,但建议明确列名用于生产
SELECT col1, col2SELECT col1, col2 FROM table查询指定列SELECT name, salary FROM employees;减少数据传输,提升性能
SELECT with aliasSELECT expr AS alias FROM ...为列或表达式设置别名SELECT name AS employee_name, salary * 1.1 AS raised_salary FROM employees;提高可读性,用于计算字段
DISTINCTSELECT DISTINCT col FROM table去重查询SELECT DISTINCT department FROM employees;消除重复值,可用于单列或多列
LIMITSELECT ... LIMIT n限制返回行数SELECT * FROM employees LIMIT 5;常用于调试或分页
LIMIT + OFFSETSELECT ... LIMIT n OFFSET m实现分页查询SELECT * FROM employees LIMIT 10 OFFSET 20; -- 第3页,每页10条OFFSET 性能随偏移量增大而下降

4.3 更新数据(UPDATE)

方法/语法语法格式用途代码示例注意事项
UPDATE … SETUPDATE table SET col = value WHERE ...更新满足条件的行UPDATE employees SET salary = salary * 1.1 WHERE department = 'Engineering';必须谨慎使用 WHERE,避免误更新全表
更新多列UPDATE table SET col1 = v1, col2 = v2一次更新多个列UPDATE employees SET salary = salary + 5000, department = 'Senior Eng' WHERE id = 1;用逗号分隔多个赋值
WHERE 条件WHERE condition限定更新范围UPDATE employees SET salary = 0 WHERE active = false;强烈建议始终使用 WHERE 防止全表更新
不带 WHEREUPDATE table SET col = value更新所有行UPDATE employees SET last_check = CURRENT_DATE;风险高,确认后再执行
支持 RETURNING?不支持 RETURNING返回被更新的行DuckDB 当前不支持 RETURNING 子句,需额外查询验证

4.4 删除数据(DELETE)

方法/语法语法格式用途代码示例注意事项
DELETE FROM … WHEREDELETE FROM table WHERE condition删除满足条件的行DELETE FROM employees WHERE hire_date < '2020-01-01';推荐用法,精确控制删除范围
DELETE all rowsDELETE FROM table删除表中所有数据(保留结构)DELETE FROM temp_cache;不同于 DROP TABLE,表结构仍在
TRUNCATE TABLETRUNCATE TABLE table_name清空表(比 DELETE 更快)TRUNCATE TABLE logs;DuckDB 支持 TRUNCATE,适用于大表清空
不支持 RETURNINGDELETE ... RETURNING删除并返回被删行不支持 RETURNING 子句
性能对比DELETE vs TRUNCATE清空操作性能TRUNCATE 更快,不记录逐行删除日志,推荐用于清空全表

第5章:数据查询进阶

深入学习 SQL 查询能力。

5.1 WHERE 条件过滤

方法/语法语法格式用途代码示例注意事项
比较操作符=, !=, <, <=, >, >=基本比较过滤SELECT * FROM employees WHERE salary >= 70000;支持所有标准比较运算
IN 操作符col IN (v1, v2, ...)匹配值列表SELECT * FROM employees WHERE department IN ('Sales', 'HR');等价于多个 OR 条件,性能较好
NOT INcol NOT IN (...)排除值列表SELECT * FROM employees WHERE id NOT IN (1, 2, 3);若列表含 NULL,结果可能为 UNKNOWN,需注意
BETWEENcol BETWEEN low AND high范围匹配(闭区间)SELECT * FROM employees WHERE salary BETWEEN 50000 AND 80000;包含边界值,等价于 >= AND <=
LIKEcol LIKE 'pattern'模糊匹配(支持 % 和 _)SELECT * FROM employees WHERE name LIKE 'A%';% 匹配任意字符,_ 匹配单个字符
ILIKEcol ILIKE 'pattern'不区分大小写的 LIKESELECT * FROM employees WHERE name ILIKE 'alice';DuckDB 特有扩展(源自 PostgreSQL)
IS NULL / IS NOT NULLcol IS NULL判断空值SELECT * FROM employees WHERE department IS NULL;必须使用 IS NULL,不能用 = NULL
逻辑操作符AND, OR, NOT组合多个条件SELECT * FROM employees WHERE salary > 60000 AND department = 'Engineering';注意优先级,复杂表达式建议加括号

5.2 GROUP BY 与聚合函数

方法/语法语法格式用途代码示例注意事项
GROUP BYSELECT ..., agg_func(col) FROM ... GROUP BY col按列分组并聚合SELECT department, AVG(salary) AS avg_sal FROM employees GROUP BY department;所有非聚合列必须出现在 GROUP BY 中
聚合函数(SUM)SUM(expr)求和SELECT department, SUM(salary) FROM employees GROUP BY department;忽略 NULL 值
聚合函数(COUNT)COUNT(*), COUNT(col)计数SELECT department, COUNT(*) AS cnt FROM employees GROUP BY department;COUNT(*) 包含 NULL,COUNT(col) 排除 NULL
聚合函数(AVG)AVG(expr)平均值SELECT AVG(age) FROM employees;自动忽略 NULL
聚合函数(MIN/MAX)MIN(expr), MAX(expr)最小/最大值SELECT MIN(salary), MAX(salary) FROM employees;可用于数值、字符串、日期
聚合函数(STRING_AGG)STRING_AGG(col, sep)字符串拼接SELECT department, STRING_AGG(name, ', ') FROM employees GROUP BY department;类似 GROUP_CONCAT
多列分组GROUP BY col1, col2按多列组合分组SELECT dept, role, AVG(salary) FROM employees GROUP BY dept, role;分组粒度更细

5.3 HAVING 过滤分组结果

方法/语法语法格式用途代码示例注意事项
HAVINGSELECT ..., agg() FROM ... GROUP BY ... HAVING condition对分组后的聚合结果进行过滤SELECT department, AVG(salary) AS avg_sal FROM employees GROUP BY department HAVING AVG(salary) > 65000;HAVING 作用于分组后,WHERE 作用于分组前
使用聚合函数HAVING agg_func(col) > value基于聚合值过滤SELECT manager_id, COUNT(*) AS team_size FROM employees GROUP BY manager_id HAVING COUNT(*) >= 3;是 HAVING 的主要用途
多条件 HAVINGHAVING cond1 AND cond2组合多个过滤条件HAVING AVG(salary) > 60000 AND COUNT(*) > 1;支持 AND/OR/NOT
与 WHERE 区别WHERE 在 GROUP BY 前,HAVING 在后不同阶段的过滤SELECT d, SUM(x) FROM t WHERE x > 0 GROUP BY d HAVING SUM(x) > 100;先用 WHERE 减少数据量,再用 HAVING 过滤结果

5.4 ORDER BY 与 LIMIT

方法/语法语法格式用途代码示例注意事项
ORDER BYORDER BY col [ASC|DESC]排序结果SELECT * FROM employees ORDER BY salary DESC;默认 ASC(升序),NULL 值排在最后
多列排序ORDER BY col1, col2按优先级排序SELECT * FROM employees ORDER BY department, salary DESC;先按第一列排,相同再按第二列
NULL 排序ORDER BY col NULLS FIRST|LAST控制 NULL 值位置SELECT * FROM employees ORDER BY department NULLS FIRST;显式控制 NULL 排序行为
LIMITLIMIT n限制返回行数SELECT * FROM employees ORDER BY salary DESC LIMIT 5;常用于 Top-N 查询
LIMIT + OFFSETLIMIT n OFFSET m实现分页SELECT * FROM employees ORDER BY id LIMIT 10 OFFSET 20;OFFSET 性能随偏移增大而下降
FETCH FIRSTFETCH FIRST n ROWS ONLY标准化 LIMIT 替代语法SELECT * FROM employees ORDER BY salary DESC FETCH FIRST 5 ROWS ONLY;SQL 标准语法,与 LIMIT 等价

5.5 JOIN 多表连接(INNER, LEFT, RIGHT, FULL)

方法/语法语法格式用途代码示例注意事项
INNER JOINSELECT ... FROM A INNER JOIN B ON A.key = B.key内连接:仅保留匹配行SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id = d.id;最常用,只返回两表都有的匹配记录
LEFT JOINSELECT ... FROM A LEFT JOIN B ON ...左外连接:保留左表所有行SELECT e.name, COALESCE(d.dept_name, 'Unassigned') FROM employees e LEFT JOIN departments d ON e.dept_id = d.id;左表全保留,右表无匹配则为 NULL
RIGHT JOINSELECT ... FROM A RIGHT JOIN B ON ...右外连接:保留右表所有行SELECT e.name, d.dept_name FROM employees e RIGHT JOIN departments d ON e.dept_id = d.id;右表全保留,左表无匹配则为 NULL
FULL JOINSELECT ... FROM A FULL JOIN B ON ...全外连接:保留两表所有行SELECT e.name, d.dept_name FROM employees e FULL JOIN departments d ON e.dept_id = d.id;任一表有记录即保留,缺失补 NULL
USING 子句JOIN ... USING (col)简化等值连接(列名相同)SELECT * FROM emp USING (dept_id);当连接列名相同时可简化语法
自连接JOIN 同一表表自身连接SELECT a.name, b.name AS manager FROM employees a JOIN employees b ON a.manager_id = b.id;用于层级关系(如员工-经理)
多表 JOIN链式 JOIN连接三个及以上表SELECT e.name, d.name, p.project_name FROM emp e JOIN dept d ON e.d = d.id JOIN projects p ON e.p_id = p.id;按业务逻辑顺序连接

5.6 子查询与 CTE(WITH 子句)

方法/语法语法格式用途代码示例注意事项
标量子查询SELECT (SELECT ...) FROM ...返回单值的子查询SELECT name, (SELECT AVG(salary) FROM employees) AS avg_all FROM employees;必须返回单行单列,常用于计算字段
WHERE 中子查询WHERE col OP (SELECT ...)在 WHERE 中使用子查询SELECT * FROM employees WHERE salary > (SELECT AVG(salary) FROM employees);支持 IN, EXISTS, 比较等
FROM 中子查询SELECT ... FROM (SELECT ...) AS alias衍生表(内联视图)SELECT dept, avg_sal FROM (SELECT department AS dept, AVG(salary) AS avg_sal FROM employees GROUP BY department) t WHERE avg_sal > 60000;子查询必须有别名
WITH 子句(CTE)WITH cte_name AS (query) SELECT ... FROM cte_name公共表表达式,提高可读性WITH dept_avg AS (SELECT department, AVG(salary) AS avg_sal FROM employees GROUP BY department) SELECT * FROM dept_avg WHERE avg_sal > 65000;可定义多个 CTE,支持递归(见下)
递归 CTEWITH RECURSIVE cte AS (base_query UNION ALL recursive_query)实现递归查询(如树形结构)WITH RECURSIVE emp_tree AS (SELECT id, name, manager_id, 1 AS level FROM employees WHERE manager_id IS NULL UNION ALL SELECT e.id, e.name, e.manager_id, et.level + 1 FROM employees e JOIN emp_tree et ON e.manager_id = et.id) SELECT * FROM emp_tree;DuckDB 支持递归 CTE,用于组织架构、路径遍历等

5.7 窗口函数(OVER, PARTITION BY, ROWS/RANGE)

方法/语法语法格式用途代码示例注意事项
ROW_NUMBER()ROW_NUMBER() OVER (ORDER BY col)行号(唯一)SELECT name, salary, ROW_NUMBER() OVER (ORDER BY salary DESC) AS rank FROM employees;每行分配唯一序号,即使值相同也不同
RANK()RANK() OVER (ORDER BY col)排名(跳跃)RANK() OVER (ORDER BY salary DESC) AS ranked相同值同名次,下一名跳过(如 1,1,3)
DENSE_RANK()DENSE_RANK() OVER (...)密集排名DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank相同值同名次,下一名连续(如 1,1,2)
PARTITION BYOVER (PARTITION BY col ORDER BY col2)分组窗口计算SELECT name, department, salary, AVG(salary) OVER (PARTITION BY department) AS dept_avg FROM employees;在每个分区内独立计算
LAG() / LEAD()LAG(col, offset, default)访问前/后 N 行SELECT date, revenue, LAG(revenue, 1) OVER (ORDER BY date) AS prev_rev FROM sales;时间序列分析常用
SUM() OVERSUM(col) OVER (...)累计求和SUM(sales) OVER (ORDER BY date ROWS UNBOUNDED PRECEDING) AS cum_sales实现运行总计
ROWS / RANGEOVER (ORDER BY ... ROWS BETWEEN ...)定义窗口范围SUM(sales) OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)ROWS 按物理行,RANGE 按值范围
FIRST_VALUE() / LAST_VALUE()FIRST_VALUE(col) OVER (...)获取窗口内首/尾值FIRST_VALUE(salary) OVER (PARTITION BY dept ORDER BY hire_date)常用于获取最早/最晚记录

第6章:数据类型与函数

熟悉 DuckDB 支持的数据类型与内置函数。

6.1 基本数据类型(INTEGER, VARCHAR, DATE, BOOLEAN 等)

数据类型说明注意事项
BOOLEAN布尔值:TRUE / FALSE / NULL支持标准逻辑运算
TINYINT8位整数,范围 -128 到 127小整数存储,节省空间
SMALLINT16位整数,范围 -32,768 到 32,767
INTEGER32位整数,范围约 ±21亿最常用整型
BIGINT64位整数大数、时间戳等
HUGEINT128位整数(实验性)极大数值,如金融计算
REAL / FLOAT32位浮点数单精度
DOUBLE / FLOAT864位浮点数双精度,推荐用于科学计算
DECIMAL(p,s)精确小数,p=精度,s=标度DECIMAL(10,2) 表示最多10位,2位小数,适合货币
VARCHAR / TEXT可变长度字符串无默认长度限制
CHAR(n)固定长度字符串不足补空格
DATE日期(年-月-日)范围 '0001-01-01''9999-12-31'
TIME时间(时:分:秒[.毫秒])精确到微秒
TIMESTAMP日期+时间默认无时区
TIMESTAMP WITH TIME ZONE带时区的时间戳存储 UTC,显示时转换
INTERVAL时间间隔INTERVAL '1 day',用于日期运算

6.2 复合类型(STRUCT, LIST, MAP)

复合类型语法/构造方式用途代码示例注意事项
STRUCTROW(expr, ...){'key': val, ...}类似记录或对象SELECT ROW('Alice', 30) AS person;
SELECT {'name': 'Bob', 'age': 25} AS info;
可嵌套,通过 col.key 访问字段
LIST / ARRAY[elem1, elem2, ...]有序集合SELECT [1, 2, 3] AS nums;
SELECT ['a', 'b'] AS tags;
支持数组操作函数
MAPMAP{'key': val, ...}键值对集合SELECT MAP{'x': 1, 'y': 2} AS coords;类似字典,键必须唯一
访问 STRUCT 字段struct_col.key提取结构体字段SELECT info.name FROM (SELECT {'name':'Alice'} AS info) t;点号语法
访问 LIST 元素list_col[index]按索引访问(从1开始)SELECT tags[1] FROM mytable; -- 第一个元素索引越界返回 NULL
LIST 聚合LIST_VALUE(...)LIST(...)聚合生成列表SELECT department, LIST(name) AS members FROM employees GROUP BY department;强大功能,用于分组聚合
MAP 聚合MAP(keys, values)从两列创建 MAPSELECT MAP(ARRAY['a','b'], ARRAY[1,2]) AS m; -- {'a':1, 'b':2}键值数组长度需一致

6.3 日期时间函数(DATE_TRUNC, INTERVAL, EXTRACT 等)

函数名称语法用途代码示例注意事项
CURRENT_DATECURRENT_DATE返回当前日期SELECT CURRENT_DATE;格式:YYYY-MM-DD
CURRENT_TIMECURRENT_TIME返回当前时间(含时区)SELECT CURRENT_TIME;包含时区偏移
CURRENT_TIMESTAMPCURRENT_TIMESTAMP返回当前时间戳SELECT CURRENT_TIMESTAMP;最常用,包含日期和时间
NOW()NOW()同 CURRENT_TIMESTAMPSELECT NOW();PostgreSQL 兼容别名
DATE_TRUNCDATE_TRUNC(unit, timestamp)截断时间到指定粒度SELECT DATE_TRUNC('month', '2023-04-15 12:34:56'); -- 结果: 2023-04-01 00:00:00支持 'year', 'month', 'day', 'hour', 'minute', 'second'
EXTRACTEXTRACT(field FROM timestamp)提取时间部分SELECT EXTRACT(YEAR FROM '2023-04-15') AS y;
SELECT EXTRACT(HOUR FROM CURRENT_TIMESTAMP);
field 可为 YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, DOW, DOY 等
DATE_PARTDATE_PART(unit, timestamp)类似 EXTRACT,但 unit 为字符串SELECT DATE_PART('day', '2023-04-15');与 EXTRACT 功能重叠,PostgreSQL 风格
INTERVALINTERVAL 'value unit'创建时间间隔SELECT '2023-01-01'::DATE + INTERVAL '1 day'; -- 结果: 2023-01-02支持 'day', 'month', 'year', 'hour', 'minute'
年月日构造MAKE_DATE(year, month, day)从年月日构造日期SELECT MAKE_DATE(2023, 4, 15);安全构造日期,自动校验
时间加减datetime ± INTERVAL日期时间运算SELECT CURRENT_TIMESTAMP - INTERVAL '7 days';支持加减各种单位
AGEAGE(timestamp)计算与当前时间的间隔SELECT AGE('1990-05-20');返回 INTERVAL 类型,表示年龄

6.4 字符串处理函数(SUBSTR, REPLACE, REGEXP 等)

函数名称语法用途代码示例注意事项
LENGTH / CHAR_LENGTHLENGTH(string)返回字符串字符数SELECT LENGTH('Hello'); -- 5支持多字节字符(如中文)
LOWER / UPPERLOWER(string), UPPER(string)大小写转换SELECT UPPER('hello'); -- 'HELLO'基本文本处理
TRIMTRIM(string)去除首尾空格SELECT TRIM(' abc '); -- 'abc'支持 LTRIM, RTRIM 去除单侧
SUBSTR / SUBSTRINGSUBSTR(string, start, length?)截取子串SELECT SUBSTR('Hello', 2, 3); -- 'ell'
SELECT SUBSTR('Hello', -3); -- 'llo'
起始位置从1开始,支持负数(从末尾)
LEFT / RIGHTLEFT(string, n), RIGHT(string, n)取左/右 N 个字符SELECT LEFT('Hello', 2); -- 'He'
SELECT RIGHT('Hello', 3); -- 'llo'
简化常用操作
REPLACEREPLACE(string, old, new)字符串替换SELECT REPLACE('Hello World', 'World', 'DuckDB'); -- 'Hello DuckDB'全局替换所有匹配
REVERSEREVERSE(string)字符串反转SELECT REVERSE('abc'); -- 'cba'文本处理工具
CONCATstring1 || string2CONCAT(s1,s2,...)字符串拼接SELECT 'Hello ' || 'World';|| 是标准 SQL 拼接操作符
SPLIT_PARTSPLIT_PART(string, delim, n)按分隔符分割取第 n 部分SELECT SPLIT_PART('a,b,c', ',', 2); -- 'b'类似 Python 的 split()[n-1]
REGEXP_MATCHESREGEXP_MATCHES(string, pattern)正则匹配,返回匹配组SELECT REGEXP_MATCHES('abc123', '([a-z]+)([0-9]+)'); -- ['abc', '123']返回 LIST,支持捕获组
REGEXP_EXTRACTREGEXP_EXTRACT(string, pattern, group?)提取正则匹配的指定组SELECT REGEXP_EXTRACT('User: Alice', 'User: (\w+)', 1); -- 'Alice'group 缺省为1
REGEXP_REPLACEREGEXP_REPLACE(string, pattern, replacement)正则替换SELECT REGEXP_REPLACE('abc123', '\d+', 'XXX'); -- 'abcXXX'支持全局替换
LIKE / ILIKEstring LIKE 'pattern'模式匹配(见 5.1)SELECT 'hello' LIKE 'h%'; -- true% 任意,_ 单字符

6.5 数值与聚合函数(SUM, AVG, ROUND 等)

函数名称语法用途代码示例注意事项
SUMSUM(expr)求和SELECT SUM(salary) FROM employees;忽略 NULL,返回对应类型
AVGAVG(expr)平均值SELECT AVG(price) FROM products;自动转换为 DOUBLE
COUNTCOUNT(*), COUNT(expr)计数SELECT COUNT(*) FROM employees;
SELECT COUNT(commission) FROM employees; -- 非 NULL 个数
COUNT(*) 统计行数
MIN / MAXMIN(expr), MAX(expr)最小/最大值SELECT MIN(age), MAX(age) FROM people;支持数值、字符串、日期
ROUNDROUND(expr, decimals?)四舍五入SELECT ROUND(3.14159, 2); -- 3.14
SELECT ROUND(123.456, -1); -- 120.0
decimals 可为负数(小数点左)
CEIL / FLOORCEIL(x), FLOOR(x)向上/下取整SELECT CEIL(3.2), FLOOR(3.8); -- 4, 3返回 DOUBLE
ABSABS(x)绝对值SELECT ABS(-10); -- 10
MODx % yMOD(x, y)取模运算SELECT 10 % 3; -- 1
POWERPOWER(x, y)幂运算SELECT POWER(2, 3); -- 8
SQRTSQRT(x)平方根SELECT SQRT(16); -- 4
LOG / LNLOG(x), LN(x)对数(10为底 / 自然)SELECT LOG(100); -- 2.0
SELECT LN(2.718); -- ~1.0
GREATEST / LEASTGREATEST(a,b,...), LEAST(a,b,...)返回最大/最小值SELECT GREATEST(3, 7, 1); -- 7可比较多个值
COVAR_SAMP / CORRCOVAR_SAMP(x,y), CORR(x,y)样本协方差 / 相关系数SELECT CORR(income, spending) FROM data;统计分析函数
STDDEV / VARSTDDEV_SAMP(expr), VAR_SAMP(expr)样本标准差 / 方差SELECT STDDEV_SAMP(score) FROM exam_results;支持 _POP 版本(总体)

6.6 条件函数(CASE, COALESCE, IF 等)

函数名称语法用途代码示例注意事项
CASE(简单)CASE expr WHEN val THEN result ... END简单分支SELECT name, CASE department WHEN 'Sales' THEN 'A' WHEN 'Eng' THEN 'B' ELSE 'C' END AS grade FROM employees;类似 switch
CASE(搜索)CASE WHEN cond THEN result ... END条件分支SELECT name, salary, CASE WHEN salary < 50000 THEN 'Low' WHEN salary < 80000 THEN 'Medium' ELSE 'High' END AS level FROM employees;更灵活,支持任意条件
COALESCECOALESCE(expr1, expr2, ...)返回第一个非 NULL 值SELECT COALESCE(NULL, 'default', 'backup'); -- 'default'
SELECT COALESCE(phone, mobile, 'N/A') FROM contacts;
常用于空值替换
IFIF(condition, true_val, false_val)三元条件判断SELECT IF(score >= 60, 'Pass', 'Fail') FROM exams;简化简单条件逻辑
NULLIFNULLIF(expr1, expr2)若两值相等则返回 NULLSELECT NULLIF(0, 0); -- NULL
SELECT NULLIF(1, 0); -- 1
常用于避免除零:x / NULLIF(y, 0)
ISNULL / IFNULLISNULL(expr, replacement)扩展:若为 NULL 则替换SELECT ISNULL(department, 'Unknown') FROM employees;SQLite 兼容函数,等价于 COALESCE
AND / OR / NOTcondition AND condition逻辑运算WHERE active AND (age > 18 OR override);标准布尔逻辑
BETWEEN / INexpr BETWEEN low AND high范围/集合判断WHERE score BETWEEN 0 AND 100;
WHERE status IN ('active', 'pending');
见 5.1

第7章:外部数据交互

学习如何导入导出数据,连接外部文件与系统。

7.1 读取 CSV 文件(READ_CSV)

方法/语法语法格式用途代码示例注意事项
READ_CSV()SELECT * FROM READ_CSV('file.csv')直接查询 CSV 文件SELECT * FROM READ_CSV('employees.csv');DuckDB 自动推断模式
指定列类型READ_CSV('file.csv', columns={'col': 'type', ...})显式定义列和类型SELECT * FROM READ_CSV('data.csv', columns={'id': 'INTEGER', 'name': 'VARCHAR', 'salary': 'DOUBLE'});避免类型推断错误
自动模式推断AUTO_DETECT=TRUE启用自动检测分隔符、标题等SELECT * FROM READ_CSV('data.txt', AUTO_DETECT=TRUE);推荐用于格式不规范的文件
指定分隔符DELIMITER=','设置字段分隔符SELECT * FROM READ_CSV('data.tsv', DELIMITER='\t');支持 ','
是否有标题行HEADER=TRUE|FALSE指定文件是否包含列名SELECT * FROM READ_CSV('no_header.csv', HEADER=FALSE);
多文件读取READ_CSV(['f1.csv','f2.csv'])合并多个 CSV 文件SELECT * FROM READ_CSV(['sales_2023.csv', 'sales_2024.csv']);自动 UNION ALL,结构需一致
通配符读取READ_CSV('sales_*.csv')使用通配符匹配文件SELECT region, SUM(amount) FROM READ_CSV('sales_*.csv') GROUP BY region;方便批量处理
创建表基于 CSVCREATE TABLE t AS SELECT * FROM READ_CSV(...)将 CSV 数据加载到表中CREATE TABLE employees AS SELECT * FROM READ_CSV('emp.csv', AUTO_DETECT=TRUE);实现”导入”操作

7.2 读取 Parquet 文件(READ_PARQUET)

方法/语法语法格式用途代码示例注意事项
READ_PARQUET()SELECT * FROM READ_PARQUET('file.parquet')查询 Parquet 文件SELECT * FROM READ_PARQUET('users.parquet');高效列式存储,推荐使用
读取目录READ_PARQUET('dir/')读取目录下所有 Parquet 文件SELECT * FROM READ_PARQUET('logs/');自动合并,支持分区目录
通配符读取READ_PARQUET('data_*.parquet')匹配多个文件SELECT * FROM READ_PARQUET('sales_*.parquet');类似 CSV
元数据查看PRAGMA file_metadata('file.parquet')查看 Parquet 文件元数据PRAGMA file_metadata('users.parquet');包含行组、列统计等信息
列裁剪SELECT col1, col2 FROM ...自动只读取所需列SELECT name, age FROM READ_PARQUET('big.parquet');Parquet 优势:I/O 高效
谓词下推WHERE col = value自动下推过滤条件SELECT * FROM READ_PARQUET('data.parquet') WHERE status = 'active';仅扫描匹配的行组,性能极佳
分区读取支持 Hive 分区自动识别分区列-- 目录结构: logs/year=2023/month=01/file.parquet
SELECT * FROM READ_PARQUET('logs/') WHERE year = 2023;
分区列自动作为字段可用
创建表基于 ParquetCREATE TABLE t AS SELECT * FROM READ_PARQUET(...)导入 Parquet 到表CREATE TABLE users AS SELECT * FROM READ_PARQUET('users.parquet');快速加载

7.3 读取 JSON 文件(READ_JSON)

方法/语法语法格式用途代码示例注意事项
READ_JSON()SELECT * FROM READ_JSON('file.json')读取 JSON 文件(数组)SELECT * FROM READ_JSON('data.json');支持 JSON Lines (.jsonl) 和数组
JSON Lines每行一个 JSON 对象逐行解析SELECT * FROM READ_JSON('logs.jsonl');流式处理,内存友好
自动模式推断AUTO_DETECT=TRUE推断嵌套结构类型SELECT * FROM READ_JSON('data.json', AUTO_DETECT=TRUE);处理复杂 JSON 结构
指定模式COLUMNS={'col': 'type', ...}显式定义模式SELECT * FROM READ_JSON('data.json', COLUMNS={'id': 'INT', 'info': 'STRUCT(name VARCHAR, age INT)'});精确控制,避免推断错误
嵌套 JSON 访问使用 -> 操作符提取嵌套字段SELECT json_col->'address'->>'city' AS city FROM (SELECT READ_JSON('users.json') AS json_col);-> 返回 JSON,->> 返回文本
读取多个文件READ_JSON(['f1.json','f2.json'])合并多个 JSON 文件SELECT * FROM READ_JSON(['data1.json', 'data2.json']);结构应一致
创建表基于 JSONCREATE TABLE t AS SELECT * FROM READ_JSON(...)导入 JSON 数据CREATE TABLE events AS SELECT * FROM READ_JSON('events.jsonl', AUTO_DETECT=TRUE);实现 JSON 到关系表转换

7.4 导出为 CSV、Parquet、JSON

格式导出语法用途代码示例注意事项
导出为 CSVCOPY (query) TO 'file.csv' WITH (FORMAT CSV, HEADER true)将查询结果保存为 CSVCOPY (SELECT * FROM employees) TO 'output.csv' WITH (FORMAT CSV, HEADER true);支持 HEADER, DELIMITER, QUOTE 等选项
导出为 ParquetCOPY (query) TO 'file.parquet' (FORMAT PARQUET)导出为 Parquet 格式COPY (SELECT * FROM sales) TO 'sales.parquet';高效存储,推荐长期保存
导出为 JSONCOPY (query) TO 'file.json' (FORMAT JSON)导出为 JSON 数组COPY (SELECT * FROM users) TO 'users.json';生成 [{},{}] 格式
导出为 JSON LinesCOPY ... (FORMAT JSON, ARRAY false)每行一个 JSON 对象COPY (SELECT * FROM logs) TO 'logs.jsonl' WITH (FORMAT JSON, ARRAY false);流式处理友好
压缩导出COMPRESSION 'zstd'启用压缩COPY (SELECT * FROM big_table) TO 'data.parquet' (COMPRESSION 'ZSTD');支持 SNAPPY, GZIP, ZSTD 等,减小文件大小
分区导出PARTITION_BY (col)按列分区导出COPY (SELECT * FROM sales) TO 'sales/' (FORMAT PARQUET) PARTITION_BY (region);生成 Hive 分区目录结构,便于后续查询
从表导出COPY table_name TO ...直接导出整个表COPY employees TO 'emp.csv' (FORMAT CSV, HEADER true);简化操作

7.5 与 Pandas DataFrame 交互(df.to_sql / duckdb.sql)

方法/方向语法用途代码示例注意事项
DuckDB 查询 → DataFrameconn.execute(sql).df()执行 SQL 并返回 DataFrameimport duckdb
con = duckdb.connect()
df = con.execute("SELECT * FROM employees").df()
最常用方式
DataFrame → DuckDB 表con.register('name', df)注册 DataFrame 为虚拟表con.register('sales_df', sales_dataframe)
result = con.execute("SELECT region, SUM(amount) FROM sales_df GROUP BY region").df()
无需复制数据,高效
直接查询 DataFramecon.execute("SELECT * FROM df").df()把 DataFrame 当作表查询# 假设 df 已存在
filtered = con.execute("SELECT * FROM df WHERE age > 30").df()
DataFrame 名称作为表名
将结果保存到 DataFrameassign 或 create_view创建视图或变量con.execute("CREATE VIEW v AS SELECT * FROM df WHERE active")
v_df = con.table('v').df()
用于复杂工作流
批量处理结合 register 和 SQL利用 DuckDB 处理大 DataFramecon.register('large_df', big_df)
aggregated = con.execute("SELECT category, AVG(value) AS avg_val FROM large_df GROUP BY category").df()
避免 Pandas 内存瓶颈

7.6 与 Arrow 表集成

方法/方向语法用途代码示例注意事项
Arrow Table → DuckDBcon.register('name', table)注册 Arrow 表为虚拟表import pyarrow as pa
import duckdb
table = pa.table([pa.array([1, 2, 3]), pa.array(['a', 'b', 'c'])], names=['id', 'value'])
con = duckdb.connect()
con.register('arrow_table', table)
result = con.execute("SELECT * FROM arrow_table WHERE id > 1").fetch_arrow_table()
零拷贝,极高性能
DuckDB → Arrow Tableconn.execute(sql).fetch_arrow_table()执行 SQL 返回 Arrow 表arrow_result = con.execute("SELECT * FROM employees").fetch_arrow_table()用于与 Arrow 生态(如 Polars)集成
批量数据交换register + fetch_arrow_table在 DuckDB 和 Arrow 间高效传输# 处理 Arrow 数据
processed = con.execute("SELECT id, UPPER(value) AS upper_val FROM arrow_table").fetch_arrow_table()
避免序列化开销
支持数据类型完全兼容 Arrow 类型系统无缝映射包括 STRUCT, LIST, DICTIONARY 等复杂类型
内存效率零拷贝访问不复制数据特别适合大内存数据集处理
与 Pandas 对比更高效Arrow 是列式内存格式对于数值计算和大表,Arrow 比 Pandas DataFrame 更高效

第8章:性能优化与高级特性

提升查询效率,掌握 DuckDB 高级功能。

8.1 索引支持(CREATE INDEX)

方法/语法语法格式用途代码示例注意事项
创建 B-Tree 索引CREATE INDEX idx_name ON table(col)加速等值和范围查询CREATE INDEX idx_salary ON employees(salary);DuckDB 支持 B-Tree 索引,适用于 =, >, <, BETWEEN, IN
创建复合索引CREATE INDEX idx_multi ON table(col1, col2)支持多列查询条件CREATE INDEX idx_dept_role ON employees(department, role);遵循最左前缀原则,查询需使用索引前列
唯一索引CREATE UNIQUE INDEX idx_uniq ON table(col)确保列值唯一并加速查询CREATE UNIQUE INDEX idx_email ON employees(email);插入重复值会报错
查看索引PRAGMA show_indexes ON table_name列出表的所有索引PRAGMA show_indexes ON employees;用于调试和验证
删除索引DROP INDEX idx_name移除索引DROP INDEX idx_salary;重建或优化时使用
索引与性能在 WHERE、JOIN、ORDER BY 中使用减少扫描行数,加速排序SELECT * FROM employees WHERE salary > 70000; -- 使用 idx_salary索引会增加写入开销,需权衡
自动索引(临时)DuckDB 可能为优化 JOIN 创建临时索引内部优化机制用户无需干预,但了解有助于理解执行计划

8.2 分区表与分块读取

方法/语法语法格式用途代码示例注意事项
创建分区表CREATE TABLE ... (col TYPE) PARTITION BY (partition_col)按列对表进行物理分区CREATE TABLE sales (sale_date DATE, amount DOUBLE, region VARCHAR) PARTITION BY (region);数据按分区列的值存储在不同文件/块中
插入分区数据INSERT INTO table VALUES (...)自动路由到对应分区INSERT INTO sales VALUES ('2023-01-01', 1000, 'North');写入时自动定位分区
分区裁剪WHERE partition_col = value查询时自动跳过无关分区SELECT SUM(amount) FROM sales WHERE region = 'South';极大减少 I/O,核心性能优势
外部文件分区READ_PARQUET('path/', hive_partitioning=TRUE)读取 Hive 风格分区目录SELECT * FROM READ_PARQUET('sales_data/', hive_partitioning=TRUE) WHERE year = 2023 AND month = '01';目录结构如 sales_data/year=2023/month=01/file.parquet
分块读取(内部)DuckDB 自动分块将大文件/表分块处理内部机制,支持并行处理和内存管理
优势减少扫描范围、并行处理、高效存储优化大表查询性能特别适合时间序列、日志、按区域/类别划分的数据
注意事项分区列选择选择高基数、常用于过滤的列避免过度分区(小文件问题)

8.3 并行查询与向量化执行机制

特性说明用途代码示例注意事项
并行查询DuckDB 自动利用多核 CPU加速大数据集处理SELECT department, AVG(salary), COUNT(*) FROM employees GROUP BY department;大多数查询自动并行化,无需手动配置
向量化执行一次处理一批数据(向量)提高 CPU 缓存效率和指令吞吐核心引擎特性,所有操作均向量化
批处理大小默认 1024 行/批平衡内存和计算效率可通过 PRAGMA vector_size 调整(不推荐)
并行 I/O读取 Parquet/CSV 时并行快速加载外部数据SELECT COUNT(*) FROM READ_PARQUET('huge_file.parquet');利用多核和磁盘带宽
并行聚合GROUP BY 在多个线程上并行执行加速分组聚合对于高基数分组效果显著
并行排序ORDER BY 多线程执行加速大型排序SELECT * FROM big_table ORDER BY key;使用外部排序算法,支持大于内存的数据
资源控制可设置线程数限制资源使用PRAGMA threads=4; -- 设置最大线程数在资源受限环境中有用

8.4 使用 EXPLAIN 查看执行计划

命令语法用途代码示例注意事项
EXPLAINEXPLAIN SELECT ...查看查询执行计划(文本)EXPLAIN SELECT * FROM employees WHERE salary > 70000;显示操作符树,了解执行流程
EXPLAIN QUERY PLANEXPLAIN QUERY PLAN SELECT ...更简洁的执行计划EXPLAIN QUERY PLAN SELECT COUNT(*) FROM sales;类似 SQLite,显示高层次操作
输出解读包含操作符如 SEQ_SCAN, HASH_JOIN, AGGREGATE识别性能瓶颈关注是否使用索引、JOIN 策略、聚合方式
识别全表扫描SEQ_SCAN 表名可能缺少索引或谓词下推失败检查 WHERE 条件和索引
识别 JOIN 类型NESTED LOOP, HASH JOIN, MERGE JOIN了解连接效率HASH JOIN 通常最快,MERGE JOIN 用于有序数据
结合性能测试配合实际执行时间验证优化效果PRAGMA enable_profiling;
SELECT ...; -- 查看详细耗时
enable_profiling 可生成更详细的性能报告

8.5 缓存与内存管理

特性/方法说明用途代码示例注意事项
内存数据库默认在内存中运行高速数据处理con = duckdb.connect() # 内存数据库断开连接后数据丢失
持久化数据库connect('db_name.db')将数据持久化到磁盘con = duckdb.connect('my_db.db')数据库文件可复用
内存限制PRAGMA memory_limit设置最大内存使用PRAGMA memory_limit='2GB';防止 OOM,处理超大数据集时启用外部排序/聚合
自动溢出到磁盘当内存不足时支持处理大于内存的数据集透明进行,性能会下降但保证查询完成
数据缓存DuckDB 自动缓存热数据提高重复查询性能无需手动干预
结果缓存无内置查询缓存每次执行重新计算应用层可实现缓存逻辑
优化建议合理设置 memory_limit,利用持久化平衡性能与资源对于 ETL 任务,内存数据库 + 最后持久化是高效模式

8.6 自定义函数(UDF)与扩展(如 httpfs, json, parquet)

特性/方法语法/方式用途代码示例注意事项
加载扩展LOAD 'extension_name'启用额外功能LOAD 'httpfs'; -- 访问 S3/GCS
LOAD 'json'; -- 增强 JSON 支持
LOAD 'parquet'; -- 确保 Parquet 支持
大多数功能默认启用,httpfs 需显式加载
HTTPFS 扩展读取云存储文件直接查询 S3、GCS 等SELECT * FROM READ_PARQUET('s3://bucket/data/*.parquet');需配置凭据(环境变量或 AWS CLI)
创建标量 UDF(Python)con.create_function()在 Python 中定义自定义函数def greet(name): return f"Hello, {name}!"
con.create_function('greet', greet, [str], str)
con.execute("SELECT greet('Alice')").fetchone() # 'Hello, Alice!'
函数在 DuckDB 内部调用,可用于 SQL
创建向量化 UDF使用 Arrow 集成高性能自定义函数import pyarrow.compute as pc
def add_ten(arr): return pc.add(arr, 10)
con.create_vectorized_function('add_ten', add_ten, ['DOUBLE'], 'DOUBLE')
利用 Arrow 计算,性能接近原生
注册聚合 UDFcreate_aggregate_function()定义自定义聚合函数较复杂,需定义状态、步进、完成函数
常用扩展httpfs, json, parquet, icu, spellfix增强功能LOAD 'icu'; -- 启用国际化排序、大小写转换等扩展丰富,可根据需要加载
UDF 限制性能低于内置函数复杂逻辑的补充尽量使用内置函数,UDF 用于无法表达的逻辑

第9章:Python API 深度使用

聚焦 Python 中的 DuckDB 使用模式。

9.1 duckdb.connect() 参数详解

参数语法/值用途代码示例注意事项
database'dbname.db'':memory:'指定数据库文件或内存模式# 内存数据库(默认)
con = duckdb.connect()
# 持久化数据库
con = duckdb.connect('analytics.db')
':memory:' 显式指定内存,None'' 也表示内存
read_onlyTrue / False是否以只读模式打开con = duckdb.connect('data.db', read_only=True)防止意外修改,允许多进程同时读取同一文件
config{'key': 'value', ...}设置运行时配置参数con = duckdb.connect(config={'allow_unsigned_extensions': 'true', 'memory_limit': '1GB'})可设置内存、并行度、扩展策略等
check_same_threadTrue / False是否检查线程一致性con = duckdb.connect(check_same_thread=False)多线程应用中需设为 False
无参数调用duckdb.connect()创建临时内存连接con = duckdb.connect() # 最常用断开后数据丢失,适合临时分析

9.2 cursor 对象方法(execute, fetch_df, fetch_arrow_table 等)

方法语法用途代码示例注意事项
execute()con.execute(sql)执行 SQL 语句con.execute("CREATE TABLE t AS SELECT 42 AS a")
result = con.execute("SELECT * FROM t")
返回结果集对象,可链式调用 fetch 方法
fetchdf() / df()result.fetchdf()获取 Pandas DataFramedf = con.execute("SELECT * FROM t").df()fetchdf 和 df 是同义词,最常用
fetcharrowtable()result.fetcharrowtable()获取 PyArrow Tableat = con.execute("SELECT * FROM t").fetcharrowtable()高效,零拷贝,适合与 Polars、Dataset 等集成
fetchone()result.fetchone()获取单行元组row = con.execute("SELECT a FROM t").fetchone() # (42,)用于标量查询或逐行处理
fetchall()result.fetchall()获取所有行的元组列表rows = con.execute("SELECT * FROM t").fetchall() # [(42,)]全部加载到内存,大数据集慎用
fetchdf_chunked()result.fetch_record_batch()流式获取数据块result = con.execute("SELECT * FROM huge_table")
while True: batch = result.fetch_record_batch(); if not batch: break; process(batch)
处理超大数据集,避免内存溢出
close()result.close()关闭结果集通常自动管理,显式关闭可释放资源

9.3 使用 prepare() 执行预编译语句

方法语法用途代码示例注意事项
prepare()con.prepare(sql)预编译带参数的 SQLprep = con.prepare("SELECT ? + ? AS sum")
result = prep.execute([10, 20]).df()
参数用 ? 占位
执行预编译语句prep.execute([args])使用不同参数多次执行prep = con.prepare("INSERT INTO log VALUES (?, ?)")
prep.execute(['error', 'File not found'])
prep.execute(['info', 'Process started'])
高效,避免重复解析 SQL
命名参数con.prepare("SELECT $1 * $2")使用 $1, $2prep = con.prepare("SELECT $1 * $2 AS product")
prep.execute([5, 6])
与位置参数 ? 等效
性能优势减少解析开销循环中执行相同 SQL 时特别适合 ETL 中的批量插入/更新
生命周期prep 对象存活于连接期间连接关闭后失效

9.4 上下文管理器(with 语句)

用法语法用途代码示例注意事项
连接管理with duckdb.connect() as con:自动关闭连接with duckdb.connect('temp.db') as con:
con.execute("CREATE TABLE ...")
con.execute("INSERT INTO ...")
# 连接自动关闭
确保资源释放,即使发生异常
结果集管理with con.execute(sql) as res:自动关闭结果集with con.execute("SELECT * FROM t") as res:
df = res.df()
# 结果集自动关闭
较少使用,通常 fetch 后即释放
嵌套使用with 内创建表/数据临时分析工作流with duckdb.connect() as con:
con.execute("CREATE VIEW v AS SELECT 1 AS a")
df = con.execute("SELECT a+1 FROM v").df()
内存连接 + with 是安全的临时分析模式
异常安全自动处理异常防止资源泄漏推荐在生产脚本中使用

9.5 注册/卸载数据对象(register, unregister)

方法语法用途代码示例注意事项
register()con.register('name', obj)将 Python 对象注册为 SQL 表import pandas as pd
df = pd.DataFrame({'x': [1,2]})
con.register('my_df', df)
result = con.execute("SELECT x*2 FROM my_df").df()
支持 Pandas DataFrame, PyArrow Table, Polars DataFrame 等
unregister()con.unregister('name')移除注册的表名con.unregister('my_df')
# my_df 不再可查
释放引用,清理命名空间
作用域仅在当前连接有效其他连接无法访问
零拷贝不复制数据高效访问特别适合大对象
动态数据源可注册函数返回表高级用法def get_data(): return pd.DataFrame({'val': range(100)})
con.register('dynamic', get_data)
查询时调用函数

9.6 与 Pandas 高效交互模式

模式说明代码示例注意事项
注册 DataFrame将 df 作为虚拟表查询con.register('sales', sales_df)
summary = con.execute("SELECT region, SUM(amount) AS total FROM sales GROUP BY region").df()
避免 pd.read_sql 的低效,利用 DuckDB 引擎处理大 df
执行 SQL 返回 df利用 SQL 能力处理 Pandas 数据con.register('df', df)
cleaned = con.execute("SELECT * FROM df WHERE age BETWEEN 18 AND 65 AND salary IS NOT NULL").df()
比 Pandas 原生操作更简洁高效
混合处理DuckDB 处理重计算,Pandas 做轻量后处理# DuckDB 做聚合
agg = con.execute("SELECT cat, AVG(val) FROM df GROUP BY cat").df()
# Pandas 做可视化
agg.plot(kind='bar')
发挥各自优势
大数据集处理用 DuckDB 替代 Pandas 处理大于内存的数据# 不要用 pd.read_csv 大文件
con.execute("CREATE TABLE t AS SELECT * FROM READ_CSV('huge.csv')")
result = con.execute("SELECT ... FROM t ...").df()
避免 Pandas 内存溢出
性能对比DuckDB 通常比 Pandas 快

第10章:实战应用案例

综合运用所学知识解决实际问题。

10.1 日志分析系统(Nginx 日志解析)

步骤方法/语法用途代码示例注意事项
日志格式识别Nginx 默认 combined 格式了解字段结构"$remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\""需确认实际使用的日志格式
读取日志文件READ_CSV() + 正则解析加载日志并结构化-- 假设日志已按行存为文本
CREATE VIEW nginx_logs AS
SELECT
REGEXP_EXTRACT(line, '(\S+)', 1) AS ip,
REGEXP_EXTRACT(line, '\[(.+?)\]', 1) AS timestamp,
REGEXP_EXTRACT(line, '"(\S+) (\S+)', 1) AS method,
REGEXP_EXTRACT(line, '"(\S+) (\S+)', 2) AS url,
CAST(REGEXP_EXTRACT(line, ' (\d{3}) ', 1) AS INTEGER) AS status,
CAST(REGEXP_EXTRACT(line, ' (\d+) "$', 1) AS BIGINT) AS bytes
FROM READ_CSV('access.log', COLUMNS={'line': 'VARCHAR'}, SEP='\n');
使用 SEP='\n' 按行读取,再用正则提取字段
时间解析STRPTIME()将字符串转为时间戳SELECT STRPTIME(timestamp, '%d/%b/%Y:%H:%M:%S %z') AS ts FROM nginx_logs;格式需与日志中 [%d/%b/%Y:%H:%M:%S %z] 一致
基础统计COUNT, GROUP BY分析请求量、状态码等-- 每小时请求量
SELECT DATE_TRUNC('hour', ts) AS hour, COUNT(*) AS reqs FROM nginx_logs GROUP BY hour ORDER BY hour;
-- 状态码分布
SELECT status, COUNT(*) AS cnt FROM nginx_logs GROUP BY status;
快速生成监控指标
用户行为分析COUNT(DISTINCT), LIKE分析用户、来源、机器人-- 独立 IP 数
SELECT COUNT(DISTINCT ip) AS unique_ips FROM nginx_logs;
-- 来源分析
SELECT http_referer, COUNT(*) FROM nginx_logs GROUP BY http_referer;
-- 爬虫识别
SELECT COUNT(*) FROM nginx_logs WHERE LOWER(user_agent) LIKE '%bot%';
结合字符串函数进行模式识别
性能分析AVG, PERCENTILE分析响应大小、性能SELECT AVG(bytes) AS avg_bytes, PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY bytes) AS p95_bytes FROM nginx_logs;识别大文件传输或异常流量
导出分析结果COPY TO保存结果供可视化COPY (SELECT DATE_TRUNC('day', ts), COUNT(*) FROM nginx_logs GROUP BY 1) TO 'daily_traffic.csv' (HEADER, DELIMITER=',');生成报表数据

10.2 数据清洗流水线(ETL 简易实现)

步骤方法/语法用途代码示例注意事项
加载原始数据READ_CSV / READ_PARQUET读取待清洗数据CREATE OR REPLACE VIEW raw_data AS SELECT * FROM READ_CSV('raw_input.csv', AUTO_DETECT=TRUE);使用视图隔离原始数据
处理缺失值COALESCE, IS NULL填充或过滤空值SELECT COALESCE(name, 'Unknown') AS name, age, COALESCE(salary, (SELECT AVG(salary) FROM raw_data)) AS salary FROM raw_data WHERE email IS NOT NULL; -- 过滤关键字段为空根据业务逻辑决定填充策略
数据类型修正CAST, TRY_CAST确保类型正确SELECT name, TRY_CAST(age_str AS INTEGER) AS age, TRY_CAST(price_txt AS DOUBLE) AS price FROM raw_data WHERE TRY_CAST(age_str AS INTEGER) IS NOT NULL; -- 过滤转换失败TRY_CAST 避免因脏数据报错
去重ROW_NUMBER() 或 DISTINCT移除重复记录-- 方法1: 使用窗口函数保留第一条
CREATE VIEW cleaned_1 AS
SELECT * EXCEPT(rn) FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY updated_at DESC) AS rn FROM raw_data) WHERE rn = 1;
-- 方法2: 简单去重
SELECT DISTINCT * FROM raw_data;
根据主键和时间决定保留策略
标准化文本TRIM, UPPER, REPLACE统一文本格式SELECT TRIM(UPPER(name)) AS name, REPLACE(phone, '-', '') AS phone_clean FROM cleaned_1;确保一致性,便于后续匹配
创建维度表CREATE TABLE AS SELECT构建规范化表CREATE TABLE dim_product AS SELECT DISTINCT product_id, product_name, category FROM cleaned_data;
CREATE TABLE fact_sales AS SELECT sale_id, product_id, amount, sale_date FROM cleaned_data;
实现简单的维度建模
导出清洗后数据COPY TO保存结果COPY fact_sales TO 'cleaned_sales.parquet' (FORMAT PARQUET);
COPY dim_product TO 'dim_product.parquet' (FORMAT PARQUET);
推荐使用 Parquet 作为中间/最终存储

10.3 本地 OLAP 分析(销售数据分析)

分析类型SQL 查询用途代码示例注意事项
基础聚合SUM, COUNT, GROUP BY总体销售情况SELECT SUM(amount) AS total_sales, COUNT(*) AS order_count, AVG(amount) AS avg_order_value FROM sales;快速了解业务规模
时间趋势分析DATE_TRUNC, GROUP BY销售随时间变化SELECT DATE_TRUNC('month', order_date) AS month, SUM(amount) AS monthly_sales FROM sales GROUP BY month ORDER BY month;识别季节性、增长趋势
产品分析GROUP BY, ORDER BY畅销产品排行SELECT product_name, SUM(amount) AS product_sales, COUNT(*) AS units_sold FROM sales GROUP BY product_name ORDER BY product_sales DESC LIMIT 10;识别核心产品
区域分析GROUP BY, JOIN各地区业绩对比SELECT r.region_name, SUM(s.amount) AS region_sales FROM sales s JOIN regions r ON s.region_id = r.id GROUP BY r.region_name;结合地理维度
客户分层CASE, SUMRFM 或自定义分层SELECT customer_id, SUM(amount) AS total_spent, CASE WHEN SUM(amount) > 10000 THEN 'VIP' WHEN SUM(amount) > 1000 THEN 'Regular' ELSE 'New' END AS customer_tier FROM sales GROUP BY customer_id;支持精细化运营
同比/环比LAG(), DATE arithmetic增长率计算WITH monthly AS (SELECT DATE_TRUNC('month', order_date) AS m, SUM(amount) AS sales FROM sales GROUP BY m) SELECT m, sales, LAG(sales, 1) OVER (ORDER BY m) AS prev_month, (sales - LAG(sales, 1) OVER (ORDER BY m)) / LAG(sales, 1) OVER (ORDER BY m) AS growth_rate FROM monthly;分析业务健康度
多维分析(CUBE)GROUPING SETS / CUBE全组合聚合SELECT region, product_category, SUM(amount) AS sales FROM sales GROUP BY CUBE(region, product_category);生成交叉报表,但可能数据量大

10.4 嵌入式分析服务(FastAPI + DuckDB)

组件/步骤实现方式用途代码示例(Python)注意事项
项目结构FastAPI + duckdb创建 REST APIfrom fastapi import FastAPI
import duckdb
app = FastAPI()
con = duckdb.connect('analytics.db', read_only=True) # 只读连接
将 DuckDB 作为嵌入式分析引擎
健康检查@app.get("/health")服务可用性检测@app.get("/health")
def health_check():
return {"status": "ok"}
标准运维接口
参数化查询 API@app.get("/sales")提供数据查询接口@app.get("/sales")
def get_sales(region: str = None, start_date: str = None):
query = "SELECT region, SUM(amount) FROM sales WHERE 1=1"
params = []
if region: query += " AND region = ?"; params.append(region)
if start_date: query += " AND order_date >= ?"; params.append(start_date)
query += " GROUP BY region"
df = con.execute(query, params).df()
return df.to_dict(orient='records')
使用预编译语句防止 SQL 注入
返回 JSON 结果.df().to_dict()将结果转为 JSON 响应# 在路由函数中
return df.to_dict(orient='records')
FastAPI 自动序列化
静态文件服务@app.get("/")提供前端页面from fastapi.staticfiles import StaticFiles
app.mount("/", StaticFiles(directory="frontend", html=True), name="frontend")
构建完整 Web 应用
连接池管理全局连接或依赖注入管理数据库连接# 简单场景:全局只读连接
# 复杂场景:使用 Depends 获取连接
高并发时考虑连接池或每个请求新连接
部署uvicorn.run()启动服务if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
可打包为 Docker 镜像或独立服务

总结: DuckDB 凭借其轻量、高性能、零依赖的特性,非常适合嵌入到 Python 应用中,为 Web 服务、数据分析工具、桌面应用等提供强大的本地分析能力。通过与 FastAPI 等框架结合,可以快速构建出功能完整的嵌入式 BI 或数据服务。