【问题标题】:Sqlite: How to speed up those Sqlite queries?Sqlite:如何加快那些 Sqlite 查询?
【发布时间】:2016-10-14 14:14:17
【问题描述】:

鉴于我的本地 Sqlite 数据库中有一个大表(大约 5'000'000 行),其架构如下

id(整数,唯一),例如6543

description(文本),例如"any meaningful text"

tags(文本),例如"(living)(home)(garden)"

所以如果我需要计算所有标记为“家”和“花园”的条目,我会这样做

SELECT count(id) FROM tbl WHERE tags LIKE ‘%(home)%' AND tags LIKE '%(garden)%'

显然,这需要很长时间。 (我用 2'000'000 个条目的表格版本对其进行了测试,耗时 93 秒......)

引入新列是否明智,每个标签一个(最多五个)?

所以我会有类似的列

id | description | tag1 | tag2 | tag3 | tag4 | tag5 34 | blahblah | home |garden| null | null | null

相应的查询看起来像

SELECT count(id) FROM tbl WHERE (tag1 = 'home' OR tag2 = 'home' OR tag3 = 'home' OR tag4 = 'home' OR tag5 = 'home') AND (tag1 = 'garden' OR tag2 = 'garden' OR tag3 = 'garden' OR tag4 = 'garden' OR tag5 = 'garden')

这真的更好吗?

我只是不知道什么是最好的。我确定有办法减少查询时间……但我不知道。

任何帮助将不胜感激!

或者,也许,您有一个全新的想法,如何重组数据和/或查询以使其更快。

提前致谢!

【问题讨论】:

  • 在我看来,您正在建模标签和 tbl 之间的 M-N 关系。您可以将这些标签放在一个表格中:CREATE TABLE tbl_tags (tbl_id INTEGER REFERENCES tbl(id), tag TEXT)。或者,如果有一组固定的标签,将它们放在一个表标签中并从 tbl_tags 中引用它。您的第二种方法,使用五个“标签”字段,应该比您的第一种方法快得多。如果 SQLite 找到匹配的标签,它将不会继续评估。你确定你的应用不会有超过 5 个标签吗?
  • 如果你为你的标签创建一个表,你可以加入那些:SELECT count(id) FROM tbl INNER JOIN tbl_tags ON tags.tbl_id = tbl.id AND (tags.tag = 'home' OR ...)

标签: sql performance sqlite


【解决方案1】:

在您使用条件...<field> LIKE '%<sub-str>%' 的那一刻,您正在触发全表扫描,因此需要很长时间。尽管如此,对于具有 64 个字符的字符串(字段tags)的 2M 记录,在我看来,93 秒的时间太长了。我怀疑问题不是来自全面扫描。

我建议您检查以下查询所需的时间:

select count(*)
  from <tablename>
 where tags like '(home)%' ;

对于 2M 行,这不会超过几秒钟。如果是这样,我会在其他地方搜索性能问题。

此外,您可以存储|living|home|garder|... 之类的标签并搜索tags like '%|home|%' [这不会解决您的问题,但是您可以节省一些空间,因为您将)( 替换为|]。

编辑:

我刚刚意识到您正在使用两个条件(WHERE ... AND)。请尝试以下操作:

SELECT count(id)
  FROM tbl
 WHERE tags like '%(home)%(garden)%'
    OR tags like '%(garden)%(home)%' ;

【讨论】:

  • 其实你建议的查询取了359468ms。是的,6分钟。所以很明显,问题似乎是别的。我只是不知道可能出了什么问题。我不是内存不足,只是检查了一下。数据库中没有其他表。我还能检查或做什么? :(
  • 我建议的两个查询中哪一个花了 6 分钟?如果是第一个 (... where tags like '(home)%'),那么您需要分析您的数据库,例如在查询执行时监控资源(CPU、内存 IO 等)。
  • 是的,第一个简单的查询。我检查了 CPU(80% 空闲)和内存使用情况(4GB 空闲,Sqlite 只使用了大约 200MB)。我刚刚找到了 PRAGMA 设置,增加 cache_size 最终增加了内存使用量,从而减少了调用时间。第一次运行仍然需要一段时间,但随后的查询现在执行得更快(大约 5 次,大约)。无论如何,我仍然想改进表格设置,所以我会尽快尝试将标签添加到表格建议中。
  • 这非常奇怪。看?与...'%(home)%' 相比,... where tags like '(home)%' 的检查条件要简单得多。我的猜测是查询不应超过 10 秒。你的数据库有问题...
【解决方案2】:

当您有多个相同事物的实例时,您应该将它放在多行中。 在这种情况下,这将为标签生成一个单独的表:

CREATE TABLE tags (
    id INTEGER  REFERENCES tbl(id),
    tag TEXT
);

CREATE INDEX tags_index ON tags(tag);

然后,您可以通过在 tag 列上进行简单查找来进行查询,因为有索引,这很有效:

SELECT count(*)
FROM tbl
WHERE id IN (SELECT id FROM tags WHERE tag = 'home')
  AND id IN (SELECT id FROM tags WHERE tag = 'garden');

或者,使用compound query:

SELECT count(*)
FROM (SELECT id FROM tags WHERE tag = 'home'
      INTERSECT
      SELECT id FROM tags WHERE tag = 'garden');

【讨论】:

  • 我知道我需要一个单独的带有 id 和标签的 tags-table。我不明白这是如何链接到tbl-table :( 如何用() 中的每个标签填充标签表?标签ID 是随机数还是以某种方式链接到 tbl 中的条目? 我调用了create table & create index 语句,但显然查询不能开箱即用,因为标签表是空的。实际上我对sql的了解都是自学的和初级的。我知道的不多不仅仅是简单的选择语句。请告知如何继续链接两个表之间的数据,我一无所知。
  • id 指的是父表;它不是tags 表的主键。
猜你喜欢
  • 2011-05-03
  • 2013-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多