⌘+k ctrl+k
1.3 (稳定版)
搜索快捷键 cmd + k | ctrl + k
SQLite 扩展

SQLite 扩展允许 DuckDB 直接从 SQLite 数据库文件读取和写入数据。数据可以直接从底层的 SQLite 表中查询。数据可以从 SQLite 表加载到 DuckDB 表中,反之亦然。

安装和加载

sqlite 扩展在首次使用时将从官方扩展仓库透明地自动加载。如果您想手动安装和加载它,请运行

INSTALL sqlite;
LOAD sqlite;

用法

要使 SQLite 文件可供 DuckDB 访问,请使用带有 sqlitesqlite_scanner 类型的 ATTACH 语句。附加的 SQLite 数据库支持读写操作。

例如,要附加到 sakila.db 文件,请运行

ATTACH 'sakila.db' (TYPE sqlite);
USE sakila;

文件中的表可以像普通 DuckDB 表一样读取,但底层数据在查询时直接从文件中的 SQLite 表中读取。

SHOW TABLES;
name
actor
address
category
城市
国家
customer
customer_list
film
film_actor
film_category
film_list
film_text
inventory
language
payment
rental
sales_by_film_category
sales_by_store
staff
staff_list
store

您可以使用 SQL 查询表,例如,使用 sakila-examples.sql 中的示例查询

SELECT
    cat.name AS category_name,
    sum(ifnull(pay.amount, 0)) AS revenue
FROM category cat
LEFT JOIN film_category flm_cat
       ON cat.category_id = flm_cat.category_id
LEFT JOIN film fil
       ON flm_cat.film_id = fil.film_id
LEFT JOIN inventory inv
       ON fil.film_id = inv.film_id
LEFT JOIN rental ren
       ON inv.inventory_id = ren.inventory_id
LEFT JOIN payment pay
       ON ren.rental_id = pay.rental_id
GROUP BY cat.name
ORDER BY revenue DESC
LIMIT 5;

数据类型

SQLite 是一个弱类型数据库系统。因此,在 SQLite 表中存储数据时,不强制执行类型。以下是 SQLite 中有效的 SQL:

CREATE TABLE numbers (i INTEGER);
INSERT INTO numbers VALUES ('hello');

DuckDB 是一个强类型数据库系统,因此它要求所有列都具有定义的类型,并且系统严格检查数据的正确性。

在查询 SQLite 时,DuckDB 必须推断出特定的列类型映射。DuckDB 遵循 SQLite 的类型亲和规则,并进行了一些扩展。

  1. 如果声明的类型包含字符串 INT,则将其转换为类型 BIGINT
  2. 如果列的声明类型包含字符串 CHARCLOBTEXT 中的任何一个,则将其转换为 VARCHAR
  3. 如果列的声明类型包含字符串 BLOB,或者未指定类型,则将其转换为 BLOB
  4. 如果列的声明类型包含字符串 REALFLOADOUBDECNUM 中的任何一个,则将其转换为 DOUBLE
  5. 如果声明的类型是 DATE,则将其转换为 DATE
  6. 如果声明的类型包含字符串 TIME,则将其转换为 TIMESTAMP
  7. 如果以上都不适用,则将其转换为 VARCHAR

由于 DuckDB 强制相应列只包含正确类型的值,我们无法将字符串“hello”加载到 BIGINT 类型的列中。因此,当从上面的“numbers”表中读取时会抛出错误

Mismatch Type Error: Invalid type in column "i": column was declared as integer, found "hello" of type "text" instead.

可以通过设置 sqlite_all_varchar 选项来避免此错误

SET GLOBAL sqlite_all_varchar = true;

设置后,此选项将覆盖上述类型转换规则,而是始终将 SQLite 列转换为 VARCHAR 列。请注意,此设置必须在调用 sqlite_attach 之前进行。

直接打开 SQLite 数据库

SQLite 数据库也可以直接打开,并且可以透明地代替 DuckDB 数据库文件使用。在任何客户端连接时,都可以提供 SQLite 数据库文件的路径,然后将打开 SQLite 数据库。

例如,在 shell 中,SQLite 数据库可以按如下方式打开

duckdb sakila.db
SELECT first_name
FROM actor
LIMIT 3;
first_name
PENELOPE
NICK
ED

向 SQLite 写入数据

除了从 SQLite 读取数据外,该扩展还允许您使用标准 SQL 查询创建新的 SQLite 数据库文件、创建表、将数据导入 SQLite 并对 SQLite 数据库文件进行其他修改。

这允许您使用 DuckDB,例如,将存储在 SQLite 数据库中的数据导出到 Parquet,或者将 Parquet 文件中的数据读取到 SQLite 中。

以下是关于如何创建新的 SQLite 数据库并向其中加载数据的简要示例。

ATTACH 'new_sqlite_database.db' AS sqlite_db (TYPE sqlite);
CREATE TABLE sqlite_db.tbl (id INTEGER, name VARCHAR);
INSERT INTO sqlite_db.tbl VALUES (42, 'DuckDB');

然后可以从 SQLite 中读取生成的 SQLite 数据库。

sqlite3 new_sqlite_database.db
SQLite version 3.39.5 2022-10-14 20:58:05
sqlite> SELECT * FROM tbl;
id  name  
--  ------
42  DuckDB

支持对 SQLite 表进行许多操作。所有这些操作都直接修改 SQLite 数据库,后续操作的结果可以使用 SQLite 读取。

并发

当 DuckDB 或 SQLite 从不同的线程或单独的进程读写同一数据库时,DuckDB 可以读写 SQLite 数据库。多个线程或进程可以同时读取 SQLite 数据库,但每次只能有一个线程或进程写入数据库。数据库锁定由 SQLite 库处理,而不是 DuckDB。在同一进程内,SQLite 使用互斥锁。从不同进程访问时,SQLite 使用文件系统锁。锁定机制也取决于 SQLite 配置,如 WAL 模式。有关更多信息,请参阅SQLite 锁定文档

警告:将多个 SQLite 库副本链接到同一个应用程序可能会导致应用程序错误。有关更多信息,请参阅sqlite_scanner 问题 #82

设置

该扩展公开了以下配置参数。

名称 描述 默认值
sqlite_debug_show_queries 调试设置:将所有发送到 SQLite 的查询打印到标准输出 false

支持的操作

以下是支持的操作列表。

CREATE TABLE

CREATE TABLE sqlite_db.tbl (id INTEGER, name VARCHAR);

INSERT INTO

INSERT INTO sqlite_db.tbl VALUES (42, 'DuckDB');

SELECT

SELECT * FROM sqlite_db.tbl;
id name
42 DuckDB

COPY

COPY sqlite_db.tbl TO 'data.parquet';
COPY sqlite_db.tbl FROM 'data.parquet';

UPDATE

UPDATE sqlite_db.tbl SET name = 'Woohoo' WHERE id = 42;

DELETE

DELETE FROM sqlite_db.tbl WHERE id = 42;

ALTER TABLE

ALTER TABLE sqlite_db.tbl ADD COLUMN k INTEGER;

DROP TABLE

DROP TABLE sqlite_db.tbl;

CREATE VIEW

CREATE VIEW sqlite_db.v1 AS SELECT 42;

事务

CREATE TABLE sqlite_db.tmp (i INTEGER);
BEGIN;
INSERT INTO sqlite_db.tmp VALUES (42);
SELECT * FROM sqlite_db.tmp;
i
42
ROLLBACK;
SELECT * FROM sqlite_db.tmp;
i
 

已废弃:旧的 sqlite_attach 函数已废弃。建议切换到新的 ATTACH 语法