SQL GROUP BY是一个设计缺陷吗?

时间:2010-02-22 13:14:28

标签: sql

为什么SQL要求我指定要分组的属性?为什么不能只使用所有非聚合?

如果某个属性未聚合且不在 GROUP BY 子句中,那么非确定性选择将是唯一的选项,假设元组是无序的(mysql类似于此)并且这是一个巨大的问题。据我所知,Postgresql要求所有未出现在 GROUP BY 中的属性必须进行聚合,这强化了它是多余的。

  • 我是否遗漏了某些内容,或者这是一个语言设计缺陷,促使松散的实现,并使查询更难写?
  • 如果我遗漏了某些内容,那么无法推断出组属性的示例查询是什么?

9 个答案:

答案 0 :(得分:11)

您不必按照您选择的完全相同的内容进行分组,例如:

SQL:select priority,count(*) from rule_class
group by priority

PRIORITY COUNT(*) 70 1 50 4 30 1 90 2 10 4

SQL:select decode(priority,50,'Norm','Odd'),count(*) from rule_class group by priority

DECO COUNT(*) Odd 1 Norm 4 Odd 1 Odd 2 Odd 4

SQL:select decode(priority,50,'Norm','Odd'),count(*) from rule_class group by decode(priority,50,'Norm','Odd')

DECO COUNT(*) Norm 4 Odd 8

答案 1 :(得分:6)

还有一个原因为什么SQL要求我指定要分组的属性。

让我们坐下来,我们有两个简单的表: friend car ,我们存储有关我们的朋友及其汽车的信息。

让我们说我们想要显示我们所有朋友的数据(来自表friend)以及我们所有朋友,他们现在拥有多少辆汽车,已经售出,已经崩溃和总数。哦,我们希望长老先行,最后年轻。

我们会做类似的事情:

SELECT f.id
     , f.firstname
     , f.lastname
     , f.birthdate
     , COUNT(NOT c.sold AND NOT c.crashed) AS owned
     , COUNT(c.sold) AS sold
     , COUNT(c.crashed) AS crashed
     , COUNT(c.friendid) AS totalcars
FROM friend f
LEFT JOIN car c     <--to catch (shame!) those friends who have never had a car 
  ON f.id = c.friendid
GROUP BY f.id
       , f.firstname
       , f.lastname
       , f.birthdate
ORDER BY f.birthdate DESC

但我们真的需要GROUP BY中的所有字段吗?是不是每个朋友都由他的id唯一确定?换句话说,firstname, lastname and birthdate上的f.id功能不依赖吗?为什么不这样做(就像我们在MySQL中一样):

SELECT f.id
     , f.firstname
     , f.lastname
     , f.birthdate
     , COUNT(NOT c.sold AND NOT c.crashed) AS owned
     , COUNT(c.sold) AS sold
     , COUNT(c.crashed) AS crashed
     , COUNT(c.friendid) AS totalcars
FROM friend f
LEFT JOIN car c     <--to catch (shame!) those friends who have never had a car 
  ON f.id = c.friendid
GROUP BY f.id
ORDER BY f.birthdate 

如果SELECT(加ORDER BY)部分中有20个字段,该怎么办?第二个查询不是更短,更清晰,可能更快(在接受它的RDBMS中)吗?

我说是的。那么,如果这篇文章是正确的,那么SQL 1999和2003规范是否会说:Debunking group by myths

答案 2 :(得分:3)

我想说如果group by子句中有大量项目,那么核心信息可能会被拉出到你内部加入的表格子查询中。

可能会有性能损失,但它会产生更整洁的代码。

select  id, count(a), b, c, d
from    table
group by
        id, b, c, d

变为

select  id, myCount, b, c, d
from    table t
        inner join (
            select id, count(*) as myCount
            from table
            group by id
        ) as myCountTable on myCountTable.id = t.id

那就是说,我有兴趣听到这样做的反驳论据,而不是有一个大的group by子句。

答案 3 :(得分:3)

我同意其详细说明,按列表分组不应与非聚合选择列隐式相同。在Sas中,数据聚合操作更简洁。

另外:很难想出一个例子,在组列表中有一个比列表更长的列列表是有用的。我能想到的最好的是......

create table people
(  Nam char(10)
  ,Adr char(10)
)

insert into people values ('Peter', 'Tibet')
insert into people values ('Peter', 'OZ')
insert into people values ('Peter', 'OZ')

insert into people values ('Joe', 'NY')
insert into people values ('Joe', 'Texas')
insert into people values ('Joe', 'France')

-- Give me people where there is a duplicate address record

select * from people where nam in 
(
select nam              
from People        
group by nam, adr        -- group list different from select list
having count(*) > 1
)

答案 4 :(得分:2)

如果您只是为了更简单的方式来编写脚本。 这是一个提示:

在MS SQL MGMS中,您可以在文本中查询select * from my_table 之后选择文本右键单击“编辑器中的设计查询”。 Sql studio将打开新编辑器并提交所有字段,然后再次右键单击并选择“Add Gruop BY” Sql MGM studio将为您添加代码。

我资助这种方法对插入语句非常有用。当我需要编写脚本以在表中插入很多字段时,我只需从table_where_want_to_insert中选择*,然后更改要插入的select语句类型,

答案 5 :(得分:2)

我同意

我完全同意这个问题。我问同一个人here

老实说,我认为这是一种语言缺陷。

我意识到有反对的论据,但我还没有使用GROUP BY子句,其中包含现实世界中SELECT子句中所有非聚合字段以外的任何内容。

答案 6 :(得分:1)

答案 7 :(得分:1)

我认为更有可能是语言设计选择决策是明确的,而不是隐含的。例如,如果我希望以与输出列不同的顺序对数据进行分组,该怎么办?或者,如果我想按所选列中未包含的列进行分组?或者,如果我只想输出分组列而不使用聚合函数?只有明确说明我在group by子句中的偏好是我的意图。

你还必须记住,SQL是一种非常古老的语言(1970)。看看Linq如何翻转所有内容以使Intellisense工作 - 现在看起来很明显,但SQL早于IDE,因此无法考虑这些问题。

答案 8 :(得分:0)

“superflous”属性会影响结果的排序。

考虑:

create table gb (
  a number,
  b varchar(3),
  c varchar(3)
);

insert into gb values (   3, 'foo', 'foo');
insert into gb values (   1, 'foo', 'foo');
insert into gb values (   0, 'foo', 'foo');

insert into gb values (  20, 'foo', 'bar');
insert into gb values (  11, 'foo', 'bar');
insert into gb values (  13, 'foo', 'bar');

insert into gb values ( 170, 'bar', 'foo');
insert into gb values ( 144, 'bar', 'foo');
insert into gb values ( 130, 'bar', 'foo');

insert into gb values (2002, 'bar', 'bar');
insert into gb values (1111, 'bar', 'bar');
insert into gb values (1331, 'bar', 'bar');

本声明

select sum(a), b, c
  from gb
group by b, c;

结果

    44 foo bar
   444 bar foo
     4 foo foo
  4444 bar bar

而这一个

select sum(a), b, c
  from gb
group by c, b;

结果

   444 bar foo
    44 foo bar
     4 foo foo
  4444 bar bar