【问题标题】:Correct use of the HAVING clause, to return the unique row正确使用 HAVING 子句,返回唯一行
【发布时间】:2013-06-02 23:48:11
【问题描述】:

在这种情况下,我有 3 个表:TeamsPlayersPlayersInTeams

Player 只是一个注册用户。没有 W/L 数据与 Player 关联。

Team 维护赢/输记录。如果一个玩家自己玩,那么他会和他的“单人”团队一起玩(Team,里面只有那个玩家)。每次 Bob 和 Jan 一起获胜时,他们的 Team 条目就会获得 win++。每次 Jason 和 Tommy 一起输球时,他们的参赛队伍都会输球++。

PlayersInTeams表只有2列,是PlayersTeams的交集表:

> desc PlayersInTeams ;
+------------+---------+
| Field      | Type    |
+------------+---------+
| fkPlayerId | int(11) |
| fkTeamId   | int(11) |
+------------+---------+

所以这是困难的部分:

因为Player 可以是多个Teams 的一部分,所以在匹配开始时从Teams 表中获取正确的TeamId 很重要。

玩家的 SOLO 团队由

select fkTeamId from PlayersInTeams where
fkPlayerId=1 HAVING count(fkTeamId)=1;

不,不是!!但我不明白为什么。

我想说:

从 PlayersInTeams 获取 fkTeamId
fkPlayerId=1,还有行数
有这个特定的 fkTeamId 正好是 1。

查询返回(空集),实际上如果我将 HAVING 子句更改为不正确的(HAVING count(fkTeamId)<>1;),它会返回我想要的行。

【问题讨论】:

标签: mysql sql


【解决方案1】:

要修正您的查询,请添加group by。要计算每个团队的计数,您需要更改 where 子句以返回玩家 1 所在的所有团队:

select  fkTeamId 
from    PlayersInTeams 
where   fkTeamId in
        (
        select  fkTeamId
        from    PlayersInTeams
        where   fkPlayerId = 1 
        )
group by
        fkTeamId
having  count(*) = 1;

Example at SQL Fiddle.


下面详细解释了为什么您的count(*) = 1 条件以令人惊讶的方式起作用。当查询包含像count 这样的聚合,但没有group by 子句时,数据库会将整个结果集视为一个组。

在 MySQL 以外的数据库中,如果没有聚合,则无法选择不在 group by 中的列。在 MySQL 中,所有这些列都与数据库遇到的第一个值一起返回(本质上是组中的随机值。)

For example:

create table YourTable (player int, team int);
insert YourTable values (1,1), (1,2), (2,2);

select  player
,       team
,       count(team)
from    YourTable
where   player = 2

-->

player   team    count(team)
1        1       1

前两列来自player = 1 的随机行。 count(team) 的值为 2,因为有两行 player = 1 和一个非空的 team。计数并没有说明团队中的玩家人数。

