【问题标题】:Combining a large number of conditions in SQLite WHERE clause在 SQLite WHERE 子句中组合大量条件
【发布时间】:2012-01-27 23:34:00
【问题描述】:

我需要检索与列表中存储的 ID 匹配的记录。运行时生成的查询很简单:

SELECT [whatever FROM sometable] WHERE (id = 1) or (id = 5) or (id = 33).

相当于

SELECT [whatever FROM sometable] WHERE [id] IN (1, 5, 33);

这很好,但如果列表包含成百上千个 ID 怎么办?该语句将很大,并且在某些时候 SQL 解析器可能会发出嘶哑的声音,或者如果没有,性能可能会非常糟糕。 我怎样才能以一种对检索到的记录数量不那么敏感的方式来做到这一点?

(我不能只遍历列表并逐个检索记录的原因是我需要数据库为我执行 ORDER BY。记录必须来自按特定字段排序的数据库,而该列表表示用户在网格中选择的记录,可以以多种方式排序。是的,我可以在检索记录后用代码对记录进行排序,但这是 B 计划,因为我什至不需要持有它们都在一个数据结构中,只是为了正确排序。)

【问题讨论】:

  • 你能把所有的 id 放到一个临时表中,然后说 SELECT [whatever FROM sometable] WHERE [id] IN (SELECT id FROM #temptable) 吗?
  • 我有更多的想法。 1)好的设计会阻止任何列表显示数千个ID。无论如何,人类一次只能看一个屏幕。人类很有可能正在对这些信息进行一些汇总。为他们做! 2)我根本无法相信循环查询的唯一原因。曾经! (我可能会在那里受热)。绝不! SQL 太强大了。这表明模型存在问题。把你的模型带到第三范式。它的工作量很小,但它在可扩展性方面带来了好处。
  • @Matt: (1) 说来话长,我希望避免它 :) 它是剪贴板管理器中的剪辑列表,但它可能是书签管理器中的 URL 等。显示可能已经被过滤(例如,仅今天添加的项目),用户通常只选择一个项目,或一次选择几个。但该应用程序没有施加任何任意限制。如果它恰好显示 2k 个项目并且用户点击 Ctrl+A 然后“导出”,那么我需要优雅地处理这个问题,即使这是一种罕见的情况。 (续)
  • (续)因为这些是剪辑,通常明智的做法是按时间顺序排列它们,即使它们目前是按字母顺序或长度等排序(用户决定)。这就是为什么选择的顺序可能不是记录需要来自数据库的顺序。 (2) 我同意循环。我问是因为我想避免它。
  • 因为有一个按时间顺序排列的附件,所以按日期分页。我仍然认为将这么多记录放在一个列表中是一种糟糕的做法。看看谷歌浏览器是如何做到的。历史/书签。添加按日期范围过滤器导出,按当前视图中选择的方式导出,并导出所有功能,您应该对几乎所有用户都很满意。或者,如果他们确实需要这么大的粒度而不是提供大视图,请提供一个过滤器控制系统,帮助他们缩小列表中的结果。甚至是“包含”导出功能。

标签: sql performance sqlite


【解决方案1】:

如果您真的要拥有这么多 ID,以至于担心 SQL 解析器会乱码,您可以将它们存储到临时表中并进行交叉连接。

只需使用一个(主键)列 ID 创建表,然后使用所需的 ID 填充它并使用类似的内容:

SELECT [whatever] FROM [sometable] st, [idtable] it
WHERE st.id = it.id

该查询不会阻塞任何解析器,检索到的行将仅限于在临时表中具有 ID 的行。

必须是一个临时表,当然,如果你确保一次只有一个“东西”使用它,你可以把它放在一边。

【讨论】:

    【解决方案2】:

    你能把这些项目添加到一个表中然后加入它吗?

    SELECT Whatever FROM TableA CROSS JOIN TableB ON TableA.ID = TableB.ID
    

    【讨论】:

      【解决方案3】:

      大概在确定要检索的 id 列表时涉及一些逻辑,这个逻辑可以不合并到 where 子句中吗?也许您可以加入一个包含某种会话 ID 以及所需 ID 的表?

      【讨论】:

      • 这基本上就是他使用 IN 子句所做的事情,但他在问这个列表在什么时候变得如此之大以至于 IN 不再有效,如果这是一个问题,他能做什么?去做吧……
      • 我的意思是,id 的选择是基于一些数据,这些数据更适合用在 where 子句而不是 id 中。例如,如果我们选择所有没有头发的用户的 id,您可以使用 WHERE hair='N' 而不是 id 列表。我的例子过于琐碎,但你明白了。
      • @Nineside:数据库中没有数据。正如我在问题中解释的那样,用户在网格中选择(Shift+单击或 Ctrl+单击)多个记录。我生成一个包含所选记录 ID 的列表。现在,我可以将“已选择”标志存储在数据库中,但要这样做,我必须解决与原始问题相同的问题。
      • 这实际上可能没有帮助,但是,如果您从多选框中选择行,我怀疑您的最终用户会使其阻塞编译器。有 1 个例外。列表框中的“全部”项。在这种情况下,我可能会考虑使用第二个查询来处理这种情况。此外,如果确实在所有条件之外选择了那么多记录,您可以考虑找到一些标准化的方法将它们分组到另一个表中。可能是组 ID,或者可以单独存储的某个量词。或者查看排除而不是包含列表。以为我会分享:)
      【解决方案4】:

      如果 where 子句中的值在表中,您可以这样做。

      select id from foo where id in (select id from bar)
      

      【讨论】:

        【解决方案5】:

        您的编程语言应该支持准备好的查询,即

         SELECT [whatever FROM sometable] WHERE (id = ?);
        

        或:

         SELECT [whatever FROM sometable] WHERE (id = @id);
        

        通过准备查询,查询可以重复使用,并且您可以将参数绑定到您的本地编程语言中的变量。如果此搜索频率很高,则值得将这些准备好的查询保留一段时间。

        这里有一个有用的讨论:

        【讨论】:

        • 我知道什么是准备好的查询,我会尽可能使用它们。不幸的是,这不适用于我要问的问题:)
        • 对 SQLite 没有帮助,但在 PostgreSQL 中查询将是 SELECT whatever FROM sometable WHERE id = SOME $1::integer[]。但是在没有数组的 SQLite 中,我认为临时辅助表是你最好的选择。
        猜你喜欢
        • 2011-02-13
        • 1970-01-01
        • 2013-08-30
        • 2016-10-16
        • 1970-01-01
        • 1970-01-01
        • 2021-11-28
        • 2017-04-14
        • 2010-09-28
        相关资源
        最近更新 更多