sql:在group by语句中包含未分组列的合法性

时间:2014-06-17 06:29:30

标签: sql sqlite

在SQLite中,如果我这样做:

CREATE TABLE fraction (
  id Int,
  tag Int,
  num Int,
  den Int,
  PRIMARY KEY (id)
);

INSERT INTO fraction VALUES (1,1,3,4);
INSERT INTO fraction VALUES (2,1,5,6);
INSERT INTO fraction VALUES (3,2,3,8);
INSERT INTO fraction VALUES (4,2,5,7);
INSERT INTO fraction VALUES (5,1,10,13);
INSERT INTO fraction VALUES (6,2,5,7);

SELECT fraction.tag, max(1.0 * fraction.num / fraction.den)
FROM fraction
GROUP BY fraction.tag;

我会得到结果:

1|0.833333333333333
2|0.714285714285714

然后,如果我发出:

SELECT fraction.tag, max(1.0 * fraction.num / fraction.den),
  fraction.num, fraction.den
FROM fraction
GROUP BY fraction.tag;

我会得到结果:

1|0.833333333333333|5|6
2|0.714285714285714|5|7

后者是我所期待的,但它似乎比任何可预测或可靠的事故更幸福。例如,如果汇总函数sum而不是min,某些类型的“rider”列就没有意义了。

在我正在做的一个当前项目中,我正在使用一个连接自己的表来模拟后者:

SELECT DISTINCT fraction_a.tag, fraction_a.high,
  fraction_b.num, fraction_b.den
FROM
  (SELECT fraction.tag, max(1.0 * fraction.num / fraction.den) AS high
    FROM fraction
    GROUP BY fraction.tag)
  AS fraction_a JOIN
  (SELECT fraction.tag, fraction.num, fraction.den
    FROM fraction)
  AS fraction_b
  ON fraction_a.tag = fraction_b.tag
    AND fraction_a.high = 1.0 * fraction_b.num / fraction_b.den;

产生

1|0.833333333333333|5|6
2|0.714285714285714|5|7

但我发现这种语法难看,不切实际且难以维护。

由于我将在几种SQL方言之间移植我的项目,我需要一种在所有方言中都可靠的解决方案。所以,如果我必须咬紧牙关并使用我将要的丑陋语法,但我更喜欢使用更清洁的语法。

2 个答案:

答案 0 :(得分:1)

SELECT子句中包含未出现在GROUP BY子句中的非聚合列是不可移植的,可能会导致错误/意外结果。您使用的语法不是 cleaner - 这是完全错误的,并且恰好在SQLite上工作。它不适用于Oracle(导致语法错误),它在MySQL上不会按预期工作(它将从组中返回随机值),并且它可能不适用于其他RDBMS。

实现这一目标最直接的方法是使用窗口函数 - 但由于你需要支持SQLite,这是不可能的。

请注意,如果您碰巧有几个最大值,那么您的第二种方法(“丑陋”查询)将为每个标记返回多行。这可能是也可能不是你想要的。

所以咬紧牙关并使用类似你的丑陋方法 - 它是可移植的并且可以按预期工作。

答案 1 :(得分:0)

当您使用GROUP BY时,数据库必须从(可能)多个输入行创建单个输出行。

GROUP BY子句中提到的列对于组中的所有行具有相同的值,因此这是要使用的输出值。

具有一些聚合函数的列使用它来计算输出值。

但是,其他列是个问题,因为组中可能存在不同的值。 SQL标准禁止这样做。 MySQL忘记检查此错误,并为输出提供一些随机行的值。 SQLite允许这与MySQL兼容。

从版本3.7.11开始,当您使用MIN或MAX时,SQLite会保证其他列来自具有最小/最大值的记录。