多个外部apply子句不会返回正确的结果?

时间:2018-12-27 18:03:19

标签: oracle

以下查询应返回列n.c的所有非空值,因为C不可为空。

select  distinct A, B, a.C, n.C
from    o
        outer apply ( -- Get first C which Status = 1, if there is any
            select C 
            from o i where i.A = o.A and i.B = o.B and STATUS = 1 and ROWNUM = 1) a
        outer apply ( -- Get the latest C otherwise (ID is identity column)
            select C
            from o i where i.A = o.A and i.B = o.B and ROWNUM = 1
            order by ID desc) n

但是,如果n.C为null,则a.C将为null。如果我移除a的外部套用,它将返回所需的值。

select  distinct A, B, n.C
from    o
        outer apply (
            select C
            from o i where i.A = o.A and i.B = o.B and ROWNUM = 1
            order by ID desc) n

这是Oracle的错误吗?

顺便说一句,如果我切换两个outer apply,它可以正常工作吗?

select  distinct A, B, a.C, n.C
from    o
        outer apply (
            select C
            from o i where i.A = o.A and i.B = o.B and ROWNUM = 1
            order by ID desc) n
        outer apply (
            select C 
            from o i where i.A = o.A and i.B = o.B and STATUS = 1 and ROWNUM = 1) a

Oracle版本

Oracle Database 12c Standard Edition Release 12.1.0.2.0 - 64bit Production
PL/SQL Release 12.1.0.2.0 - Production
CORE    12.1.0.2.0  Production
TNS for Linux: Version 12.1.0.2.0 - Production
NLSRTL Version 12.1.0.2.0 - Production

测试数据

CN中的22为空:

with o(ID, A, B, C, Status) as (
    select 1, 1, 1, 1, 1 from dual union all
    select 2, 2, 2, 2, 0 from dual union all
    select 3, 2, 2, 2, 0 from dual
    )
select  distinct A, B, a.C Ca, n.C Cn
from    o
        outer apply ( -- Get first C which Status = 1, if there is any
            select C 
            from o i where i.A = o.A and i.B = o.B and STATUS = 1 and ROWNUM = 1) a
        outer apply ( -- Get the latest C otherwise (ID is identity column)
            select C
            from o i where i.A = o.A and i.B = o.B and ROWNUM = 1
            order by ID desc) n

返回

A   B   CA  CN
1   1   1   1
2   2   NULL    NULL

CN中的22在移动outer apply (...) n后不为空:

with o(ID, A, B, C, Status) as (
    select 1, 1, 1, 1, 1 from dual union all
    select 2, 2, 2, 2, 0 from dual union all
    select 3, 2, 2, 2, 0 from dual
    )
select  distinct A, B, a.C Ca, n.C Cn
from    o
        outer apply ( -- Get the latest C otherwise (ID is identity column)
            select C
            from o i where i.A = o.A and i.B = o.B and ROWNUM = 1
            order by ID desc) n
        outer apply ( -- Get first C which Status = 1, if there is any
            select C 
            from o i where i.A = o.A and i.B = o.B and STATUS = 1 and ROWNUM = 1) a

返回

A   B   CA  CN
1   1   1   1
2   2   NULL    2

下面的查询(试图使ROWNUM更加确定)仍然得到错误的结果。

with o(ID, A, B, C, Status) as (
    select 1, 1, 1, 1, 1 from dual union all
    select 2, 2, 2, 2, 0 from dual union all
    select 3, 2, 2, 2, 0 from dual
    )
select  distinct A, B, a.C Ca, n.C Cn
from    o
        outer apply ( -- Get first C which Status = 1, if there is any
            select C 
            from o i where i.A = o.A and i.B = o.B and STATUS = 1 and ROWNUM = 1) a
        outer apply ( -- Get the latest C otherwise (ID is identity column)
            select * from (
                select C
                from o i where i.A = o.A and i.B = o.B 
                order by ID desc) x
            where ROWNUM = 1) n

1 个答案:

答案 0 :(得分:3)

在第二个外部apply语句中,将结果限制为ROWNUM = 1,但是还具有order by子句,但是ROWNUM与排序顺序无关。它表示Oracle从数据库检索行的顺序,该顺序可能与请求的显示顺序不对应。通过使用ROWNUM = 1,您可以有效地消除订单。相反,您将需要根据所需的排序和过滤条件使用生成的行号来更改第二个外部应用。

with o(ID, A, B, C, Status) as (
    select 1, 1, 1, 1, 1 from dual union all
    select 2, 2, 2, 2, 0 from dual union all
    select 3, 2, 2, 2, 0 from dual
    )
select  distinct A, B, a.C CA, n.C CN
from    o
        outer apply ( -- Get first C which Status = 1, if there is any
            select C 
            from o i where i.A = o.A and i.B = o.B and STATUS = 1 and ROWNUM = 1) a
        outer apply ( -- Get the latest C otherwise (ID is identity column)
            select C
            from (select o.*, row_number() over (partition by a, b order by id desc) rn from o) i 
            where i.A = o.A and i.B = o.B and rn = 1
            order by ID desc) n

但是请注意,对于ID = 2,标识为RN = 1的记录是不确定的,因为唯一的排序标准是ID,所以它可能是第一条记录或第二条记录。这与您的样本数据无关紧要,因为两个记录都是相同的,但是,如果C中存在差异,则结果可能是不可预测的。

返回相同数据的更简单方法可能是使用此查询,这完全避免了整个外部应用问题:

select a, b
     , max(case status when 1 then C end) keep (DENSE_RANK FIRST ORDER BY status desc, rownum) CA
     , max(c) keep (DENSE_RANK LAST ORDER by id) CN
  from o
 group by a,b;

两个查询的结果相同:

A   B   CA  CN
1   1   1   1
2   2       2