【问题标题】:How to make Sqlite use an index for ordering on multiple columns in the case of multiple selection from the same table?如何让Sqlite在同一张表多选的情况下使用索引对多列进行排序?
【发布时间】:2014-10-20 00:10:52
【问题描述】:

我有一张有几十万行的表格。 (这是一个预先计算好的表,表示词的词元与其他大表之间的关系。) 我需要进行多项选择以找到不同条目的组合,即我必须使用“AS”来进行选择……从 ltc 作为 l0,ltc 作为 l1,ltc 作为 l2……排序…… 查询的速度取决于排序:不排序是几毫秒,排序是几分钟。据我所知,这是由于 Sqlite 为排序而构建的临时 B-Tree,即使我在排序列“nr”上有一个索引。我不明白为什么 Sqlite 不使用这个索引。

CREATE TABLE ltc
(nr INTEGER, lemId INTEGER, cId INTEGER, bId INTEGER,
-- UNIQUE (lemId, cId, bId), 
-- if I add this uniqueness constraint, strangely enough it doesn’t use my index at all, even at a simple ORDER BY.
PRIMARY KEY(nr,lemId,cId),
FOREIGN KEY(lemId) REFERENCES lems(rowid),
FOREIGN KEY(cId) REFERENCES cs(rowid),
FOREIGN KEY(bId) REFERENCES bs(rowid) )

CREATE INDEX nri ON ltc(nr)

这是我的选择命令的精简版:

SELECT  l0.nr,l1.nr,l2.nr
FROM ltc as l0, ltc as l1, ltc as l2
WHERE 
    l0.lemId IN (1001) -- in reality 1001 is some simple sub select.
AND l1.lemId IN (1002,1003)
AND l2.lemId IN (1004 )
ORDER BY
    l0.nr,
    l1.nr,
    l2.nr
LIMIT 10;

EXPLAIN QUERY PLAN 给出:

(0, 0, 0, u'SCAN TABLE ltc AS l0')
(0, 0, 0, u'EXECUTE LIST SUBQUERY 1')
(1, 0, 0, u'SEARCH TABLE lems USING COVERING INDEX lem (lem=?)')
(0, 1, 1, u'SCAN TABLE ltc AS l1')
(0, 0, 0, u'EXECUTE LIST SUBQUERY 2')
(2, 0, 0, u'SEARCH TABLE lems USING COVERING INDEX lem (lem=?)')
(0, 2, 2, u'SCAN TABLE ltc AS l2')
(0, 0, 0, u'EXECUTE LIST SUBQUERY 3')
(3, 0, 0, u'SEARCH TABLE lems USING COVERING INDEX lem (lem=?)')
(0, 0, 0, u'USE TEMP B-TREE FOR ORDER BY')

删除了 ORDER BY(或减少到只有一列 order by l0.nr):

(0, 0, 0, u'SCAN TABLE ltc AS l0 USING COVERING INDEX sqlite_autoindex_ltc_1')
(0, 0, 0, u'EXECUTE LIST SUBQUERY 1')
(1, 0, 0, u'SEARCH TABLE lems USING COVERING INDEX lem (lem=?)')
(0, 1, 1, u'SCAN TABLE ltc AS l1 USING COVERING INDEX sqlite_autoindex_ltc_1')
(0, 0, 0, u'EXECUTE LIST SUBQUERY 2')
(2, 0, 0, u'SEARCH TABLE lems USING COVERING INDEX lem (lem=?)')
(0, 2, 2, u'SCAN TABLE ltc AS l2 USING COVERING INDEX sqlite_autoindex_ltc_1')
(0, 0, 0, u'EXECUTE LIST SUBQUERY 3')
(3, 0, 0, u'SEARCH TABLE lems USING COVERING INDEX lem (lem=?)')

我尝试了各种单一和组合的 indeces,但似乎没有任何区别。

问题似乎是双重排序本身而不是双重选择:即使是无用的双重 ORDER BY 也会创建一个临时 b 树(即使在这种情况下结果是立即的):

EXPLAIN QUERY PLAN SELECT  ltc.nr
FROM ltc
WHERE 
ltc.lemId = 716 
ORDER BY
    ltc.nr,
    ltc.nr
