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

Is this an accepted way to join (left outer join table)?

这里是设置-

           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;    

我想根据一个帐号从任务 table 中获取任务号。任务 table 只有一个叫做 customerno 的东西。此 customerno 可以与多个 accountno 关联(将 customerno 视为父级,将 accountno 视为子级)。因此,如果您查看我们的设置,如果我传入 accountno 1000 或 1100,则此查询中的 return 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

我想要比这更详细的细节。所以我找到了另一个 table 'Items',它有 taskno 和 accountno。因此,如果我将它添加到查询中,它将正确地 return taskno 1 用于 accountno 1000 而不是 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

这一切都很好,但项目 table 并不总是可靠的。它可以说只有 90% 的任务在任务 table 中找到。因此,在这种情况下,当在项目 table 中找不到任务时,我希望从任务 table 中找到它,例如 accountno 3000(这意味着我将不得不通过 customerno 并且不会有粒度级别帐户没有加入。但没关系)。但是当在 Items 中找到这个 accountno 时,我希望它被使用,因为它有 accountno,这给了我与确切的 accountno 关联的任务号。因此,我将左外连接用于带有任务的项目。

这很完美-

           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 将项目链接到帐户的部分?这是执行此操作的预期方法吗?或者这是一个奇怪的循环方式?

正如您所说,您的查询确实有效,而且非常聪明。但它很难阅读和理解,部分原因在于所使用的连接语法。使用 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)