1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
| # DuckDB - 直接查询 CSV(零导入)
import duckdb
conn = duckdb.connect()
result = conn.execute("SELECT * FROM 'data/ecommerce_orders.csv'").fetchall()
conn.close()
# SQLite - 需要创建表并导入
import sqlite3
import csv
conn = sqlite3.connect('test.db')
cursor = conn.cursor()
cursor.execute('CREATE TABLE orders (...)')
with open('data/ecommerce_orders.csv', 'r') as f:
reader = csv.reader(f)
next(reader)
cursor.executemany('INSERT INTO orders VALUES (?, ?, ?, ?, ?, ?)', reader)
conn.commit()
```text
测试结果:
| 操作 | DuckDB | SQLite | 速度对比 |
|------|--------|--------|----------|
| 读取/导入数据 | 76.56ms | 13.81ms | SQLite 快 5.5 倍 |
当时看到这个结果我愣了一下。DuckDB 不是号称"零导入"吗?怎么反而比 SQLite 慢?
后来想明白了:DuckDB 的"零导入"是指不需要预先创建表结构,但每次查询都要从 CSV 文件重新读取解析。而 SQLite 虽然需要导入,但导入完成后数据就在内存/磁盘里了,后续查询直接读取。
### 简单查询
```sql
SELECT * FROM table WHERE product = 'Phone'
```sql
| 数据库 | 耗时 | 返回行数 | 速度对比 |
|--------|------|----------|----------|
| DuckDB | 75.73ms | 176 行 | - |
| SQLite | 0.57ms | 176 行 | SQLite 快 132 倍 |
### 聚合查询
```sql
SELECT product, SUM(quantity), AVG(price)
FROM table
GROUP BY product
ORDER BY SUM(quantity) DESC
```text
| 数据库 | 耗时 | 返回行数 | 速度对比 |
|--------|------|----------|----------|
| DuckDB | 71.36ms | 6 行 | - |
| SQLite | 0.97ms | 6 行 | SQLite 快 73 倍 |
### 窗口函数
```sql
SELECT * FROM (
SELECT order_id, product, price,
ROW_NUMBER() OVER (PARTITION BY product ORDER BY price DESC) as rn
FROM table
) WHERE rn <= 3
```sql
| 数据库 | 耗时 | 返回行数 | 速度对比 |
|--------|------|----------|----------|
| DuckDB | 82.21ms | 18 行 | - |
| SQLite | 1.80ms | 18 行 | SQLite 快 45 倍 |
### JOIN 查询
```sql
WITH max_orders AS (
SELECT customer_id, MAX(price) as max_price
FROM table
GROUP BY customer_id
)
SELECT o.customer_id, o.order_id, o.price
FROM table o
JOIN max_orders m ON o.customer_id = m.customer_id AND o.price = m.max_price
LIMIT 10
```text
| 数据库 | 耗时 | 返回行数 | 速度对比 |
|--------|------|----------|----------|
| DuckDB | 136.47ms | 10 行 | - |
| SQLite | 0.89ms | 10 行 | SQLite 快 153 倍 |
### 日期函数
```sql
-- DuckDB
SELECT DATE_TRUNC('month', CAST(order_date AS DATE)) as month, COUNT(*)
FROM table
GROUP BY month
-- SQLite
SELECT strftime('%Y-%m', order_date) as month, COUNT(*)
FROM table
GROUP BY month
```sql
| 数据库 | 耗时 | 返回行数 | 速度对比 |
|--------|------|----------|----------|
| DuckDB | 75.33ms | 12 行 | - |
| SQLite | 1.00ms | 12 行 | SQLite 快 75 倍 |
### 小数据集测试总结
在 1000 行数据、已导入的场景下,SQLite 的查询性能完全碾压 DuckDB,快了 45 到 153 倍。
这个结果可能会让一些 DuckDB 粉丝不舒服,但这就是事实。小数据集场景下,SQLite 就是更快。
---
## 大数据集测试(10 万行)
### 聚合查询
```sql
SELECT product, COUNT(*), SUM(quantity), AVG(unit_price)
FROM large_orders
GROUP BY product
ORDER BY COUNT(*) DESC
```text
| 数据库 | 导入时间 | 查询时间 | 总耗时 | 速度对比 |
|--------|----------|----------|--------|----------|
| DuckDB | 0ms(零导入) | 137.02ms | 137.02ms | - |
| SQLite | 401.83ms | 61.15ms | 462.98ms | 总耗时 DuckDB 快 3.38 倍 |
这里有个关键发现:
- **包含导入时间**:DuckDB 快 3.38 倍
- **纯查询时间**:SQLite 快 2.2 倍(61.15ms vs 137.02ms)
也就是说,即使数据量到了 10 万行,SQLite 的纯查询性能仍然比 DuckDB 快。但 DuckDB 的优势在于"零导入",省去了数据准备的时间。
如果你的场景是"拿到 CSV 文件就要立刻分析",DuckDB 更合适。如果数据已经导入数据库了,SQLite 的查询更快。
---
## 功能对比
| 功能 | DuckDB | SQLite | 说明 |
|------|--------|--------|------|
| 直接查询 CSV | ✅ 原生支持 | ❌ 需要导入 | DuckDB 核心优势 |
| 直接查询 Parquet | ✅ 原生支持 | ❌ 不支持 | DuckDB 独有 |
| 直接查询 JSON | ✅ 原生支持 | ❌ 需要导入 | DuckDB 核心优势 |
| 窗口函数 | ✅ 完整支持 | ✅ 3.25+ 支持 | 两者都支持 |
| CTE (WITH) | ✅ 支持 | ✅ 支持 | 两者都支持 |
| 递归查询 | ✅ 支持 | ✅ 支持 | 两者都支持 |
| 全文搜索 | ✅ 扩展支持 | ✅ FTS5 | SQLite 更成熟 |
| 事务支持 | ✅ ACID | ✅ ACID | 两者都支持 |
| 触发器 | ❌ 不支持 | ✅ 支持 | SQLite 优势 |
| 并发写入 | 1 写多读 | 1 写多读 | 两者都有限制 |
| 索引 | 自动优化 | 手动创建 | SQLite 更灵活 |
---
## 存储空间对比
| 格式 | 大小 | 说明 |
|------|------|------|
| CSV 原始文件 | 56.30 KB | 文本格式 |
| SQLite 数据库 | 64.00 KB | 持久化存储 |
| DuckDB | 0 KB | 无需持久化,直接查询 CSV |
DuckDB 的"零存储"优势在这里体现得很明显。不需要额外的数据库文件,直接查询原始 CSV 文件。
---
## 踩坑记录
### 坑 1:DuckDB 小数据集性能"翻车"
**现象:** 1000 行数据,DuckDB 查询比 SQLite 慢 45-153 倍
**原因:** DuckDB 每次查询都要从 CSV 重新读取解析,SQLite 已预先导入到内存/磁盘
**结论:** DuckDB 优势在大数据量和零导入,小数据集已导入场景 SQLite 更快
### 坑 2:大文件列名不一致
**现象:** large_ecommerce_orders.csv 列名与 ecommerce_orders.csv 不同
**原因:** 大文件多了 category、region、discount 等字段,price 改为 unit_price
**解决:** 测试脚本需要适配不同列名
### 坑 3:DuckDB 日期函数语法
**现象:** DuckDB 使用 `DATE_TRUNC('month', CAST(order_date AS DATE))`
**对比:** SQLite 使用 `strftime('%Y-%m', order_date)`
**注意:** 两者日期函数语法不同,迁移时需要注意
---
## 适用场景建议
### 选择 DuckDB 的场景
1. **数据分析/BI 报表**:需要快速分析 CSV/Parquet/JSON 文件
2. **数据科学/ML**:与 Pandas/Polars 集成,特征工程
3. **ETL 数据管道**:数据转换、清洗、聚合
4. **临时分析**:不想预先导入数据,直接查询原始文件
5. **大数据量分析**:10 万行以上数据分析查询
### 选择 SQLite 的场景
1. **嵌入式应用**:移动应用、桌面应用、IoT 设备
2. **高频小事务**:需要频繁 INSERT/UPDATE/DELETE
3. **小数据集**:几千行以内的数据,已预先导入
4. **需要触发器**:业务逻辑触发器
5. **成熟稳定**:20+ 年历史,生态完善
---
## 总结
通过这次实测,几个关键结论:
1. **DuckDB 不是 SQLite 的替代品**,两者定位完全不同
2. **DuckDB 核心优势**:零导入直接查询 CSV/Parquet/JSON,适合数据分析场景
3. **SQLite 核心优势**:小数据集高频事务,适合嵌入式应用
4. **性能对比**:
- 小数据集(1000 行)已导入:SQLite 快 45-153 倍
- 大数据集(10 万行)包含导入:DuckDB 快 3.38 倍
- 大数据集纯查询:SQLite 快 2.2 倍
选择建议很简单:
- 数据分析/BI/ETL → DuckDB
- 嵌入式应用/高频事务 → SQLite
- 临时分析/不想导入数据 → DuckDB
- 成熟稳定/需要触发器 → SQLite
没有哪个数据库是万能的,关键看用在哪里。
---
## 参考资源
- [DuckDB 官方文档](https://duckdb.org/docs/)
- [SQLite 官方文档](https://www.sqlite.org/docs.html)
- [DuckDB vs SQLite 测试代码](https://github.com/datalabx/duckdb-sqlite-benchmark)
- [DuckDB 论文:A Column-Store DBMS in a Single-Host Setting](https://www.cidrdb.org/cidr2021/papers/cidr2021_paper15.pdf)
---
## 延伸阅读
- [DuckDB vs PostgreSQL:嵌入式分析与服务端数据库的正面对决](/p/duckdb-vs-postgresql/) - 1000 行数据 DuckDB 3ms 内完成
- [DuckDB vs Pandas 性能对比:10 万行数据实测](/p/duckdb-vs-pandas/) - DuckDB 平均快 2.3 倍,内存省 55%
- [DuckDB 安装完全指南:从入门到实战](/p/duckdb-install-guide/) - Python/CLI/Node.js 三种方式实测
| 数据库 | 导入时间 | 查询时间 | 总耗时 | 速度对比 |
|--------|----------|----------|--------|----------|
| DuckDB | 0ms(零导入) | 137.02ms | 137.02ms | - |
| SQLite | 401.83ms | 61.15ms | 462.98ms | 总耗时 DuckDB 快 3.38 倍 |
这里有个关键发现:
- **包含导入时间**:DuckDB 快 3.38 倍
- **纯查询时间**:SQLite 快 2.2 倍(61.15ms vs 137.02ms)
也就是说,即使数据量到了 10 万行,SQLite 的纯查询性能仍然比 DuckDB 快。但 DuckDB 的优势在于"零导入",省去了数据准备的时间。
如果你的场景是"拿到 CSV 文件就要立刻分析",DuckDB 更合适。如果数据已经导入数据库了,SQLite 的查询更快。
---
## 功能对比
| 功能 | DuckDB | SQLite | 说明 |
|------|--------|--------|------|
| 直接查询 CSV | ✅ 原生支持 | ❌ 需要导入 | DuckDB 核心优势 |
| 直接查询 Parquet | ✅ 原生支持 | ❌ 不支持 | DuckDB 独有 |
| 直接查询 JSON | ✅ 原生支持 | ❌ 需要导入 | DuckDB 核心优势 |
| 窗口函数 | ✅ 完整支持 | ✅ 3.25+ 支持 | 两者都支持 |
| CTE (WITH) | ✅ 支持 | ✅ 支持 | 两者都支持 |
| 递归查询 | ✅ 支持 | ✅ 支持 | 两者都支持 |
| 全文搜索 | ✅ 扩展支持 | ✅ FTS5 | SQLite 更成熟 |
| 事务支持 | ✅ ACID | ✅ ACID | 两者都支持 |
| 触发器 | ❌ 不支持 | ✅ 支持 | SQLite 优势 |
| 并发写入 | 1 写多读 | 1 写多读 | 两者都有限制 |
| 索引 | 自动优化 | 手动创建 | SQLite 更灵活 |
---
## 存储空间对比
| 格式 | 大小 | 说明 |
|------|------|------|
| CSV 原始文件 | 56.30 KB | 文本格式 |
| SQLite 数据库 | 64.00 KB | 持久化存储 |
| DuckDB | 0 KB | 无需持久化,直接查询 CSV |
DuckDB 的"零存储"优势在这里体现得很明显。不需要额外的数据库文件,直接查询原始 CSV 文件。
---
## 踩坑记录
### 坑 1:DuckDB 小数据集性能"翻车"
**现象:** 1000 行数据,DuckDB 查询比 SQLite 慢 45-153 倍
**原因:** DuckDB 每次查询都要从 CSV 重新读取解析,SQLite 已预先导入到内存/磁盘
**结论:** DuckDB 优势在大数据量和零导入,小数据集已导入场景 SQLite 更快
### 坑 2:大文件列名不一致
**现象:** large_ecommerce_orders.csv 列名与 ecommerce_orders.csv 不同
**原因:** 大文件多了 category、region、discount 等字段,price 改为 unit_price
**解决:** 测试脚本需要适配不同列名
### 坑 3:DuckDB 日期函数语法
**现象:** DuckDB 使用 `DATE_TRUNC('month', CAST(order_date AS DATE))`
**对比:** SQLite 使用 `strftime('%Y-%m', order_date)`
**注意:** 两者日期函数语法不同,迁移时需要注意
---
## 适用场景建议
### 选择 DuckDB 的场景
1. **数据分析/BI 报表**:需要快速分析 CSV/Parquet/JSON 文件
2. **数据科学/ML**:与 Pandas/Polars 集成,特征工程
3. **ETL 数据管道**:数据转换、清洗、聚合
4. **临时分析**:不想预先导入数据,直接查询原始文件
5. **大数据量分析**:10 万行以上数据分析查询
### 选择 SQLite 的场景
1. **嵌入式应用**:移动应用、桌面应用、IoT 设备
2. **高频小事务**:需要频繁 INSERT/UPDATE/DELETE
3. **小数据集**:几千行以内的数据,已预先导入
4. **需要触发器**:业务逻辑触发器
5. **成熟稳定**:20+ 年历史,生态完善
---
## 总结
通过这次实测,几个关键结论:
1. **DuckDB 不是 SQLite 的替代品**,两者定位完全不同
2. **DuckDB 核心优势**:零导入直接查询 CSV/Parquet/JSON,适合数据分析场景
3. **SQLite 核心优势**:小数据集高频事务,适合嵌入式应用
4. **性能对比**:
- 小数据集(1000 行)已导入:SQLite 快 45-153 倍
- 大数据集(10 万行)包含导入:DuckDB 快 3.38 倍
- 大数据集纯查询:SQLite 快 2.2 倍
选择建议很简单:
- 数据分析/BI/ETL → DuckDB
- 嵌入式应用/高频事务 → SQLite
- 临时分析/不想导入数据 → DuckDB
- 成熟稳定/需要触发器 → SQLite
没有哪个数据库是万能的,关键看用在哪里。
---
## 参考资源
- [DuckDB 官方文档](https://duckdb.org/docs/)
- [SQLite 官方文档](https://www.sqlite.org/docs.html)
- [DuckDB vs SQLite 测试代码](https://github.com/datalabx/duckdb-sqlite-benchmark)
- [DuckDB 论文:A Column-Store DBMS in a Single-Host Setting](https://www.cidrdb.org/cidr2021/papers/cidr2021_paper15.pdf)
---
## 延伸阅读
- [DuckDB vs PostgreSQL:嵌入式分析与服务端数据库的正面对决](/p/duckdb-vs-postgresql/) - 1000 行数据 DuckDB 3ms 内完成
- [DuckDB vs Pandas 性能对比:10 万行数据实测](/p/duckdb-vs-pandas/) - DuckDB 平均快 2.3 倍,内存省 55%
- [DuckDB 安装完全指南:从入门到实战](/p/duckdb-install-guide/) - Python/CLI/Node.js 三种方式实测
|