【问题标题】:Inner join an inner join with another inner join内连接 一个内连接与另一个内连接
【发布时间】:2017-08-11 10:04:53
【问题描述】:

我想知道是否可以将一个内连接与另一个内连接进行内连接。

我有一个包含 3 个表格的数据库:
people
athletes
coachs

每个运动员或教练都必须存在于人员表中,但有些人既不是教练也不是运动员。

我要做的是找到一份至少在 3 种不同运动中活跃(即比赛或教练)的人的名单。活跃的定义是他们要么是教练、运动员,要么是该运动的教练和运动员。

person 表将由 (idnameheight) 组成 athlete 表将是 (id, sport) coaching 表将是 (id, sport)

我创建了 3 个内部联接,它们告诉我谁既是教练又是运动员,谁只是教练,谁只是运动员。

这是通过内部连接完成的。

例如,

1) 既是教练又是运动员

select 
 person.id, 
 person.name, 
 coach.sport as 'Coaches and plays this sport'
from coach 
 inner join athlete 
   on coach.id = athlete.id 
   and coach.sport = athlete.sport 
 inner join person 
   on athlete.id = person.id

这会列出所有教练和从事相同运动的人。

2)为了找出谁只教练运动,我使用了如下内连接:

select 
 person.id, 
 person.name, 
 coach.sport as 'Coaches this sport'
from coach 
 inner join person 
   on coach.id = person.id

3) 然后找出谁只玩运动,我和 2) 一样,只是调整了单词

select 
 person.id, 
 person.name, 
 athlete.sport as 'Plays this sport'
from athlete 
 inner join person 
    on athlete.id = person.id

现在我得到了最终结果: 1) 从事和指导同一项运动的人 2) 教练运动的人 3) 从事运动的人

我想知道的是如何找到参加或指导至少 3 种不同运动的人的名单?我想不通,因为如果有人在表 1 中从事和指导曲棍球等运动,那么我不想将他们计入表 2 和表 3。

我尝试使用这 3 个内部连接来制作一个庞大的连接表,以便我可以选择不同的值,但它不起作用。

有没有更简单的方法来解决这个问题而无需进行子查询?

【问题讨论】:

  • 你有什么错误吗?
  • 您需要什么实际输出?您最初说您需要找到至少在 3 项运动中活跃的人 - 但随后您现有的查询会生成更多详细信息(例如,他们如何他们在这些运动中活跃)。最终输出中是否需要该详细信息?
  • 请阅读minimal reproducible example并采取行动。

标签: sql sql-server tsql join


【解决方案1】:

我想知道的是如何找到玩的人列表/ 教练至少 3 种不同的运动?我无法弄清楚,因为如果 有人在表 1 中从事和指导曲棍球等运动,那么我不 想把它们算在表 2 和表 3 中。

你可以这样做

select p.id,min(p.name) name
from 
person p inner join
(
select id,sport from athlete
union
select id,sport from coach
)
ca
on ca.id=p.id
group by p.id
having count(ca.sport)>2

