代码之家  ›  专栏  ›  技术社区  ›  AJ.

为什么SQL强制我在GROUP BY子句中重复SELECT子句中的所有非聚合字段?[关闭]

  •  38
  • AJ.  · 技术社区  · 16 年前

    这件事困扰了我很长时间。

    99%的情况下,GROUP BY子句是SELECT子句的精确副本,减去聚合函数(MAX、SUM等)。
    这违反了“不要重复自己”的原则。

    GROUP BY子句何时可以不包含SELECT子句减去聚合函数的精确副本?

    编辑

    我意识到,有些实现允许您在GROUP BY中使用与SELECT不同的字段(因此是99%,而不是100%),但这肯定是一个非常小的例外吗?
    有人能解释一下,如果你使用不同的字段,应该返回什么吗?

    谢谢。

    10 回复  |  直到 5 年前
        1
  •  17
  •   Ian Varley    16 年前

    我倾向于同意你的观点——这是SQL应该有稍微更智能的默认值以节省我们所有人一些打字的许多情况之一。例如,想象一下,如果这是合法的:

    Select ClientName, InvoiceAmount, Sum(PaymentAmount) Group By *
    

    其中“*”表示“所有非聚合字段”。如果每个人都知道这就是它的工作原理,那么就不会有混乱。如果你想做一些棘手的事情,你可以在一个特定的字段列表中进行细分,但splat的意思是“所有”(在这种情况下,这意味着所有 可能的 )。

    当然,“*”在这里的含义与SELECT子句中的含义不同,所以也许不同的字符会更好:

    Select ClientName, InvoiceAmount, Sum(PaymentAmount) Group By !
    

    在其他一些领域,SQL并不像它所能表达的那样有说服力。但在这一点上,它可能太根深蒂固了,无法做出许多这样的重大改变。

        2
  •  7
  •   Binary Worrier    16 年前

    因为它们是两个不同的东西,所以可以按不在select子句中的项目进行分组

    编辑:

    此外,做出这样的假设安全吗?

    我有一个SQL语句

    Select ClientName, InvAmt, Sum(PayAmt) as PayTot
    

    服务器假设我想按ClientName和InvoiceAmount进行分组,这“正确”吗? 我个人更喜欢(并且认为这更安全)有这段代码

    Select ClientName, InvAmt, Sum(PayAmt) as PayTot
    Group By ClientName
    

    抛出错误,提示我将代码更改为

    Select ClientName, Sum(InvAmt) as InvTot, Sum(PayAmt) as PayTot
    Group By ClientName
    
        3
  •  3
  •   Mike Woodhouse    16 年前

    我希望/期待我们很快会看到更全面的东西;关于这一主题的SQL历史课将是有用和有益的。有人吗?有人吗?布勒?

    与此同时,我可以观察到以下情况:

    SQL早于DRY原则,至少就它在 The Pragmatic Programmer .

    并非所有数据库都需要完整的列表:例如,Sybase会愉快地执行以下查询

    SELECT a, b, COUNT(*)
    FROM some_table
    GROUP BY a
    

    ……这(至少每次我不小心运行了这样一个怪物)通常会导致如此巨大的无意记录集,以至于惊慌失措的请求很快接踵而至,乞求DBA跳出服务器。结果是一种部分笛卡尔积,但我认为这可能主要是Sybase未能正确实现SQL标准。

        4
  •  2
  •   Peter T. LaComb Jr.    16 年前

    也许我们需要一个简写形式——称之为GroupSelect

    GroupSelect Field1, Field2, sum(Field3) From SomeTable Where (X = "3")
    

    这样,如果省略了聚合函数,解析器只需要抛出错误。

        5
  •  2
  •   Peter Lang    14 年前

    这样做的一个很好的理由是,如果你没有指定所有列,你会经常得到不正确的结果。假设你有三列, col1 , col2 col3 .

    假设你的数据如下:

    Col1  Col2 Col3
    a      b    1
    a      c    1
    b      b    2
    a      b    3
    

    select col1, col2, sum(col3) from mytable group by col1, col2
    将给出以下结果:

    Col1  Col2 Col3
    a      b    4
    a      c    1
    b      b    2
    

    它会如何解释
    select col1, col2, sum(col3) from mytable group by col1

    我的猜测是

    Col1  Col2 Col3
    a      b    5
    a      c    5
    b      b    2
    

    这些显然是糟糕的结果。当然,查询越复杂,连接越多,查询返回正确结果的可能性就越小,程序员甚至不太可能知道结果是否正确。

    就我个人而言,我很高兴 group by 需要字段。

        6
  •  2
  •   Milan    13 年前

    我同意GROUP BY ALL、GROUP BY*或类似的说法。正如原始帖子中提到的,在99%(可能更多)的情况下,您希望按所有非聚合列/表达式进行分组。

    然而,这里有一个例子,出于向后兼容性的原因,您需要GROUP BY列。

    SELECT 
      MIN(COUNT(*)) min_same_combination_cnt, 
      MAX(COUNT(*)) max_same_comb_cnt, 
      AVG(COUNT(*)) avg_same_comb_cnt, 
      SUM(COUNT(*)) total_records,
      COUNT(COUNT(*)) distinct_combinations_cnt
    FROM <some table>
    GROUP BY <list of columns>
    

    这在Oracle中有效。我用它来估计列的选择性。分组方式应用于内部聚合函数。然后,施加外部骨料。

    很乐意为SQL标准的改进提出建议。我只是不知道这是怎么回事。

        7
  •  1
  •   Mark Bostleman    16 年前

    事实上,这不是100%的时间吗?是否存在一种情况,即您可以在选择中拥有一个不在GROUP BY中的(非聚合)列?

    但我没有答案。这对语言来说确实是一个尴尬的时刻。

        8
  •  1
  •   BenMorel Manish Pradhan    11 年前

    我同意op的观点,即重复有点烦人,特别是如果非聚合字段包含ifs和函数等复杂语句以及许多其他东西。如果分组子句中能有一些简写,那就太好了——至少是一个列别名。按数字引用列可能是另一种选择,尽管它可能有自己的问题。

        9
  •  0
  •   Yar    13 年前

    例如,在某些情况下,您可能需要从所有分组的行中提取一个id,并将其数量相加。在这种情况下,您可以按名称对它们进行分组,并保留未分组的ID。SQLite似乎是这样工作的。

        10
  •  0
  •   René Höhle oasisfleeting    12 年前

    由于group-by的结果是整个元组组的单个元组,因此其他非group-by属性只能在聚合函数中使用。如果在select中添加非group by属性,则sql无法决定从该组中选择哪个值。