【问题标题】:Oracle: How to use left outer join to get all entries from left table and satisfying the condition in Where clauseOracle:如何使用左外连接从左表中获取所有条目并满足 Where 子句中的条件
【发布时间】:2017-11-18 12:23:47
【问题描述】:

我有下表。

客户:

ID | clientName
--------------
1    A1        
2    A2
3    A3          

订单:

OrdID clientID status_cd
------------------------
100  1        DONE
101  1        SENT
102  3       SENT

状态:

status_cd  status_category
DONE        COMPL
SENT        INPROG

我必须编写一个查询来获取所有客户并针对所有客户进行订单计数,无论 client_id 是否存在于 Order 表中并且具有“COMPL”作为状态类别的订单。

在这种情况下,我使用下面的查询,但它会过滤掉没有订单的客户。我想获得所有客户,使预期结果如下。

查询:

select c.ID, count(distinct o.OrdID) 
from client c, order o, status s
where c.ID=o.client_id(+)
and o.status_cd=s.status_cd where s.status_category='COMPL'
group by c.ID

预期结果:

C.ID   count(distinct o.OrdID)
---------------------------- 
1       1
2       0
3       0

有人可以帮我解决这个问题吗?我知道,在这种情况下,当我使用 where 子句时,左外连接的行为类似于内连接,但是还有其他方法可以实现上述结果吗?

【问题讨论】:

  • 甚至 Oracle 也建议停止使用 (+) 进行外连接。请改用明确的left join
  • 阅读 oracle 文档以了解“(+)”符号。您没有正确使用它来获得外部连接。 (它被忽略了。)甚至 oracle 都说不要使用它。

标签: sql oracle outer-join


【解决方案1】:

使用显式连接运算符可以更轻松地处理这个问题:

select c.ID, count(distinct s.status_cd) 
from client c
  left join orders o on o.clientid = c.id
  left join status s on s.status_cd = o.status_cd and s.status_category='COMPL'
group by c.ID;

以上假设orders.status_cd定义为not null

另一种选择是在派生表中移动订单和状态之间的连接:

select c.ID, count(distinct o.ordid) 
from client c
  left join (
    select o.ordid
    from orders o
      join status s on s.status_cd = o.status_cd
    where s.status_category='COMPL'
  ) o on o.clientid = c.id
group by c.ID;

与第一个解决方案相比,上述“状态”更清楚(至少在我看来)只有该状态类别中的订单才有意义

【讨论】:

    【解决方案2】:

    像往常一样,有很多方法可以表达这个要求。

    尝试 ANSI 加入的人会讨厌我投票否决这个答案;):

    select c.ID, count(distinct o.OrdID) 
    from client c, order o, status s
    where c.ID = o.client_id(+)
    and o.status_cd = s.status_cd 
    and s.status_category='COMPL'
    group by c.ID
    ;
    

    select c.ID
    , nvl((select count(distinct o.OrdID) 
       from order o, status s 
       where c.ID = o.client_id 
         and o.status_cd = s.status_cd 
         and s.status_category='COMPL'
       ), 0) as order_count
    from client c
    group by c.ID
    ;
    

    with ord as
    (select client_id, count(distinct o.OrdID) cnt
       from order o, status s 
       where 1=1
         and o.status_cd = s.status_cd 
         and s.status_category='COMPL'
       group by client_id
    )
    select c.ID
    , nvl((select cnt from ord o where c.ID = o.client_id ), 0) as order_count
    from client c
    group by c.ID
    ;
    

    ...

    【讨论】:

    • 如果您投反对票,他们不一定是因为使用 Oracle 表示法。例如:在您的第一个解决方案中,您不会得到正确的结果,因为您仍然没有向左加入第三个表(无论符号/语法如何)。
    • 你好@mathguy。我看过你的一些cmets和答案,发现它们是高水平的。有趣的是,您内置了 SQL 编译器和执行引擎;我只是在我的大脑中内置了一个 SQL 编译器。我实际上并不担心否决票,我真的只是觉得他们很好奇。自 1985 年 (v4) 以来,我一直在使用 Oracle,但我确信大多数人都会犯错误。无论如何,如果你的皮肤太薄,你在这个领域就达不到我的年龄。
    • 非常感谢罗杰。我尝试了以下方法,它确实有效: select c.ID , nvl((select count(distinct o.OrdID) from order o, status s where c.ID = o.client_id and o.status_cd = s.status_cd and s .status_category='COMPL' ), 0) as order_count from client c group by c.ID ;
    【解决方案3】:

    第二个 WHERE 应该是 AND。

    除此之外,您还需要在第二个连接条件中使用加号 (+),标记左外连接。左外连接前两个表是不够的。

    类似

    select c.ID, count(distinct o.OrdID) 
    from client c, order o, status s
    where c.ID=o.client_id(+)
    and o.status_cd=s.status_cd(+)    AND   s.status_category='COMPL'
    --                         ^^^    ^^^  (not WHERE)
    group by c.ID
    

    当然,如果您使用正确的(SQL 标准)连接语法会更好。

    【讨论】:

    • 对不起,这是一个错字,第二个是 AND,它没有带来预期的结果。
    • @user5252179 - 您仍然遇到在第二个连接条件中缺少(+) 的问题。 那个回答了你的问题吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-02
    • 1970-01-01
    • 1970-01-01
    • 2021-12-07
    • 2021-04-04
    • 2012-05-01
    • 2019-04-04
    相关资源
    最近更新 更多