为啥 SQLite 需要这么长时间来获取数据?
Posted
技术标签:
【中文标题】为啥 SQLite 需要这么长时间来获取数据?【英文标题】:Why does SQLite take such a long time to fetch the data?为什么 SQLite 需要这么长时间来获取数据? 【发布时间】:2010-04-17 19:05:14 【问题描述】:我有两个可能的查询,都给出了我想要的结果集。
查询一个大约需要 30 毫秒,但从数据库中获取数据需要 150 毫秒。
SELECT
id
FROM
featurevalues as featval3
WHERE
featval3.feature IN (?,?,?,?)
AND
EXISTS
(
SELECT
1
FROM
product_to_value,
product_to_value as prod2,
features,
featurevalues
WHERE
product_to_value.feature = features.int
AND
product_to_value.value = featurevalues.id
AND
features.id = ?
AND
featurevalues.id IN (?,?)
AND
product_to_value.product = prod2.product
AND
prod2.value = featval3.id
)
查询二大约需要 3 毫秒 - 这是我因此更喜欢的一个 - 但也需要 170 毫秒来获取数据。
SELECT
(
SELECT
prod2.value
FROM
product_to_value,
product_to_value as prod2,
features,
featurevalues
WHERE
product_to_value.feature = features.int
AND
product_to_value.value = featurevalues.id
AND
features.id = ?
AND
featurevalues.id IN (?,?)
AND
product_to_value.product = prod2.product
AND
prod2.value = featval3.id
) as id
FROM
featurevalues as featval3
WHERE
featval3.feature IN (?,?,?,?)
170ms 似乎与featval3 表的行数有关。在 featval3.feature IN (?,?,?,?) 上使用索引后,feetval3 中“保留”了 151 个项目。
关于缓慢的获取,我有什么明显的遗漏吗?据我所知,一切都已正确编入索引。我很困惑,因为第二个查询只需要 3 毫秒即可运行。
更新
这是我在第二个查询上运行 EXPLAIN 时得到的结果:
0 Trace 0 0 0 00
1 Variable 4 1 4 00
2 Goto 0 88 0 00
3 OpenRead 5 6883 0 00
4 If 6 16 0 00
5 Integer 1 6 0 00
6 OpenEphemeral 7 1 0 00
7 Null 0 8 0 00
8 MakeRecord 1 1 8 00
9 IdxInsert 7 8 0 00
10 MakeRecord 2 1 8 00
11 IdxInsert 7 8 0 00
12 MakeRecord 3 1 8 00
13 IdxInsert 7 8 0 00
14 MakeRecord 4 1 8 00
15 IdxInsert 7 8 0 00
16 Rewind 7 86 0 00
17 Column 7 0 5 00
18 IsNull 5 85 0 00
19 Affinity 5 1 0 00
20 SeekGe 5 85 5 00
21 IdxGE 5 85 5 01
22 Null 0 10 0 00
23 Integer 1 11 0 00
24 MustBeInt 11 0 0 00
25 IfZero 11 82 0 00
26 Variable 1 12 3 00
27 OpenRead 2 25 0 00
28 OpenRead 8 7005 0 00
29 OpenRead 9 26 0 00
30 OpenRead 10 6732 0 00
31 OpenRead 11 6766 0 00
32 Column 5 1 15 00
33 IsNull 15 77 0 00
34 Affinity 15 1 0 00
35 SeekGe 8 77 15 00
36 IdxGE 8 77 15 01
37 IdxRowid 8 8 0 00
38 Seek 2 8 0 00
39 Column 2 0 16 00
40 IsNull 16 76 0 00
41 Affinity 16 1 0 00
42 SeekGe 9 76 16 00
43 IdxGE 9 76 16 01
44 Column 9 1 17 00
45 IsNull 17 75 0 00
46 SCopy 12 18 0 00
47 IsNull 18 75 0 00
48 Affinity 17 2 0 00
49 SeekGe 10 75 17 00
50 IdxGE 10 75 17 01
51 If 20 59 0 00
52 Integer 1 20 0 00
53 OpenEphemeral 13 1 0 00
54 Null 0 21 0 00
55 MakeRecord 13 1 21 00
56 IdxInsert 13 21 0 00
57 MakeRecord 14 1 21 00
58 IdxInsert 13 21 0 00
59 Rewind 13 74 0 00
60 Column 13 0 19 00
61 IsNull 19 73 0 00
62 Affinity 19 1 0 00
63 SeekGe 11 73 19 00
64 IdxGE 11 73 19 01
65 Column 9 2 21 00
66 Column 11 0 7 00
67 Ne 7 72 21 6a
68 Column 8 0 22 00
69 Move 22 10 1 00
70 AddImm 11 -1 0 00
71 IfZero 11 77 0 00
72 Next 11 64 0 00
73 Next 13 60 0 00
74 Next 10 50 0 00
75 Next 9 43 0 00
76 Next 8 36 0 00
77 Close 2 0 0 00
78 Close 8 0 0 00
79 Close 9 0 0 00
80 Close 10 0 0 00
81 Close 11 0 0 00
82 SCopy 10 9 0 00
83 ResultRow 9 1 0 00
84 Next 5 21 0 00
85 Next 7 17 0 00
86 Close 5 0 0 00
87 Halt 0 0 0 00
88 Transaction 0 0 0 00
89 VerifyCookie 0 319 0 00
90 TableLock 0 14 0 00
91 TableLock 0 25 0 00
92 TableLock 0 11 0 00
93 Goto 0 3 0 00
但不确定是什么意思。
【问题讨论】:
你是如何获取数据的? 使用 php:while ($row = $statement->fetch(PDO::FETCH_OBJ))
【参考方案1】:
我不确定 sqlite,但大多数数据库不会在您执行查询时立即计算整个结果集。他们编译和优化查询,然后通过获取前几行开始运行它。
有点像打开一个大文件很快,但读取所有内容需要很长时间。
【讨论】:
【参考方案2】:您可能应该在这里使用 JOIN,并且您使用相关子查询可能会减慢速度。虽然优化器通常可以执行带有 where 子句的交叉连接,就好像它是连接一样,并且还可以将相关子查询作为连接执行,但我不指望 SQLite 在所有情况下都能做到这一点。我认为用 JOIN 重写您的查询会给出这样的结果:
SELECT DISTINCT prod2.value AS id
FROM product_to_value
JOIN features ON product_to_value.feature = features.int
JOIN product_to_value as prod2 ON product_to_value.product = prod2.product
JOIN featurevalues as featval3 ON prod2.value = featval3.id
JOIN featurevalues ON product_to_value.value = featurevalues.id
WHERE features.id = ?
AND featurevalues.id IN (?,?)
AND featval3.feature IN (?,?,?,?)
试试这个,看看它更快(并且仍然给出正确的结果)。
【讨论】:
SQLite 将 JOIN 转换为 WHERE 子句。我很确定查询会做必须做的事情。 @Derk:好的,我错过了。很难理解您的查询在做什么。我添加了 DISTINCT。相关子查询可能会很慢,因为您可能需要对表/索引进行多次扫描,而不仅仅是一次扫描。 @Derk:大约 0.3 毫秒?对于每一个输入?您确定在计时时,结果尚未针对您使用的输入进行缓存吗? @Derk:优化器应该自动选择合适的索引。因此,请确保您拥有所有适当的索引(主键也是索引) - 如果您不确定,请发布您的表定义。不幸的是,我不熟悉 SQLite 解释语法,所以我看不到您是否在读取该输出时缺少索引。 @Derk:奇怪....我无法解释为什么会这样。不过很高兴你找到了解决方案。【参考方案3】:为什么不看一下查询计划并自己确定答案呢?这是一个参考: How can I analyse a Sqlite query execution?
【讨论】:
【参考方案4】:在您编写 sql 查询时使用 Begin Transaction 和 End Transaction。此处对此进行了解释,并提供了许多其他有用的信息:
SQLite Optimization FAQ
您可以在此处查看 where 子句的索引详细信息:
SQLite Query Optimizer Overview
【讨论】:
如果你不使用 BEGIN/END TRANSACTION,每条语句都有自己的事务。使用显式事务可以提高多条语句的效率,但 OP 只执行一条。以上是关于为啥 SQLite 需要这么长时间来获取数据?的主要内容,如果未能解决你的问题,请参考以下文章
什么是 Qt MaintenanceTool 元数据,为什么要花这么长时间才能获取?
为啥'withColumn'在pyspark中需要这么长时间?
将数据从(大)文件 Excel 导入 datagridview,然后导入数据库 - 为啥插入数据库需要这么长时间并且不能保存所有数据?