如何在SQL中组合自然连接和左外连接

时间:2016-10-30 19:35:17

标签: sql oracle

我有3个学生表,注册表和课程。我想列出每个学生和每个班级的部门代码(部门代码在课堂上)包括未参加任何课程的学生(在这种情况下,部门代码将是空白的)。这是必要的,因为将来的计算将会完成。

以下是我对此的看法。以下查询执行学生和注册的左连接,以便我包括尚未注册任何课程的学生

select s.B#, s.firstname, e.classid
from students s, enrollments e 
where s.B#=e.B#(+)

此查询按预期工作。

接下来,我加入了注册和课程,以便我可以看到每个注册的相应dept_code:

select B#, dept_code
from enrollments natural join classes

这也按预期工作。

当我尝试将两者结合在一起时,我遇到了问题

select s.B#, s.firstname, dept_code
from students s, (enrollments e natural join classes)
where s.B# = e.B#(+)

尝试运行此查询时出现错误:

ORA-25156: old style outer join (+) cannot be used with ANSI joins

有人可以解释一下这里发生了什么吗?

4 个答案:

答案 0 :(得分:2)

最好使用显式JOIN而不是使用where子句的旧式逗号分隔连接。同样适用于(+)

改为编写您的查询:

select s.B#, s.firstname, dept_code
from students s
left join (
select * -- if there are same names of columns in both tables, specify them explicitly
from enrollments e
join classes c on e.? = c.? -- specify here your natural join columns
) e on s.B# = e.B#

顺便说一句,错误告诉您确切地说,您无法将旧样式连接与ANSI连接组合在一起。可能是时候切换到现在存在很长时间的新格式了。

答案 1 :(得分:1)

支架并不是真的需要,我把它们放在那里只是为了强调结构

select      s.B#, s.firstname, c.dept_code
from                    students    s 
            left join   (           enrollments e
                        join        classes     c
                        on          c.classId = e.classId
                        )
            on s.B# = e.B3 
;            

也可能这样写(只有不同​​的格式)

select      s.B#, s.firstname, c.dept_code
from                    students    s 
            left join   enrollments e
            join        classes     c
            on          c.classId = e.classId
            on          s.B# = e.B3 
;            

P.S。 如果您对此语法有疑问,可以尝试这样做:

create table t1 (i int);
create table t2 (i int);
create table t3 (i int);
create table t4 (i int);
create table t5 (i int);
insert into t1 (i) values (1);
insert into t2 (i) values (1);
insert into t3 (i) values (1);
insert into t4 (i) values (1);
insert into t5 (i) values (1); 
select  *
from            t1
        join    t2
        join    t3
        join    t4
        join    t5
        on      t5.i=t4.i
        on      t4.i=t3.i
        on      t3.i=t2.i
        on      t2.i=t1.i
;       

答案 2 :(得分:0)

您的查询应如下所示:

select s.B#, s.firstname, c.dept_code
from students s left join
     enrollments e
     on s.B# = e.B3 left join
     classes c
     on e.classId = b.classId;  -- I am guessing what the `JOIN` column is

换句话说,您需要两个left join来保留所有学生。否则,on子句可能会过滤掉行。

我也强烈建议你避免和不学习NATURAL JOIN。这只是代码中等待发生的错误。它仅根据列名匹配列 - 甚至不考虑声明的外键关系。

它隐藏了连接列,这使得调试和理解查询变得更加困难。例如,因为我创建的表几乎总是有CreatedAtCreatedBy等列,所以无法使用它。

编辑:(对于lgrade

您可以将条件放在ON子句中:

select s.B#, s.firstname, c.dept_code
from students s left join
     enrollments e
     on s.B# = e.B3 and e.lgrade is not null left join
     classes c
     on e.classId = b.classId;  -- I am guessing what the `JOIN` column is

答案 3 :(得分:0)

我强烈建议您避免和取消学习外连接,因为它会产生空值。拥有空值只是代码中等待发生的错误。外连接根本不是一个连接,而是一个'非自然的'联合,使用空值来强制一起。

建议的替代方法:使用适当的默认值自己进行联合:

select B#, firstname, dept_code
  from students 
       natural join enrollments
       natural join classes
union
select B#, firstname, '{{NONE}}' as dept_code
  from students
 where B# not in ( select B# from enrollments );