【讨论】:

    【解决方案2】:

    最自然的做法是计算行数以查看发生了什么:

    select fkTeamId, count(*)
    from PlayersInTeams
    where fkPlayerId=1
    group by fkTeamId;
    

    group by 子句是一种更自然的查询方式:

    select fkTeamId
    from PlayersInTeams
    where fkPlayerId=1
    having count(fkteamid) = 1
    

    但是,如果一个玩家只有一行,那么您的原始版本应该可以工作 - 过滤会将其带到一行,fkTeamId 将是该行的团队,having 将被满足。一种可能是数据中有重复的行。

    如果重复是个问题,您可以这样做:

    select fkTeamId
    from PlayersInTeams
    where fkPlayerId=1
    having count(distinct fkteamid) = 1
    

    编辑“单人团队”:

    正如 Andomar 所指出的,solo 团队的定义并不完全符合我的预期。它是一名球员,是球队中唯一的球员。因此,要获取给定球员所在球队的球队名单:

    select fkTeamId
    from PlayersInTeams
    group by fkTeamId
    having sum(fkPlayerId <> 1) = 0
    

    也就是说,您无法过滤掉其他玩家并期望获得此信息。您特别需要他们,以确保他们不在团队中。

    如果您想获得所有个单人团队:

    select fkTeamId
    from PlayersInTeams
    group by fkTeamId
    having count(*) = 1
    

    【讨论】:

    • 您的where 子句会过滤掉所有其他玩家的记录,因此您无法查看某个玩家是否是团队中的单人玩家。
    • @Andomar 。 . .我理解这个问题是一名球员是在一支球队还是在多支球队。也许我误解了这个问题。
    • 问题是“具有此特定 fkTeamId 的行数正好为 1。”我只是在第二次阅读时才注意到,但似乎很清楚。
    • 如果你在最后一个查询中加上and fkPlayerId=1,你实际上也可以得到solo team。
    • +1 having sum(fkPlayerId &lt;&gt; 1) = 0 简洁的查询方式!但不是sargeable,所以如果(fkPlayerId, fkTeamId)(fkTeamId, fkPlayerId)上有索引,我想我的版本会更快。
    【解决方案3】:

    HAVING 通常与GROUP BY 语句一起使用——就像WHERE 应用于分组 数据。

    SELECT fkTeamId
    FROM PlayersInTeams
    WHERE fkPlayerId = 1
    GROUP BY fkTeamId
    HAVING COUNT(fkPlayerId) = 1
    

    【讨论】:

    • 谢谢。它仍然返回多行(例如团队 1 和 3),因为我认为 WHERE 子句消除了重复项......所以当它下降到 HAVING 子句时,所有的 teamIds 确实有 COUNT=1
    • 您的where 子句会过滤掉所有其他玩家的记录,因此您无法查看某个玩家是否是团队中的单人玩家。
    【解决方案4】:

    这里是SqlFiddle:http://sqlfiddle.com/#!2/36530/21

    尝试先找到有一名球员的球队,然后使用该球员来查找球员 ID(如果他们在任何球队中):

    select DISTINCT PlayersInTeams.fkTeamId
    from (
      select fkTeamId 
      from PlayersInTeams
      GROUP BY fkTeamId
      HAVING count(fkPlayerId)=1
    ) AS Sub
    INNER JOIN PlayersInTeams
      ON PlayersInTeams.fkTeamId = Sub.fkTeamId
    WHERE PlayersInTeams.fkPlayerId = 1;
    

    【讨论】:

    • 更正了“不一样”的样本
    • 您的where 子句会过滤掉所有其他玩家的记录,因此您无法查看某个玩家是否是团队中的单人玩家。
    • @Andomar 是的,这正是正在发生的事情。
    • @bobobobo。在这种情况下,首先找到具有单个玩家 ID 的团队,然后在其他所有内容之外执行玩家 where - 代码已更新以反映该方法。
    【解决方案5】:

    这里每个人的回答都非常有帮助。除了缺少GROUP BY之外,我发现我的问题主要是我的WHERE子句“太早了”。

    说我们有

    insert into PlayersInTeams values
      (1, 1), -- player 1 is in solo team id=1
      (1, 2),(2,2) -- player 1&2 are on duo team id=2
    ;
    

    现在我们试试:

    select fkTeamId from PlayersInTeams
    where fkPlayerId=1 -- X kills off data we need
    group by fkTeamId
    HAVING count(fkTeamId)=1;
    

    特别是WHERE 正在过滤临时结果集,以便没有 fkPlayerId=1 的任何行都被从结果集中删除。由于每个 fkPlayerId 只与每个 fkTeamId 关联一次,当然 fkTeamId 出现次数的 COUNT 始终为 1。所以您总是只返回一个团队列表玩家 1 是其中的一部分,但你仍然不了解他的 SOLO 团队。

    戈登对我的附录的回答

    select fkPlayerId,fkTeamId
    from PlayersInTeams
    group by fkTeamId
    having count(fkTeamId) = 1
    and fkPlayerId=1 ;
    

    特别好,因为它是一个便宜的查询并且可以满足我们的需求。它首先选择所有 SOLO 团队,(选择所有在表中仅出现一次的 fkTeamIds(通过HAVING count(*)=1),然后从那里我们去说,“好的,现在给我一个有这个 fkPlayerId=1" 的。瞧,单人团队。

    二人组更难,但相似。难度与上述问题相似,只是 3 人团队与 2 人团队存在歧义。Details in an SQL Fiddle

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-28
      • 1970-01-01
      • 1970-01-01
      • 2023-04-09
      相关资源
      最近更新 更多