LIMIT 10;

(0, 0, 0, u'SCAN TABLE ltc')
(0, 0, 0, u'USE TEMP B-TREE FOR ORDER BY')

SQLite ORDER BY performance issue 据说查询不能按来自不同表的索引排序。这是这里的问题吗?有办法吗?这是 Sqlite 特定的限制还是所有 SQL 系统都这样做?

编辑:

添加索引后,按照 CL 的建议,性能问题依然存在。 以一个包含四个搜索词的更完整的查询为例:

select  l0.nr,l1.nr,l2.nr,l3.nr
    from ltc as l0, ltc as l1, ltc as l2, ltc as l3 

    where 
        l0.lemId in (select rowid from lems where lems.lem = "catch" )
        and l1.lemId in (select rowid from lems where lems.lem = "cause" )
        and l2.lemId in (select rowid from lems where lems.lem = "score" )
        and l3.lemId in (select rowid from lems where lems.lem = "guest" )

    order by
        l0.nr asc

    LIMIT 10;

给出了这样的解释:

(0, 0, 0, u'SEARCH TABLE ltc AS l0 USING INDEX lid (lemId=?)')
(0, 0, 0, u'EXECUTE LIST SUBQUERY 1')
(1, 0, 0, u'SEARCH TABLE lems USING COVERING INDEX lem (lem=?)')
(0, 1, 1, u'SEARCH TABLE ltc AS l1 USING INDEX lid (lemId=?)')
(0, 0, 0, u'EXECUTE LIST SUBQUERY 2')
(2, 0, 0, u'SEARCH TABLE lems USING COVERING INDEX lem (lem=?)')
(0, 2, 2, u'SEARCH TABLE ltc AS l2 USING INDEX lid (lemId=?)')
(0, 0, 0, u'EXECUTE LIST SUBQUERY 3')
(3, 0, 0, u'SEARCH TABLE lems USING COVERING INDEX lem (lem=?)')
(0, 3, 3, u'SEARCH TABLE ltc AS l3 USING INDEX lid (lemId=?)')
(0, 0, 0, u'EXECUTE LIST SUBQUERY 4')
(4, 0, 0, u'SEARCH TABLE lems USING COVERING INDEX lem (lem=?)')
(0, 0, 0, u'USE TEMP B-TREE FOR ORDER BY')

(不再进行完整扫描。)

但是:时间:388 秒!!!

当删除 order by 时,我得到完全相同的解释减去最后一个临时 B 树!

时间:0.00025 秒!!!


这个查询对应于某种连接。我还可以将查询表示为(内部)连接(带条件)。这可能是时间似乎随着搜索词数量呈指数增长的原因:{1 个搜索词:0.08 秒,2:0.5,3:3,4:9,5:116,...} 但不知何故,我不太明白为什么数据库不能简单地使用 nr 列上的索引进行排序。毕竟,只是需要排序的结果很多,每个结果都包含 nr


按照 CL 的建议,我将根本问题放在一个新问题中:Selecting tuples of lines from an Sqlite table and sorting the tuples efficiently

【问题讨论】:

  • 不要改问题! (这个问题是是否可以使用索引进行排序,答案是“否”。)针对实际问题提出一个新问题(并澄清cId的值应该相同还是不同;当前描述自相矛盾)。还包括示例输入/输出数据。
  • 好吧,你是对的。这是新问题:[stackoverflow.com/questions/25526087/…

标签: sqlite indexing sql-order-by explain


【解决方案1】:

只有当查询允许按照它们在索引中存储的顺序返回行时,才能使用索引来加速排序。

当使用具有不同索引的另一列来查找行时,或者由于交叉连接而返回多行时,这是不可能的。

尝试在lemId 上添加索引,但这不太可能有助于排序。

排序很慢,因为 LIMIT 之前的结果太多。

【讨论】:

  • 是的,你是对的,在 lemId 上添加索引会删除没有索引的扫描,但性能问题仍然存在。我相应地编辑了我的问题。也许我的整个方法都是错误的。我在上面对问题的解释不同。
猜你喜欢
  • 2014-11-14
  • 1970-01-01
  • 1970-01-01
  • 2023-03-05
  • 2020-07-24
  • 2019-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多