【讨论】:

    【解决方案2】:
    CREATE TABLE #person (Id INT, Name VARCHAR(50));
    CREATE TABLE #athlete (Id INT, Sport VARCHAR(50));
    CREATE TABLE #coach (Id INT, Sport VARCHAR(50));
    
    INSERT INTO #person (Id, Name) VALUES(1, 'Bob'); 
    INSERT INTO #person (Id, Name) VALUES(2, 'Carol');
    INSERT INTO #person (Id, Name) VALUES(2, 'Sam');
    
    INSERT INTO #athlete (Id, Sport) VALUES(1, 'Golf');
    INSERT INTO #athlete (Id, Sport) VALUES(1, 'Football');
    INSERT INTO #coach (Id, Sport) VALUES(1, 'Tennis');
    INSERT INTO #athlete (Id, Sport) VALUES(2, 'Tennis');
    INSERT INTO #coach (Id, Sport) VALUES(2, 'Tennis');
    INSERT INTO #athlete (Id, Sport) VALUES(2, 'Swimming');
    
    -- so Bob has 3 sports, Carol has only 2 (she both coaches and plays Tennis)
    
    SELECT p.Id, p.Name
    FROM 
    (
        SELECT Id, Sport
        FROM #athlete
        UNION -- this has an implicit "distinct"
        SELECT Id, Sport
        FROM #coach
    ) a
    INNER JOIN #person p ON a.Id = p.Id
    GROUP BY p.Id, p.Name
    HAVING COUNT(*) >= 3
    
    -- returns 1, Bob
    

    【讨论】:

    • 谢谢詹姆斯!我绞尽脑汁,因为我不知道如何代表那些参加和执教过一项运动的人。
    • @philipxy 我需要知道这些活跃于至少 x 项运动的人的 ID。所以不用担心列混乱。
    【解决方案3】:

    我用一些测试数据创建了一个 SQL - 应该适用于你的情况:

    UNION 连接子选择中的两个结果:
    UNION 将只返回非重复值。因此,每项运动都只会计算一次。

    最后只是按 person.Person_id 和 person.name 对结果集进行分组。 由于HAVING 子句,只有参加 3 项或更多运动的人将被退回-

    CREATE TABLE person
    (
       Person_id int
      ,name varchar(50)
      ,height int
    )
    
    CREATE TABLE coach
    (
        id int
       ,sport varchar(50)
    )
    
    CREATE TABLE athlete
    (
        id int
       ,sport varchar(50)
    )
    
    INSERT INTO person VALUES
    (1,'John', 130),
    (2,'Jack', 150),
    (3,'William', 170),
    (4,'Averel', 190),
    (5,'Lucky Luke', 180),
    (6,'Jolly Jumper', 250),
    (7,'Rantanplan ', 90)
    
    INSERT INTO coach VALUES
    (1,'Football'),
    (1,'Hockey'),
    (1,'Skiing'),
    (2,'Tennis'),
    (2,'Curling'),
    (4,'Tennis'),
    (5,'Volleyball')
    
    
    INSERT INTO athlete VALUES
    (1,'Football'),
    (1,'Hockey'),
    (2,'Tennis'),
    (2,'Volleyball'),
    (2,'Hockey'),
    (4,'Tennis'),
    (5,'Volleyball'),
    (3,'Tennis'),
    (6,'Volleyball'),
    (6,'Tennis'),
    (6,'Hockey'),
    (6,'Football'),
    (6,'Cricket')
    
    
              SELECT person.Person_id
                    ,person.name
                FROM person
          INNER JOIN (
                        SELECT id
                              ,sport
                          FROM athlete
                       UNION
                        SELECT id
                              ,sport
                          FROM coach          
                     ) sports
                   ON sports.id = person.Person_id
            GROUP BY person.Person_id
                    ,person.name   
              HAVING COUNT(*) >= 3
            ORDER BY Person_id
    

    【讨论】:

      【解决方案4】:

      教练和运动员,即教练或运动员,与您的答案相关。那是联合(一个中的行),而不是(内部)连接一个中的行)。 (虽然外连接涉及联合,所以这里有一种复杂的使用方式。)但是通过联合 only-coachs、only-athletes 和 coach-athletes 来实现这一点是没有意义的。

      惯用语是对运动员和教练员的工会进行分组和统计。

      select id
      from (select * from Athletes union select * from Coaches) as u
      group by id
      having COUNT(*) >= 3
      

      或者,您需要教练或参加第一项运动和教练或参加第二项运动并教练或参加第三项运动的人的 ID,而这些运动都是不同的。

      with u as (select * from Athletes union select * from Coaches)
      select u1.id
      from u u1
      join u u2 on u1.id = u2.id
      join u u3 on u2.id = u3.id
      where u1.sport <> u2.sport and u2.sport <> u3.sport and u1.sport <> u3.sport
      

      如果你想要名字,你可以加入 People。

      是否有任何经验法则可以根据人类可读的描述构造 SQL 查询?](https://stackoverflow.com/a/33952141/3404097)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-08
        • 2014-10-16
        相关资源
        最近更新 更多