如何在3个表上使用外连接。

时间:2017-03-13 15:52:07

标签: sql oracle

在我的数据库中,我必须列出在Guadalcanal战斗中所有战斗的船只的名称,位移和枪支数量。为此,我需要使用3个表。这个名字来自船舶表,枪支数量和位移来自Classes表,战斗来自战斗表。

Create Table Classes (
    class Varchar(40),
    type Char (2),
    country Varchar(15),
    numGuns Int,
    bore Int,
    displacement Int
);
Create Table Ships (
    name Varchar(40),
    class Varchar(40),
    launched Int
);
Create Table Battles(
    name Varchar(40),
    date_fought Date
);

以下是我尝试使用的查询:

Select Ships.name, displacement, numGuns
From (Classes Full Outer Join Ships ON Classes.class = Ships.class), Battles
Where Battles.name = 'Guadalcanal';

现在这不起作用主要是因为我认为我没有正确使用外连接。问题是我不知道如何使用3个表的外连接。

3 个答案:

答案 0 :(得分:2)

首先确定您的主要信息来源。在这里,您正在寻找一场战斗以及相关的船只和他们的班级。 OUTER JOIN表示即使另一侧没有匹配也会返回一侧。在这种情况下,如果你从战斗开始,你只需要匹配的船只:

select     b.name 
from       Battles b 
inner join Ships   s on b.name = s.battle_name

我认为您还没有任何东西可以将船只与战斗联系起来。我的猜测是你错过了一个匹配战舰的XREF表。

Battle_Ships

+++++++++++++++
battle  | ship
+++++++++++++++
battle1 | ship1
battle1 | ship2
battle1 | ship3
battle2 | ship3
battle2 | ship4

有了这个,你就可以得到战斗和船只:

select          b.name, s.name 
from            Battles      b 
left outer join Battle_Ships bs on b.name = bs.battle 
left outer join Ships        s  on bs.ship = s.name

然后你会想要得到船的等级,这样你就可以得到位移,但我猜这可能不存在,所以你想要外连接

select          s.name, c.numGuns, c.displacement 
from            Ships   s 
left outer join Classes c on s.class = c.class

总之,你会有类似的东西:

select          b.name, s.name, c.numGuns, c.displacement 
from            Battles      b 
left outer join Battle_Ships bs on b.name = bs.battle 
left outer join Ships        s  on bs.ship = s.name 
left outer join Classes      c  on s.class = c.class 
where           b.name = 'Guadalcanal'

同样,左外连接意味着匹配左侧并且如果存在则返回右侧。在这里我们从Battle开始,然后匹配Battle_Ships,如果有的话,匹配Ships如果有的话,那么Classes。你几乎从不想要使用完整的外部连接,除非你也想要展示哪些船只从未在战斗中(因为那样它也允许左侧也是空的)。

答案 1 :(得分:0)

战斗和课程/船舶共享的关键是什么?一旦你有了,以下应该工作。此编辑假定Ships与Battle共享密钥。

SELECT Ships.name, displacement, numGuns
FROM Ships 
FULL JOIN Classes ON Classes.class = Ships.class,
FULL JOIN Battles ON Battles.ThatKey = Ships.SameKey
WHERE Battles.name = 'Guadalcanal';

答案 2 :(得分:0)

首先,您希望在每个表中都有一个主键,以便您可以准确地将表相互关联。如果class在classes表中是唯一的,并且每个Ship记录都有一个在Classes表中列出的类,那么它将作为Classes表中的键。除非每个船名,无论是哪一类,都是唯一的,否则您也需要该表中的主键。战斗也需要一把钥匙。   战斗和船舶表很可能彼此之间存在多对多的关系。一艘船可以进行多场战斗,一场战斗可以有多艘战舰。需要另一个表来将这两个表链接在一起。也许(仅使用此处的列名称)表可以是Battles_Ships_Rel:

Create Table Battles_Ships_Rel (
    battles_ships_rel_id Int,
    battle_name Varchar(40),
    ship_name Varchar(40)
);

这不太理想,因为battle_name和ship_name都引用了2个不同表中具有相同名称但含义不同的列。此外,完全外部联接将获得您不想要的记录,因为它将从所有3个表中提取记录,无论它们是否匹配。如果你只想要来自瓜达尔卡纳尔战役的船只,那么你想要一个内部连接(除非没有船只参加那场战斗,但我认为情况并非如此)。我们可以将Classes外部连接到船只,因为外部连接是您请求的,但它应该是左外连接。以下查询将起作用:

SELECT Ships.name, Classes.displacement, Classes.numGuns
FROM Ships
INNER JOIN Battles_Ships_Rel
  ON Ships.name = Battles_Ships_Rel.ship_name
LEFT INNER JOIN Classes
--all ships, but only classes that have a record for Ships.class
  ON Ships.class = Classes.class 
WHERE Battles_Ships_Rel.battle_name = 'Guadalcanal';

由于我们在此示例中的Battles_Ships_Rel中有战斗名称,因此不需要战斗表。