这是一种可接受的加入方式(左外联接表)吗?

时间:2015-12-21 21:12:17

标签: sql oracle oracle10g

这是设置 -

           create table tasks (taskno int, customerno int);
           insert into tasks values (1, 100);
           commit;
           insert into tasks values (2, 200);
           commit;
           insert into tasks values (3, 300);
           commit;
           select * from tasks;

           create table items (taskno int, accountno int);
           commit;
           insert into items values (1, 1000);
           commit;
           insert into items values (2, 2000);
           commit;
           select * from items;

           create table accounts (accountno int, customerno int);
           commit;
           insert into accounts values (1000, 100);
           commit;
           insert into accounts values (1100, 100);
           commit;
           insert into accounts values (2000, 200);
           commit;
           insert into accounts values (3000, 300);
           commit;
           select * from accounts;    

我想根据accountno从task表中获取taskno。任务表只有一个名为customerno的东西。此customerno可以与多个accountno相关联(将customerno视为父级,将accountno视为子级)。因此,如果您查看我们的设置,如果我传入accountno 1000或1100,则在此查询中都将返回taskno 1 -

           select a.taskno
           from tasks a, accounts c
           where a.customerno = c.customerno
           and c.accountno = 1000 -- but will return taskno 1 also for 1100

我想要一些比这更详细的细节。所以我找到了另一张桌子' Items'其中有taskno和accountno。因此,如果我将其添加到查询中,它将正确返回accountno 1000的taskno 1而不是1100.

           select a.taskno
           from tasks a, items b, accounts c
           where a.taskno = b.taskno
           and a.customerno = c.customerno
           and c.accountno = b.accountno
           and c.accountno = 1000 -- nothing returned for 1100

这一切都很好但是Items表并不总是可靠的。它可以说约占任务表中90%的任务。因此,在这种情况下,当在Items表中找不到任务时,我希望它来自Tasks表,例如accountno 3000(这意味着我将不得不通过customerno,并且不会具有粒度级别的accountno join。但是没关系) 。但是当在Items中找到这个accountno时,我希望它被使用,因为它有accountno,这给了我与完全accountno相关联的taskno。所以我对包含任务的Items使用左外连接。

这完美无缺 -

           select a.taskno
           from tasks a, items b, accounts c
           where a.taskno = b.taskno(+)
           and a.customerno = c.customerno
           and c.accountno = nvl(b.accountno, c.accountno)
           and c.accountno = 3000 -- will return taskno 3


           select a.taskno
           from tasks a, items b, accounts c
           where a.taskno = b.taskno(+)
           and a.customerno = c.customerno
           and c.accountno = nvl(b.accountno, c.accountno)
           and c.accountno = 1000 --returns 1 and nothing returned for 1100

我的问题是我在这里正确构建了查询 - 特别是我用NVL将项目链接到帐户的部分?这是预期的方法吗?或者这是一个奇怪的回合路?

1 个答案:

答案 0 :(得分:1)

就像你说的那样,你的查询确实有效,并且非常聪明。但是很难阅读和理解,部分原因是因为使用了连接语法。使用ANSI连接进行翻译,我们得到:

select t.taskno
  from accounts a
  join tasks t
    on t.customerno = a.customerno
  left join items i
    on i.taskno = t.taskno
 where a.accountno = 1100
   and a.accountno = nvl(i.accountno, a.accountno)

我仍然觉得意图不是很清楚。

就个人而言,我会重写查询,将左连接的逻辑移到not exists子句中。在我看来,它更好地表达了意图,并且它的效果也一样。

在ANSI连接语法中:

select t.taskno
  from accounts a
  join tasks t
    on t.customerno = a.customerno
 where a.accountno = 1100
   and not exists (select null
                     from items i
                    where i.taskno = t.taskno
                      and i.accountno <> a.accountno)

旧版连接语法中的相同查询,如果它可以帮助您更好地理解(但如果可能的话,尝试远离此语法):

select t.taskno
  from accounts a, tasks t
 where a.accountno = 1100
   and t.customerno = a.customerno
   and not exists (select null
                     from items i
                    where i.taskno = t.taskno
                      and i.accountno <> a.accountno)