【问题标题】:JOIN on another table after GROUP BY and COUNT在 GROUP BY 和 COUNT 之后加入另一个表
【发布时间】:2010-06-20 16:42:16
【问题描述】:

我正在尝试了解使用 JOINCOUNT(*)GROUP BY 进行非常简单的查询的正确方法。我实际上已经让它工作了(见下文),但从我读到的内容来看,我使用了一个我不应该使用的额外 GROUP BY

(注意:下面的问题不是我的实际问题(处理更复杂的表),但我试图提出一个类似的问题)

我有两张桌子:

Table: Person
-------------
key  name     cityKey
1    Alice    1
2    Bob      2
3    Charles  2
4    David    1

Table: City
-------------
key  name
1    Albany
2    Berkeley
3    Chico

我想查询返回的 People(带有一些 WHERE 子句)

  • 每个城市的匹配人数
  • 城市的钥匙
  • 城市名称。

如果我这样做

SELECT COUNT(Person.key) AS count, City.key AS cityKey, City.name AS cityName
FROM Person 
LEFT JOIN City ON Person.cityKey = City.key 
GROUP BY Person.cityKey, City.name

我得到了我想要的结果

count   cityKey   cityName
2       1         Albany
2       2         Berkeley

然而,我 read 认为,为了使其正常工作而将 GROUP BY 子句 (City.name) 的最后一部分加入是错误的。

那么正确的方法是什么?我一直在尝试用谷歌搜索答案,但我觉得有些基本的东西我没有得到。

【问题讨论】:

    标签: sql count group-by left-join aggregate-functions


    【解决方案1】:

    我不认为在这种情况下它是“错误的”,因为城市名称和城市密钥之间存在一对一的关系。您可以重写它,以便您加入子选择以通过键获取城市的人数,再次到城市表的名称,但有争议的是这会更好。我猜这是风格和观点的问题。

    select PC.ct, City.key, City.name
      from City
      join (select count(Person.key) ct, cityKey key from Person group by cityKey) PC
        on City.key = PC.key
    

    如果我的 SQL 不是太生疏:-)

    【讨论】:

    【解决方案2】:

    ...我已经读过将 GROUP BY 子句 (City.name) 的最后一部分扔进去只是为了让它工作是错误的。

    你误会了,你把它弄反了。
    标准 SQL要求您在 GROUP BY 中指定 SELECT 中提到的所有未包装在聚合函数中的列。如果您不希望 GROUP BY 中的某些列,请将它们包装在聚合函数中。根据数据库,您可以使用分析/窗口函数OVER...

    但是,MySQL 和 SQLite 提供了“功能”,您可以在其中从 group by 中省略这些列 - 这导致“为什么这个端口不从 MySQL 到 fill_in_the_blank 数据库?!”没有尽头Stackoverflow 和许多其他网站和论坛。

    【讨论】:

    • 与 Andomar 建议的使用 Min(city.name) 相比,将 City.name 添加到 Group By 是否“更好”(即更快、更便携等)?
    • @Craig S:取决于您的需求。但是为了不必在 GROUP BY 中使用聚合函数是有风险的——它需要更多的测试来确保事情是正确的。我没有看到仅仅为了不必在 GROUP BY 中定义而使用聚合的意义。
    • 好的;我想在阅读那篇文章之后,我担心我只是“添加 group by 以使其工作”并且这样做我犯了一个很大的 SQL 错误。我在一个相对庞大的数据库上运行(如果是我的示例中的表,那么将有数百万人和数百个城市)
    【解决方案3】:

    但是,我读到了 GROUP BY 子句的最后一部分 (City.name) 只是为了让它工作是 错了。

    没有错。您必须了解查询优化器如何查看您的查询。解析它的顺序是要求您“将最后一部分放入”的原因。优化器以类似于以下顺序的方式查看您的查询:

    • 所需的表已连接
    • 复合数据集通过 WHERE 子句过滤
    • 剩余的行被 GROUP BY 子句分成组,并聚合
    • 然后通过 HAVING 子句再次过滤它们
    • 最终通过 SELECT / ORDER BY、UPDATE 或 DELETE 操作。

    这里的重点是,并不是 GROUP BY 必须命名 SELECT 中的所有列,但实际上恰恰相反——SELECT 不能包含任何尚未在 GROUP BY 中的列。

    【讨论】:

    • 使用聚合函数是否比向 group by 添加内容更好?
    • @Craig 这取决于您的需要。如果您不需要对数据执行函数,那么不,您不应该将其放入函数中并且应该对其进行分组。您得到聚合函数或 GROUP BY 子句错误的唯一原因是优化器需要知道您在做什么,并且它必须在此列上执行一个函数或按它分组。您不应该仅仅为了避免分组而使用函数。
    【解决方案4】:

    您的查询仅适用于 MySQL,因为您在 Person.cityKey 上分组,但选择 city.key。所有其他数据库都需要您使用像 min(city.key) 这样的聚合,或者将 City.key 添加到 group by 子句中。

    因为城市名和城市键的组合是唯一的,所以下面是等价的:

    select    count(person.key), min(city.key), min(city.name)
    ...
    group by  person.citykey
    

    或者:

    select    count(person.key), city.key, city.name
    ...
    group by  person.citykey, city.key, city.name
    

    或者:

    select    count(person.key), city.key, max(city.name)
    ...
    group by  city.key
    

    组中的所有行都将具有相同的城市名称和键,因此使用 maxmin 聚合并不重要。

    附:如果您只想计算不同的人,即使他们有多行,请尝试:

    count(DISTINCT person.key)
    

    而不是

    count(person.key)
    

    【讨论】:

    • 我认为这可能是问题中的错字;我相信他打算将他的“分组依据”包括在他的“SELECT”子句中的两个非聚合列。不过我可能是错的。
    • 对于第二个示例,group by 部分中有很多东西;从我在帖子中链接的页面上,他们说这是错误的做法。但是,MIN 部分有效; (我认为这只适用于 int 类型的列。)
    • @Craig S:如果我正确阅读了您链接到的帖子,他们只是说您应该在您选择的列上group by。我的第二个示例正确地做到了这一点。这篇文章还提到了如果你做错了你会得到的“恼人的错误”。 MySQL 是唯一一个可以接受“不正确”方式而不会出现“恼人错误”的数据库:)
    猜你喜欢
    • 2016-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-17
    • 1970-01-01
    • 1970-01-01
    • 2021-08-01
    • 2014-05-20
    相关资源
    最近更新 更多