【问题标题】:Increase performance of SELECT COUNT(*) with WHERE clause in SQLite?在 SQLite 中使用 WHERE 子句提高 SELECT COUNT(*) 的性能?
【发布时间】:2021-05-16 09:46:52
【问题描述】:

我正在尝试构建一个我正在使用此查询的排行榜:

SELECT id, COUNT(type) FROM Data 
WHERE UPPER(type) = $1 AND id != '123123123123' 
GROUP BY id ORDER BY COUNT(type) DESC LIMIT 10;

在我的表Data 中大约需要 8-10 秒,其中大约需要 8-10 秒。 400 万行,并且每天增长 100,000 多行。

该表还有一个 UNIQUE INDEX,当组合来自 2 个不同列(code 和 version)的值时,它是唯一的。

我可以做些什么来提高这个查询的速度和性能?

我考虑过为每个用户的行构建一个计数器,但这不是很直观,因为 type 可以在 100 多个值之间有所不同。我还尝试在 id 和 type 上添加一个索引,如果不是更多的话,这仍然需要 8-10 秒。

【问题讨论】:

  • 这张表有索引吗?
  • 是的,让我用这些信息更新问题。
  • 你需要一个id, type的索引

标签: sql database performance sqlite


【解决方案1】:

有许多选项具有不同的性能优势和权衡。

但所有工作都通过维护type by id 列的每个值的计数的第二个汇总表来工作。 (基本上,您将要提高性能的查询输出存储在数据库的表中,这是供以后使用简单快速的查询使用)

因此,由于您没有确切说明如何创建您正在使用的表,因此我不得不从您的查询中推断出示例

所以对于Data 表的创建者

CREATE TABLE "Data" (
    "id"    INTEGER NOT NULL,
    "type"  TEXT
)

您将创建第二个表来存储计数

CREATE TABLE "Data_summary" (
    "type"  TEXT NOT NULL,
    "data_id" INTEGER NOT NULL,
    "total_count"   INTEGER,
    UNIQUE ("type", "data_id")
)

(当您按 id 分组时,它必须是唯一约束以及 type 的一部分)

您想要加速的查询将是

SELECT * FROM Data_summary WHERE data_id != 123123123123
    ORDER BY total_count DESC LIMIT 10 

都有存储空间成本与查询速度的权衡,加上维护汇总表需要额外的工作,下面将详细介绍各种策略。
type 和 id 列的内容有多少不同的值并不重要,这只会使 Data_summary 表更大以便存储

选项:
1) 按照@stephenmwyatt22-w 的建议,使用Trigger 更新汇总表
(但我在回答一个工作示例时给出了实际细节)。

所以你会创建一个类似的触发器:-

CREATE TRIGGER update_summary AFTER INSERT ON Data
BEGIN
    INSERT INTO Data_summary (type, data_id, total_count)
    -- If Insert is success this is a new value of type so count is 1
    VALUES (NEW.type, NEW.id, 1)
    -- If value of type/id combo exists then there will be a conflict (this requires sqlite version 3.24.0 or later)
    ON CONFLICT(type, data_id)
    DO UPDATE SET total_count=total_count+1
        WHERE type == NEW.type AND data_id == NEW.id;
END;

所以每次插入Data 时,都会自动对Data_summary 表进行第二次插入/更新(如果在将total_count 设置为为 1 否则插入时有一个 CONFLICT,因为它被设置为 UNIQUE 组合,因此使用 UPSERT 进程插入 UPDATE total_count + 1

这种方法的一个缺点是,如果您已经拥有包含数据的Data 表,那么您将使用代码中的一些“更新”sql 语句“引导”Data_summary 表(类似于@987654344 @) 在TRIGGER 接管维护Data_summary 表之前统计现有数据。

更新:
Online Demo

2) 您可以进行“批量”更新,而不是使用触发器来更新Data_summary 表。为此,您还需要有一个自动递增的唯一索引列整数或类似 DATE+TIME 列的东西来维护一个指向 Data_summary 最后更新到的位置的指针(这个“最后更新到”的指针需要存储在第三张表之类的地方)

当您想要对 Data_summary 表进行“更新”时,您可以像原来一样运行查询,但要使用额外的 WHERE 子句将查询限制为仅在您拥有的 Data 表中的“新”行上尚未在 Data_summary 表中进行汇总。
这将使用类似的ON CONFLICT 来插入新的id 和type 组合,或者更新现有组合的数量(如果它们已经存在)。

批量更新可以作为后台任务完成,也可以在需要结果之前完成,因为昂贵的查询只会针对“新”数据加上简单查询。

我不会给出一个例子,因为所有的 SQL 都与选项 1) 的例子非常相似。

【讨论】:

    【解决方案2】:

    在该列上保持运行总计,使用插入触发器

    Trigger for updating total records on both insert and delete

    【讨论】:

    • 您所说的“在该列上保持累计”是什么意思?
    • 当表更新时(你需要新总计的原因),更新其他表中的 TOTAL 列,然后当你需要该总计时,只需在 table_count.totval 或要保留的任何表上选择 *运行总计
    • 别迷路了,我会为你做的,发送详细信息
    • 我不确定这是否可行,因为类型可以在很多值上有所不同。我不需要为每个类型的值创建一个列吗?
    猜你喜欢
    • 1970-01-01
    • 2013-05-21
    • 1970-01-01
    • 2011-10-26
    • 2014-06-11
    • 1970-01-01
    • 1970-01-01
    • 2014-12-27
    相关资源
    最近更新 更多