SELECT *,MIN(x)中的隐式排序如何在SQLite中工作?

时间:2016-12-10 20:02:43

标签: sql sqlite

今天,我有一个非常普遍的问题,那就是选择由group by分割的每组数据集中具有最小值的行。我找到了一个SQLite独有的解决方案(它在MySQL中运行不正确并在PostgreSQL中抛出错误)并且不使用任何连接。它看起来像这样:

SELECT *, min(x) FROM table GROUP BY y

Here是一个例子。

但是,我不明白为什么这是有效的 - 只是通过包含一个聚合函数,每个组都以某种方式隐式排序并返回聚合函数结果所对应的行。默认SQL行为是选择任意行。我挖掘relevant SQLite documentation并没有找到解释。这就是我想要的解释。

编辑:到目前为止两个答案都猜测这是巧合。它不是。在实际的表格中,我用这种方法将约90个记录分成~30个组,并且每个记录都按预期工作。 See for yourself

3 个答案:

答案 0 :(得分:3)

为了与MySQL兼容,SQLite允许使用既不聚合也不按分组分组的列。

MySQL 保证值不是来自任何特定行,而且版本3.7.11之前的SQLite也没有。但是,由于在SQLite中实现分组的方式,这些列中的值恰好来自与某些情况下匹配min()/ max()的行。

有些paying客户认为这很有用,并希望得到此保证,因此SQLite enforced it in all casesdocumented it in the changelog of version 3.7.11会使其成为受支持的功能(即,已经过测试,永远不会被删除。)

虽然使用安全,但这种行为是SQL标准的违反扩展,从未正确设计,并且从未打算成为销售功能,因此在实际中并未提及文档。

答案 1 :(得分:0)

它可能是偶然的。 SQLite将为每个组返回一个任意行。该行不一定必须具有该组的最小x值。

学会正确表达查询:

SELECT t.*
FROM table t
WHERE t.x = (SELECT MIN(t2.x) FROM table t2 WHERE t2.y = t.y)

答案 2 :(得分:0)

您看到的记录是任意选择的 你不能指望看起来像你这样的行为 它可以由于表结构的变化(例如添加/删除的索引),版本之间等而改变。

https://www.sqlite.org/lang_select.html

  

如果SELECT语句是带有GROUP BY子句的聚合查询   ...
  然后,对每个表达式评估结果集中的每个表达式一次   一排排。如果表达式是聚合表达式,则为   在组中的所有行上进行评估。否则,它被评估   针对组内任意选择的一行。如果   结果集中有多个非聚合表达式,   然后对同一行评估所有这些表达式。

这让我想起了与Oracle的GROUP BY相关的一个着名陷阱 每个人只知道如果您使用GROUP BY,您可以跳过ORDER BY,因为结果集已经订购。
结果集在那时被排序的原因是Oracle使用基于排序的算法来实现组 在版本10gR2中,Oracle添加了基于 HASH 的附加GROUP BY算法 你可以猜到故事的其余部分。