正确使用HAVING子句,返回唯一行

时间:2013-06-01 22:20:06

标签: mysql sql

在此方案中我有3个表:TeamsPlayersPlayersInTeams

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

Team维持赢/输记录。如果玩家自己玩,那么他就会玩他的独奏"团队(Team只有该玩家)。每次Bob和Jan一起获胜时,他们的Team条目都会获得胜利++。每次杰森和汤米一起输球时,他们的球队入场都会有损失++。

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

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

所以这是困难的部分:

由于Player可以是多个Teams的一部分,因此在匹配开始时从TeamId表中获取正确的Teams非常重要。

玩家的SOLO团队由

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

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

我试图说:

  

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

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

5 个答案:

答案 0 :(得分:3)

要修复您的查询,请添加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

- &GT;

player   team    count(team)
1        1       1

前两列来自player = 1的随机行。 count(team)值为2,因为有两行player = 1和非空team。伯爵对球队中球员的数量一无所知。

答案 1 :(得分: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

编辑“solo team”:

正如Andomar所指出的, solo 团队的定义并不完全符合我的预期。这是球员中唯一的球员。因此,要获得给定玩家是团队的团队列表:

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

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

如果你想要所有独奏团队:

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

答案 2 :(得分:1)

HAVING通常与GROUP BY语句一起使用 - 就像WHERE一样,它会应用于分组数据。

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

答案 3 :(得分:1)

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;

答案 4 :(得分:0)

这里的每个人的答案都非常有帮助。除了遗漏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.所以你总是得到一个团队列表 player 1是的一部分,但是你仍然不认识他的SOLO团队。

戈登回答我的附录

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

特别好,因为它是一个便宜的查询,并做我们想要的。它选择所有SOLO团队第一,(选择所有仅在表中发生ONCE的fkTeamIds(通过HAVING count(*)=1)),然后从那里我们去说,“好吧,现在给我一个有这个fkPlayerId = 1的那个”。瞧,独立团队。

DUO球队更难,但相似。难度与上面的问题相似,只有3个团队的团队模糊不清。Details in an SQL Fiddle