【问题标题】:Efficient sqlite query based on list of primary keys基于主键列表的高效sqlite查询
【发布时间】:2016-11-07 00:48:36
【问题描述】:

为了根据 ID 列表(即不同的主键)查询 sqlite 表,我使用以下语句(基于 Chinook Database 的示例):

SELECT * FROM Customer WHERE CustomerId IN (1,2,3,8,20,35)

但是,我的实际 ID 列表可能会变得相当大 (>1000)。因此,我想知道这种使用IN 语句的方法是否最有效,或者是否有更好/优化的方法来根据主键列表查询 sqlite 表。

【问题讨论】:

    标签: performance sqlite select primary-key


    【解决方案1】:

    SQLite Optimizer Overview

    IN (expression-list) 确实使用索引(如果可用)。

    除此之外,我无法从中获得任何保证,因此以下内容取决于性能测量。

    轴1:如何传递表达式列表

    • hardocde 作为字符串。整数到字符串转换和字符串到整数解析的开销
    • 绑定参数(即语句为... WHERE CustomerID in (?,?,?,?,?,?,?,?,?,?....),它比硬编码值更容易从预定义字符串构建)。防止 int → string → int 转换,但参数数量的默认限制是 999。这可以增加 SQLITE_LIMIT_VARIABLE_NUMBER,但可能会导致分配过多。
    • 临时表。 在准备好语句之后,可能效率低于上述任何方法,但如果大部分时间都花在准备语句上,这将无济于事

    轴 2:语句优化

    如果在多个查询中使用相同的表达式列表以防止更改CustomerIDs,则以下之一可能有帮助:

    • 重用带有硬编码值的 prepared statement(即不要传递 1001 个参数)
    • 为带有索引的 CustomerID 创建一个临时表(因此索引只创建一次,而不是为每个查询即时创建)

    如果每个查询的表达式列表都不同,最好让 SQLite 完成它的工作。以下可能会有所改进

    • 为表达式列表创建一个临时表
    • 使用union all 批量插入表达式列表元素
    • 使用子查询

    (根据我使用 SQLite 的经验,我希望它与 SQLite 持平或稍差)


    轴 3问理查德

    sqlite 邮件列表(是的,我知道,这项技术甚至比转盘电话还要古老!)非常活跃,通常提供极好的建议,包括 SQLite 的作者。 90% 的可能性有人会在“问问题之前衡量!”,10% 的可能性有人会给你详细的见解。

    【讨论】:

      【解决方案2】:

      因为您编写的语句不包含任何关于如何 找到您想要的行的 SQLite 指令,所以“优化”的概念并不真正存在——没有什么可优化的。规划检索数据的最佳算法的工作属于 SQLite 查询优化器。

      一些数据库的查询优化器确实有一些特性,这可能会导致性能问题,但我不希望 SQLite 为这个简单的查询找到正确的算法,即使 IN 列表中有很多值。如果您发现存在性能问题,我只会担心尝试将查询优化器引导到另一个执行计划。

      【讨论】:

        【解决方案3】:

        如果 IN 中的元素数量足够多,SQLite 会为它们构造一个临时索引。这可能比手动创建temporary table 更有效。

        IN 列表的长度仅限于maximum length of an SQL statement,并受内存限制。

        【讨论】:

        • 你能链接到记录的地方吗,自动使用临时表进行大型 IN 查询?我在 Android 上使用 SQLite 的经验,我遇到了 SQLITE_MAX_VARIABLE_NUMBER,即 999。
        • @JeffLockhart 您的 Android 可能有旧的 SQLite 版本。此类详细信息的唯一文档是源代码。
        • 有趣。如果您可以大致指出此优化发生在源代码中的哪个位置,我可能可以将其缩小到引入它的版本,并与在各种 Android 平台版本上运行的版本进行比较:stackoverflow.com/questions/2421189/… 这是几年前的事了我最初遇到过这种情况,所以我很想知道这种行为是否发生了变化。
        • @JeffLockhart IN(...) 内容在parse.y 中使用exprlist(A) 进行解析。临时表是在expr.csqlite3FindInIndex() 的最后一部分生成的。无论如何,如果您使用参数,那么您将不可避免地遇到 SQLITE_MAX_VARIABLE_NUMBER。
        • 感谢您的信息。探源,在expr.c中描述为:Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)' expression it is handled the same way. An ephemeral table is filled with index keys representing the results from the SELECT or the <exprlist>.
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-05-15
        • 2020-09-28
        • 2016-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多