0

我有一个查询,它使用从整数数组(使用参数)动态生成WHERE id IN (1,2,3,...)列表的位置。现在我有一个特定的查询,它需要大约 500 毫秒的 26623 个 ID,但需要 50 秒(慢 100 倍)和 26624 个 ID。(1,2,3,...)

我在https://sqlite.org/limits.html中找不到任何看起来相关的内容

SELECT params.name AS name, json_group_array(DISTINCT params.value) AS "values"
FROM view_requests AS req, search_params(search) AS params
JOIN flows ON flows.request_id = req.id
WHERE search NOT IN ('', '?')
AND flows.id IN (1,2,3) /* <=== here more than 26623 IDs make it super slow  */
GROUP BY params.name
ORDER BY json_array_length("values") DESC, params.name ASC

在我尝试使其在隔离中可重现(例如search_params,自定义虚拟表)之前,有谁知道我可能会遇到什么限制?这不是 ID 本身的数量,因为不同的查询使用相同的 ID 运行得很好。

SQLite 版本 3.36.0 通过 Better-sqlite3 (Node.js) 和只读数据库。我使用的唯一编译指示是journal_mode = WAL.

编译(https://github.com/JoshuaWise/better-sqlite3/blob/master/docs/compilation.md#bundled-configuration):

SQLITE_DQS=0
SQLITE_LIKE_DOESNT_MATCH_BLOBS
SQLITE_THREADSAFE=2
SQLITE_USE_URI=0
SQLITE_DEFAULT_MEMSTATUS=0
SQLITE_OMIT_DEPRECATED
SQLITE_OMIT_GET_TABLE
SQLITE_OMIT_TCL_VARIABLE
SQLITE_OMIT_PROGRESS_CALLBACK
SQLITE_OMIT_SHARED_CACHE
SQLITE_TRACE_SIZE_LIMIT=32
SQLITE_DEFAULT_CACHE_SIZE=-16000
SQLITE_DEFAULT_FOREIGN_KEYS=1
SQLITE_DEFAULT_WAL_SYNCHRONOUS=1
SQLITE_ENABLE_MATH_FUNCTIONS
SQLITE_ENABLE_DESERIALIZE
SQLITE_ENABLE_COLUMN_METADATA
SQLITE_ENABLE_UPDATE_DELETE_LIMIT
SQLITE_ENABLE_STAT4
SQLITE_ENABLE_FTS3_PARENTHESIS
SQLITE_ENABLE_FTS3
SQLITE_ENABLE_FTS4
SQLITE_ENABLE_FTS5
SQLITE_ENABLE_JSON1
SQLITE_ENABLE_RTREE
SQLITE_ENABLE_GEOPOLY
SQLITE_INTROSPECTION_PRAGMAS
SQLITE_SOUNDEX
HAVE_STDINT_H=1
HAVE_INT8_T=1
HAVE_INT16_T=1
HAVE_INT32_T=1
HAVE_UINT8_T=1
HAVE_UINT16_T=1
HAVE_UINT32_T=1
4

1 回答 1

0

这是来自 SQLite 论坛的答案。本质上,这是查询计划器如何处理IN文字和我的虚拟表估计成本的组合。这意味着我正遇到查询计划者做出不同决定的确切时刻。

SQLite NGQP 是一个基于成本的查询计划器。带有文字值列表的 IN () 运算符被实现为一种临时表;有时 SQLite 决定创建索引并进行查找,有时它决定使用该表作为查询的最外层循环。

EXPLAIN QUERY PLAN 应该以更简洁的方式显示。

如果在启用 WHERETRACE 的调试模式下编译,.wheretrace 命令将显示 SQLite NGQP 如何达到其计划。基本输入是虚拟表的 xBestIndex 方法的返回值,尤其是“行数”和“估计成本”。提供准确的估计至关重要。成本应反映相对于 SQLite 本机表的处理成本。

请注意,您可以通过将 IN 表命名为 CTE 和 CROSS JOIN 来强制执行快速运行的查询计划。

https://sqlite.org/forum/forumpost/a3d68ed8b40cf583?t=h

我使用的解决方法是json_each将整数数组序列化为 JSON 字符串。在我的特定用例中,这还有其他一些好处(例如,我可以绑定单个参数并重新使用具有任意数量 ID 的查询),所以我不介意这样做:

 SELECT params.name AS name, json_group_array(DISTINCT params.value) AS "values"
 FROM view_requests AS req, search_params(search) AS params
 JOIN flows ON flows.request_id = req.id
 WHERE search NOT IN ('', '?')
-AND flows.id IN (1,2,3)
+AND flows.id IN (SELECT value FROM json_each('[1,2,3]'))
 GROUP BY params.name
 ORDER BY json_array_length("values") DESC, params.name ASC

我也知道 better-sqlite3 的通用虚拟表实现在易于使用(非常简单)和实现最大性能之间进行了权衡。

于 2021-10-24T08:54:22.233 回答