【问题标题】:Need Help Speeding up an Aggregate SQLite Query需要帮助加快聚合 SQLite 查询
【发布时间】:2012-12-09 19:31:11
【问题描述】:

我有一个如下定义的表...

CREATE table actions (
  id INTEGER PRIMARY KEY AUTO_INCREMENT,
  end BOOLEAN,
  type VARCHAR(15) NOT NULL,
  subtype_a VARCHAR(15),
  subtype_b VARCHAR(15),
);

我正在尝试查询在每个唯一的 (subtype_a, subtype_b) 对上发生的某种类型的最后一个结束操作,类似于 group by(除了 SQLite 没有说 what 行是有保证的由一组返回)。

在大约 1MB 的 SQLite 数据库上,我现在的查询可能需要两秒以上,但我需要加快速度,使其不到一秒(因为这将被频繁调用) .

示例查询:

SELECT * FROM actions a_out 
WHERE id = 
  (SELECT MAX(a_in.id) FROM actions a_in 
   WHERE a_out.subtype_a = a_in.subtype_a 
     AND a_out.subtype_b = a_in.subtype_b 
     AND a_in.status IS NOT NULL 
     AND a_in.type = "some_type");

如果有帮助,我知道(subtype_a,subtype_b) 的所有独特可能性

例如:

(a,1)
(a,2)
(b,3)
(b,4)
(b,5)
(b,6)

【问题讨论】:

  • 我添加了greatest-n-per-group 标签,由于标签限制为五个,我不得不替换您列出的标签之一。我替换了sqlite,因为您的表定义是特定于 MySQL 的。

标签: mysql performance aggregate-functions greatest-n-per-group


【解决方案1】:

从版本 3.7.11 开始,SQLite guarantees 在组中返回哪条记录:

查询形式:“SELECT max(x), y FROM table”返回包含最大 x 值的同一行上的 y 值。

所以 可以以更简单的方式实现:

SELECT *, max(id)
FROM actions
WHERE type = 'some_type'
GROUP BY subtype_a, subtype_b

【讨论】:

  • 如果你运行SELECT *, MAX(id), MIN(id) ...会怎样?
  • 这对我有用,谢谢;我确实意识到我有 sqlite 3.7.15。我的查询现在在大型数据集上快了 3 秒以上!
【解决方案2】:

这样更快吗?

select * from actions where id in (select  max(id) from actions where type="some_type" group by subtype_a, subtype_b);

【讨论】:

  • 不,请参阅我在关于 SQLite 和 GROUP BY 的问题中发布的内容。
  • 我知道,但是这个查询应该在 sqlite 中工作,因为您明确说明了您想要从组中获得哪一个。无论如何,我做了一个快速的配置文件并且速度不快,Bill Karwin 的回答很棒。
【解决方案3】:

这是 * 上经常出现的 greatest-in-per-group 问题。

我是这样解决的:

SELECT a_out.* FROM actions a_out
LEFT OUTER JOIN actions a_in ON a_out.subtype_a = a_in.subtype_a 
    AND a_out.subtype_b = a_in.subtype_b
    AND a_out.id < a_in.id
WHERE a_out.type = "some type" AND a_in.id IS NULL

如果您在 (type, subtype_a, subtype_b, id) 上有一个索引,这应该会运行得非常快。


另请参阅我对类似 SQL 问题的回答:

或者 Jan Kneschke 的这篇精彩文章:Groupwise Max

【讨论】:

  • 这不会为每组 subtype_a,subtype_b 返回最新添加的(最高自动增量 id)
  • @palako,我的错误,我把不等式倒过来了。我已编辑以更正它。它应该尝试匹配 a_in 中更大的 id 值,如果没有找到,则 a_out 必须具有相应组的最大 id。
  • 没问题。这实际上非常快。
  • @BillKarwin,我会尽快尝试一下,谢谢。你能解释一下a_in.id IS NULL 部分吗?
  • 由于某种原因,此查询为我返回 0 行。