从 Sqlite 表中选择行的元组并有效地对元组进行排序

Posted

技术标签:

【中文标题】从 Sqlite 表中选择行的元组并有效地对元组进行排序【英文标题】:Selecting tuples of lines from an Sqlite table and sorting the tuples efficiently 【发布时间】:2014-08-27 11:33:44 【问题描述】:

我有一张非常大的桌子,有四列

+-----+--------+-----+------+ |天然橡胶 | lemId |身份证 |报价 | +-----+--------+-----+------+ | ... | | | | | 611 | 19 | 2 | 3 | | 611 | 19 | 3 | 3 | | 611 | 19 | 4 | 3 | | 611 | 19 | 5 | 3 | | 611 | 19 | 6 | 3 | | 611 | 19 | 1 | 3 | | 612 | 19 | 18 | 3 | | 612 | 19 | 7 | 3 | | 612 | 19 | 8 | 3 | | 613 | 19 | 1 | 205 | | 613 | 19 | 18 | 205 | | 614 | 19 | 2 | 224 | | 615 | 19 | 2 | 249 | | ... | | | | |第659章20 | 14 | 1434| |第659章20 | 15 | 1434| |第659章20 | 16 | 1434| |第659章20 | 17 | 1434| | 660 | 20 | 14 | 1483| | 660 | 20 | 15 | 1483| | 648 | 20 | 1 | 205 | | 648 | 20 | 18 | 205 | |第649章20 | 2 | 249 | |第649章20 | 3 | 249 | |第649章20 | 8 | 249 | | 650 | 20 | 4 | 279 | | 650 | 20 | 5 | 279 | | ... | | | | +-----+--------+-----+------+

对于 n lemIds (lem0, lem1, ...),我想从具有以下属性的表中选择 n 个不同行的元组:

row0:lemId = lem0, row1:lemId = lem1, 等 所有 n 行必须具有相同的 cId 所有 n 行必须有不同的 bId

这可以通过多选来完成。 这是两个 lemId(19 和 20)的示例

SELECT  l0.cId,l0.bId,l1.bId
    FROM ltc AS l0, ltc AS l1
    WHERE
        l0.cId=l1.cId  AND l0.bId!=l1.bId
        AND l0.lemId = 19
        AND l1.lemId = 20
LIMIT 10 OFFSET 0; 

到这里为止一切顺利。

我需要以混合 cId 的顺序获取结果行,这意味着,例如,如果结果中有 20 个不同的 cId,我首先需要这些不同的结果元组,然后再重复相同的 cId。 换句话说,如果在形式为 (cId, bId0, bId1) 的 1000 个结果元组中有 20 个不同的 cId(比如从 1 到 20),我需要得到如下结果:

(1, …)
(2, …)
…
(20, …)
(1, …)
(2, …)
...

因此,我在插入信息时预先计算了一个值:nr。当按这个值排序时,它给了我想要的顺序:

 order by
  l0.nr asc,
  l1.nr asc

问题是这种排序非常慢,并且似乎不可能使用How to make Sqlite use an index for ordering on multiple columns in the case of multiple selection from the same table? 的答案中建议的任何类型的索引,至少使用这种查询方式。此外,查询时间似乎随着元组的大小n呈指数增长,这可能是因为在排序过程中构建了一个temp B-tree

是否有某种方法可以有效地获得结果,甚至可能不使用 nr


这是来自上述查询的一些未排序的结果:

+-----+-----+------+ |身份证 | 0 | b1 | +-----+-----+------+ | 1 | 3 | 205 | | 2 | 3 | 249 | | 3 | 3 | 249 | | 4 | 3 | 279 | | 4 | 3 | 321 | | 5 | 3 | 279 | | 5 | 3 | 321 | | 6 | 3 | 321 | | 6 | 3 | 386 | | 7 | 3 | 321 | | 7 | 3 | 386 | | 8 | 3 | 249 | | 18 | 3 | 205 | | 1 | 3 | 205 | | 2 | 3 | 249 | | 3 | 3 | 249 | | 4 | 3 | 279 | | 4 | 3 | 321 | | 5 | 3 | 279 | | 5 | 3 | 321 | | 6 | 3 | 321 | | 6 | 3 | 386 | | 7 | 3 | 321 | | 7 | 3 | 386 | | 8 | 3 | 249 | | 18 | 3 | 205 | | 1 | 205 | 3 | | 1 | 205 | 3 | | 18 | 205 | 3 | | 18 | 205 | 3 | | 2 | 224 | 3 | | 2 | 224 | 3 | | 2 | 224 | 249 | | 2 | 249 | 3 | | 2 | 249 | 3 | | 3 | 249 | 3 | | 3 | 249 | 3 | | 8 | 249 | 3 | | 8 | 249 | 3 | | 4 | 279 | 3 | | 4 | 279 | 3 | | 4 | 279 | 321 | | 5 | 279 | 3 | | 5 | 279 | 3 | | 5 | 279 | 321 | | 4 | 321 | 3 | | 4 | 321 | 3 | | 4 | 321 | 279 | | 5 | 321 | 3 | +-----+-----+------+

