【问题标题】:Is the ordering of a GROUP BY with a MAX aggregate well defined?具有 MAX 聚合的 GROUP BY 的排序是否定义明确?
【发布时间】:2022-01-09 06:06:23
【问题描述】:

假设我在 SQLite 中运行以下命令:

CREATE TABLE my_table
(
     id        INTEGER PRIMARY KEY,
     NAME      VARCHAR(20),
     date      DATE,
     num       INTEGER,
     important VARCHAR(20)
);

INSERT INTO my_table (NAME, date, num, important)
VALUES ('A', '2000-01-01', 10, 'Important 1');

INSERT INTO my_table (NAME, date, num, important)
VALUES ('A', '2000-02-01', 20, 'Important 2');

INSERT INTO my_table (NAME, date, num, important)
VALUES ('A', '1999-12-01', 30, 'Important 3');

表格如下所示:

id NAME date num important
1 A 2000-01-01 10 Important 1
2 A 2000-02-01 20 Important 2
3 A 1999-12-01 30 Important 3

如果我执行:

SELECT id
FROM   my_table
GROUP  BY NAME;

结果是:

+----+
| id |
+----+
| 1  |
+----+

如果我执行:

SELECT id, MAX(date)
FROM   my_table
GROUP  BY NAME;

结果是:

+----+------------+
| id | max(date)  |
+----+------------+
| 2  | 2000-02-01 |
+----+------------+

如果我执行:

SELECT id,
       MAX(date),
       MAX(num)
FROM   my_table
GROUP  BY NAME;

结果是:

+----+------------+----------+
| id | max(date)  | max(num) |
+----+------------+----------+
| 3  | 2000-02-01 | 30       |
+----+------------+----------+

我的问题是,这是明确定义的吗?具体来说,我是否保证在进行第二次查询时总是得到id = 2(使用单个Max(date) 聚合),或者这只是SQLite 如何在分组之前订购表以获取Max 的副作用?

我问这个是因为我特别想要id = 2。然后,我将执行另一个查询,为该行选择important 字段(对于我的实际问题,第一个查询将返回多个ids,我将一次为所有这些行选择所有important 字段。

此外,这一切都发生在 iOS 核心数据查询中,因此我无法执行更复杂的子查询。如果我知道 GROUP BY 的顺序是由聚合定义的,那么我会很有信心我的查询不会中断(直到 Apple 不再使用 SQLite for Core Data)。

谢谢!

【问题讨论】:

  • 所有这些查询都在 SQLite 中工作(在命令行上)。

标签: sql sqlite core-data


【解决方案1】:

来自 Sqlite 手册

2.5。聚合查询中的裸列

通常的情况是聚合查询中的所有列名要么是聚合函数的参数,要么出现在 GROUP BY 子句中。包含不在聚合函数内且未出现在 GROUP BY 子句(如果存在)中的列名的结果列称为“裸”列。示例:

SELECT a, b, sum(c) FROM tab1 GROUP BY a;

在上面的查询中,“a”列是 GROUP BY 子句的一部分,因此输出的每一行都包含“a”的不同值之一。 “c”列包含在 sum() 聚合函数中,因此输出列是“a”具有相同值的行中所有“c”值的总和。但是裸列“b”的结果是什么?答案是“b”结果将是构成聚合的输入行之一中“b”的值。问题是您通常不知道哪个输入行用于计算“b”,因此在许多情况下“b”的值是未定义的。

当聚合函数为 min() 或 max() 时会发生特殊处理。示例:

SELECT a, b, max(c) FROM tab1 GROUP BY a;

在聚合查询中使用 min() 或 max() 聚合函数时,结果集中的所有裸列都从包含最小值或最大值的输入行中获取值。所以在上面的查询中,输出中“b”列的值将是输入行中具有最大“c”值的“b”列的值。如果两个或多个输入行具有相同的最小值或最大值,或者查询包含多个 min() 和/或 max() 聚合函数,则仍然存在歧义。只有内置的 min() 和 max() 函数以这种方式工作。

如果裸列出现在缺少 GROUP BY 子句的聚合查询中,并且输入行数为零,则裸列的值是任意的。例如,在这个查询中:

SELECT count(*), b FROM tab1;

如果 tab1 表不包含任何行(count(*) 的计算结果为 0),那么裸列“b”将具有任意且无意义的值。

大多数其他 SQL 数据库引擎不允许裸列。如果在查询中包含裸列,其他数据库引擎通常会引发错误。在查询中包含裸列的能力是 SQLite 特定的扩展。

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

【讨论】:

  • 谢谢!太棒了!所以它定义明确(对于 SQLite 来说)。
【解决方案2】:

我保证在进行第二次查询时总是得到 id = 2(使用 单个 Max(date) 聚合),或者这只是如何 SQLite 很可能是在分组之前命令表去抓取最大值?

是的,您得到的结果是有保证的,因为它记录在 Bare columns in an aggregate query 中。

您获得的列id 的值来自包含最大值date 的行。

【讨论】:

    猜你喜欢
    • 2021-02-03
    • 2021-02-25
    • 2016-09-27
    • 1970-01-01
    • 1970-01-01
    • 2010-11-15
    • 1970-01-01
    • 1970-01-01
    • 2013-12-03
    相关资源
    最近更新 更多