DuckDB Python 客户端的最新稳定版本是 1.5.0。
安装
可以使用 pip 安装 DuckDB Python API:pip install duckdb。详情请参阅安装页面。也可以使用 conda 安装 DuckDB:conda install python-duckdb -c conda-forge。
Python 版本: DuckDB 需要 Python 3.9 或更高版本。
基本 API 用法
使用 DuckDB 运行 SQL 查询最直接的方法是使用 duckdb.sql 命令。
import duckdb
duckdb.sql("SELECT 42").show()
这将使用存储在 Python 模块内部全局的内存数据库来执行查询。查询结果作为关系(Relation)返回。关系是查询的符号表示。除非获取结果或请求将其打印到屏幕上,否则查询不会执行。
关系可以通过将它们存储在变量中并将其用作表,从而在后续查询中引用。通过这种方式,可以逐步构建查询。
import duckdb
r1 = duckdb.sql("SELECT 42 AS i")
duckdb.sql("SELECT i * 2 AS k FROM r1").show()
数据输入
DuckDB 可以摄取多种格式的数据——包括磁盘数据和内存数据。更多信息请参阅数据摄取页面。
import duckdb
duckdb.read_csv("example.csv") # read a CSV file into a Relation
duckdb.read_parquet("example.parquet") # read a Parquet file into a Relation
duckdb.read_json("example.json") # read a JSON file into a Relation
duckdb.sql("SELECT * FROM 'example.csv'") # directly query a CSV file
duckdb.sql("SELECT * FROM 'example.parquet'") # directly query a Parquet file
duckdb.sql("SELECT * FROM 'example.json'") # directly query a JSON file
数据帧 (DataFrames)
DuckDB 可以直接查询 Pandas DataFrames、Polars DataFrames 和 Arrow 表。请注意,这些是只读的,即无法通过 INSERT 或 UPDATE 语句编辑这些表。
Pandas
要直接查询 Pandas DataFrame,请运行
import duckdb
import pandas as pd
pandas_df = pd.DataFrame({"a": [42]})
duckdb.sql("SELECT * FROM pandas_df")
┌───────┐
│ a │
│ int64 │
├───────┤
│ 42 │
└───────┘
Polars
要直接查询 Polars DataFrame,请运行
import duckdb
import polars as pl
polars_df = pl.DataFrame({"a": [42]})
duckdb.sql("SELECT * FROM polars_df")
┌───────┐
│ a │
│ int64 │
├───────┤
│ 42 │
└───────┘
PyArrow
要直接查询 PyArrow 表,请运行
import duckdb
import pyarrow as pa
arrow_table = pa.Table.from_pydict({"a": [42]})
duckdb.sql("SELECT * FROM arrow_table")
┌───────┐
│ a │
│ int64 │
├───────┤
│ 42 │
└───────┘
结果转换
DuckDB 支持将查询结果高效地转换为各种格式。更多信息请参阅结果转换页面。
import duckdb
duckdb.sql("SELECT 42").fetchall() # Python objects
duckdb.sql("SELECT 42").df() # Pandas DataFrame
duckdb.sql("SELECT 42").pl() # Polars DataFrame
duckdb.sql("SELECT 42").arrow() # Arrow Table
duckdb.sql("SELECT 42").fetchnumpy() # NumPy Arrays
将数据写入磁盘
DuckDB 支持将关系对象直接以多种格式写入磁盘。或者,也可以使用 SQL 的 COPY 语句将数据写入磁盘。
import duckdb
duckdb.sql("SELECT 42").write_parquet("out.parquet") # Write to a Parquet file
duckdb.sql("SELECT 42").write_csv("out.csv") # Write to a CSV file
duckdb.sql("COPY (SELECT 42) TO 'out.parquet'") # Copy to a Parquet file
连接选项
应用程序可以通过 duckdb.connect() 方法打开一个新的 DuckDB 连接。
使用内存数据库
当通过 duckdb.sql() 使用 DuckDB 时,它在内存数据库上运行,即没有表会持久化到磁盘上。调用不带参数的 duckdb.connect() 方法会返回一个连接,该连接也使用内存数据库。
import duckdb
con = duckdb.connect()
con.sql("SELECT 42 AS x").show()
持久化存储
使用 duckdb.connect(dbname) 可以创建一个连接到持久化数据库的连接。写入该连接的任何数据都将被持久化,并且可以通过从 Python 或其他 DuckDB 客户端重新连接到同一个文件来重新加载。
import duckdb
# create a connection to a file called 'file.db'
con = duckdb.connect("file.db")
# create a table and load data into it
con.sql("CREATE TABLE test (i INTEGER)")
con.sql("INSERT INTO test VALUES (42)")
# query the table
con.table("test").show()
# explicitly close the connection
con.close()
# Note: connections also closed implicitly when they go out of scope
您还可以使用上下文管理器来确保连接被关闭
import duckdb
with duckdb.connect("file.db") as con:
con.sql("CREATE TABLE test (i INTEGER)")
con.sql("INSERT INTO test VALUES (42)")
con.table("test").show()
# the context manager closes the connection automatically
配置
duckdb.connect() 接受一个 config 字典,可以在其中指定配置选项。例如
import duckdb
con = duckdb.connect(config = {'threads': 1})
要指定存储版本,请传入 storage_compatibility_version 选项
import duckdb
con = duckdb.connect(config = {'storage_compatibility_version': 'latest'})
连接对象和模块
连接对象和 duckdb 模块可以互换使用——它们支持相同的方法。唯一的区别在于使用 duckdb 模块时,使用的是全局内存数据库。
如果您正在开发一个供他人使用的软件包,并在其中使用了 DuckDB,建议您创建连接对象,而不是使用
duckdb模块上的方法。这是因为duckdb模块使用共享的全局数据库——如果从多个不同的软件包内部使用,这可能会导致难以调试的问题。
在并行 Python 程序中使用连接
duckdb.sql() 和全局连接的线程安全性
duckdb.sql() 和 duckdb.connect(':default:') 使用共享的全局内存连接。此连接不是线程安全的,从多个线程对其运行查询可能会导致问题。要在并行环境中运行 DuckDB,每个线程必须拥有自己的连接。
def good_use():
con = duckdb.connect()
# uses new connection
con.sql("SELECT 1").fetchall()
相反,以下操作可能会导致并发问题,因为它们依赖于全局连接
def bad_use():
con = duckdb.connect(':default:')
# uses global connection
return con.sql("SELECT 1").fetchall()
或
def also_bad():
return duckdb.sql("SELECT 1").fetchall()
# uses global connection
避免使用 duckdb.sql() 或跨线程共享单个连接。
关于 cursor()
DuckDBPyConnection.cursor() 方法可在同一个连接上创建另一个句柄。它不会打开新连接。因此,从同一个连接创建的所有游标都不能同时运行查询。
社区扩展
要加载社区扩展,请在 install_extension 方法中使用 repository="community" 参数。
例如,安装并加载 h3 社区扩展的操作如下
import duckdb
con = duckdb.connect()
con.install_extension("h3", repository="community")
con.load_extension("h3")
未签名扩展
要加载未签名扩展,请使用
con = duckdb.connect(config={"allow_unsigned_extensions": "true"})
警告:仅从您信任的来源加载未签名扩展。避免通过 HTTP 加载未签名扩展。有关如何以安全方式设置 DuckDB 的指南,请查阅保护 DuckDB 页面。