【问题标题】:Running into some SQLite limitation using IN operator使用 IN 运算符遇到一些 SQLite 限制
【发布时间】:2021-12-09 19:14:02
【问题描述】:

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

我在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

【问题讨论】:

    标签: sqlite better-sqlite3


    【解决方案1】:

    这是来自 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 的通用虚拟表实现在易于使用(非常简单)和实现最佳性能之间进行了权衡。

    【讨论】:

      猜你喜欢
      • 2015-05-05
      • 1970-01-01
      • 1970-01-01
      • 2021-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-11
      • 2013-09-10
      相关资源
      最近更新 更多