为啥 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

但不确定是什么意思。

【问题讨论】:

你是如何获取数据的? 使用 phpwhile ($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 元数据,为什么要花这么长时间才能获取?

结果集需要很长时间来处理来自 Oracle 的大数据

为啥打开与我的数据库的连接需要这么长时间?

为啥这个查询需要这么长时间?

为啥'withColumn'在pyspark中需要这么长时间?

将数据从(大)文件 Excel 导入 datagridview,然后导入数据库 - 为啥插入数据库需要这么长时间并且不能保存所有数据?