关于sql:为什么PostgreSQL在一个表上分组并选择另一个表时不接受

Why PostgreSQL is not accepting while group by on one table and selecting towards another tables

本问题已经有最佳答案,请猛点这里访问。

我正在使用postgreSQL版本
在x86_64-unknown-linux-gnu上的PostgreSQL 9.1.9,由gcc编译(Ubuntu / Linaro 4.7.2-22ubuntu5)4.7.2,64位,我的问题是加入两个表,我们将其命名为temp1和temp2, 在这里我需要加入这两张桌子

表结构是

1
2
3
4
5
6
7
marks_map
marks   INT
stud_id  INT

student
stud_id INT
class_id INT

在这里我的查询

1
2
3
SELECT class_id,stud_id,COUNT(marks)
FROM student AS s
INNER JOIN marks_map AS m ON (s.stud_id=m.stud_id) GROUP BY stud_id

我在这里得到错误

1
ERROR:  COLUMN"s.class_id" must appear IN the GROUP BY clause OR be used IN an aggregate FUNCTION

为什么会发生这种错误? 如果我在group by中使用class_id,它就会成功运行。


您必须将class_id属性添加到group by子句中,因为在语句的select部分中,此属性上没有聚合函数。

在GROUP BY语句中,您必须添加GROUP BY子句之后尚未聚合的所有属性。

例如:

1
2
3
4
5
6
SELECT
non-aggregating-attr-1, non-aggregating-attr2, non-aggregating-attr3, SUM(attr4)
FROM
TABLE
GROUP BY
non-aggregating-attr-1, non-aggregating-attr2, non-aggregating-attr3


您应该能够理解一个甚至不涉及JOIN的简化案例的问题。

查询SELECT x,[other columns] GROUP BY x表示对于x的每个不同值,必须输出[其他列],每个x只有一行。

现在看一个简化的例子,其中student表有两个条目:

stud_id=1, class_id=1
stud_id=1, class_id=2

我们要求SELECT stud_id,class_id FROM student GROUP BY class_id

stud_id只有一个不同的值,即1。

所以我们告诉SQL引擎,给我一行stud_id=1以及随附的class_id值。问题在于没有一个,而是两个这样的值,1和2.那么选择哪一个? SQL引擎不是随机选择,而是首先产生一个错误,说这个问题在概念上是假的,因为没有规则说stud_id的每个不同值都有自己的对应class_id

另一方面,如果非GROUP'ed输出列是将一系列值转换为一个值的聚合函数,如minmaxcount,那么它们提供缺少的规则,说明如何从几个中只获得一个值。这就是SQL引擎可以使用的原因,例如:SELECT stud_id,count(class_id) FROM student GROUP BY stud_id;

此外,当遇到错误列"somecolumn"必须出现在GROUP BY子句中时,您不希望只是将列添加到group by,直到错误消失为止,就好像它纯粹是语法问题一样。这是一个语义问题,添加到GROUP BY的每个列都会改变提交给SQL引擎的问题的意义。

也就是说,GROUP BY x,y表示(x,y)对的每个不同值。它并不意味着GROUP BY x,嘿,因为它会导致错误,让我们也投入y


这就是group by的工作方式。

您可以检查您的数据

1
2
3
4
5
6
SELECT
    array_agg(class_id) AS arr_class_id,
    stud_id, COUNT(marks)
FROM student AS s
   INNER JOIN marks_map AS m ON (s.stud_id=m.stud_id)
GROUP BY stud_id

并查看每组的class_id数量。有时你的class_id依赖于stud_id(每个组只有一个elemnet数组),所以你可以使用虚拟聚合,如:

1
2
3
4
5
6
SELECT
    MAX(class_id) AS class_id,
    stud_id, COUNT(marks)
FROM student AS s
   INNER JOIN marks_map AS m ON (s.stud_id=m.stud_id)
GROUP BY stud_id