CL 的回答是正确的,我设法重做我的数据库以寻找词汇(不同引理共享的基本形式),并以这种方式使用 CL 提出的内容。这让我可以避免:

    WHERE ltc2.lemId in (21, 22)

而是拥有

    WHERE ltc2.vocabId = 11

我最终做的是在执行 CL 提出的复杂查询之前,首先使用单独的查询(在 Python 中!!!)查找 vocabId。此外,这个查询每增加一个查询词就会增加大约十几行。但仍然:这样它变得非常快。

如果可以的话,还有一个后续问题:事实上,即使是一个

    WHERE ltc2.lemId in (21)

比a慢很多

    WHERE ltc2.lemId = 21

让我想知道:这是错误还是功能?

更准确地说:您是否认为任何数据库系统都会出现同样的性能下降,或者这是否是 Sqlite 特有的?

【问题讨论】:

示例数据的期望输出是什么? 所需的输出只是满足条件的元组 (cId, bId0, bId1)。我有数千个结果,一次只需要 10 个。 - 抱歉,我编辑了您的评论,而不是发表我自己的评论:-( 哪 10 个?如果有 20 个不同的 cIds,结果会不会只有 10 个随机的 bId 值? 是的,它可以是任何随机的 bId 值,只要 bId0 和 bId1(以及 bId2 等)都不同(并且 cId 以指示的迭代顺序到达)。 示例数据不完整。添加一些带有lemId = 20 的行,以及具有所需结果的表。 【参考方案1】:

以下查询返回bIds 每个cId 的一个组合:

SELECT cId,
       bId AS bId19,
       (SELECT min(bId)
        FROM ltc AS ltc1
        WHERE ltc1.lemId = 20
          AND ltc1.cId = ltc0.cId
          AND ltc1.bId != ltc0.bId
       ) AS bId20
FROM ltc AS ltc0
WHERE lemId = 19
  AND bId20 IS NOT NULL
ORDER BY bId19, cId

使用子查询而不是连接是确保只计算一个组合的最简单方法。

不可能从同一个SELECT子句中的另一个表达式中引用SELECT子句中的表达式,所以如果你有三个或更多bIds,你需要引入更多的子查询:

SELECT cId,
       bId19,
       bId20,
       (SELECT min(bId)
        FROM ltc AS ltc2
        WHERE ltc2.lemId = 21
          AND ltc2.cId = ltc0.cId
          AND ltc2.bId != ltc0.bId19  -- and/or bId20?
       ) AS bId21
FROM (SELECT cId,
             bId AS bId19,
             (SELECT min(bId)
              FROM ltc AS ltc1
              WHERE ltc1.lemId = 20
                AND ltc1.cId = ltc0.cId
                AND ltc1.bId != ltc0.bId
             ) AS bId20
      FROM ltc AS ltc0
      WHERE lemId = 19) AS ltc0
WHERE bId20 IS NOT NULL
  AND bId21 IS NOT NULL
ORDER BY bId19, cId

使用(lemId, bId, cId) 上的索引,不需要排序。 (lemId, cId, bId) 上的索引会加快子查询的查找速度,但速度不会非常快。


至于返回所有组合:我认为没有任何机制可以比预先计算的nr 更有效地实现“混合”。

任何查询都必须首先在lemId 上进行查找来搜索行,因此为了能够使用索引进行排序,您需要(lemId, nr) 上的索引。但是,这仅允许在 one nr 列上加速排序,并且仅在 SQLite 3.8.5 或更高版本中使用 partial sorting support 加速排序,因此这可能对您来说不够快,也可能不够快。 (只需将原始查询与多个连接一起使用。)

ltc0.lemId 上的查找使用多个 ID (lemId IN (1,2)) 或子查询 (lemId IN (SELECT ...)) 时,根本不可能按 nr 进行索引排序。 由于返回的行数较多,排序会很慢。

【讨论】:

答案在我可以验证的每一点上都是完全正确的(除了 3.8.5 版本部分),但到目前为止我未能将其概括为 3 个 lemIds:我需要比较 ltc0.bId!= ltc2.bId 并且由于子查询只能返回一个值,因此在搜索 n lemIds 的一般情况下,我无法使用这个想法。对此有什么想法吗?谢谢!!!

以上是关于从 Sqlite 表中选择行的元组并有效地对元组进行排序的主要内容,如果未能解决你的问题,请参考以下文章

在元组的ndarray中查找元组并返回搜索到的元组的索引

如何为每个循环发出不同的元组并在风暴螺栓的单个字段方法中声明?

拆分两个元素的元组并添加到熊猫数据框[重复]

遍历 pandas 数据框中的行并匹配列表中的元组并创建一个新的 df 列

根据元组的值对元组列表中的重复元组进行平均

如何从python中的列表中删除重复的元组?