简体   繁体   English

Oracle 连接语法中具有多个条件的外部连接

[英]Outer Joins with Multiple Conditions in Oracle Join Syntax

I have the tables below我有下面的表格

create table xx_base_tbl
(
    tbl_id      number
,   trx_num     varchar2(100)
);


create table xx_dtl_tbl
(
    dtl_id      number
,   tbl_id      number
,   category    varchar2(100)
,   attribute1  varchar2(100)
);

insert into xx_base_tbl (tbl_id, trx_num) values (1, 'trx 1');
insert into xx_base_tbl (tbl_id, trx_num) values (2, 'trx 2');
insert into xx_base_tbl (tbl_id, trx_num) values (3, 'trx 3');
insert into xx_base_tbl (tbl_id, trx_num) values (4, 'trx 4');
insert into xx_base_tbl (tbl_id, trx_num) values (5, 'trx 5');

insert into xx_dtl_tbl (dtl_id, tbl_id, category, attribute1) values (1, 1, null, 'SAMPLE');
insert into xx_dtl_tbl (dtl_id, tbl_id, category, attribute1) values (2, 1, null, 'hello');
insert into xx_dtl_tbl (dtl_id, tbl_id, category, attribute1) values (3, 2, 'PREPAYMENT', 'this is not a value');
insert into xx_dtl_tbl (dtl_id, tbl_id, category, attribute1) values (4, 2, 'PREPAYMENT', 1);
insert into xx_dtl_tbl (dtl_id, tbl_id, category, attribute1) values (5, 3, 'PREPAYMENT', 2);
insert into xx_dtl_tbl (dtl_id, tbl_id, category, attribute1) values (6, 3, 'PREPAYMENT', 1);
insert into xx_dtl_tbl (dtl_id, tbl_id, category, attribute1) values (7, 3, 'SAMPLE', 15678);
insert into xx_dtl_tbl (dtl_id, tbl_id, category, attribute1) values (8, 4, 'PREPAYMENT', 1);
insert into xx_dtl_tbl (dtl_id, tbl_id, category, attribute1) values (9, 4, 'PREPAYMENT', NULL);
insert into xx_dtl_tbl (dtl_id, tbl_id, category, attribute1) values (10, 5, 'PREPAYMENT', null);
insert into xx_dtl_tbl (dtl_id, tbl_id, category, attribute1) values (11, 5, 'SAMPLE', 'YEY');

I am using the ANSI Syntax below to outer join xx_dtl_tbl to another xx_base_tbl show only CATEGORY with PREPAYMENT and numeric values only.我正在使用下面的 ANSI 语法将 xx_dtl_tbl 外部连接到另一个 xx_base_tbl 仅显示具有 PREPAYMENT 和数值的 CATEGORY。 then use LISTAGG() to aggregate the results into a single row然后使用LISTAGG()将结果聚合到一行中

SELECT
    xx1.trx_num,
    LISTAGG(xx3.trx_num, ',') WITHIN GROUP(
        ORDER BY
            xx3.trx_num
    ) prepayment
FROM
         xx_base_tbl xx1
    INNER JOIN xx_dtl_tbl   xx2 ON xx1.tbl_id = xx2.tbl_id
    LEFT JOIN xx_base_tbl  xx3 ON (to_number(xx2.attribute1) = xx3.tbl_id and length(TRIM(translate(xx2.attribute1, ' +-.0123456789', ' '))) IS NULL)    
GROUP BY
    xx1.trx_num

The result looks fine:结果看起来不错:

TRX_NUM     PREPAYMENT
-------     -------------
trx 1   
trx 2       trx 1
trx 3       trx 1,trx 2
trx 4       trx 1
trx 5   

However, when i use SQL Developer's tool to change the syntax to Oracle Join Syntax , it get the below result:但是,当我使用SQL 开发工具将语法更改为 Oracle Join Syntax时,得到以下结果:

SELECT
    xx1.trx_num,
    LISTAGG(xx3.trx_num, ',') WITHIN GROUP(
        ORDER BY
            xx3.trx_num
    ) prepayment
FROM
    xx_base_tbl  xx1,
    xx_dtl_tbl   xx2,
    xx_base_tbl  xx3
WHERE
        xx1.tbl_id = xx2.tbl_id
    AND to_number(xx2.attribute1) = xx3.tbl_id (+)
    AND ( length(TRIM(translate(xx2.attribute1, ' +-.0123456789', ' '))) IS NULL )
GROUP BY
    xx1.trx_num
    

The result changed:结果变了:

TRX_NUM     PREPAYMENT
-------     -------------
trx 2       trx 1
trx 3       trx 1,trx 2
trx 4       trx 1
trx 5   

the trx 1 row is suddenly missing. trx 1 行突然丢失。 How can I write this in Oracle Syntax?我怎样才能在 Oracle 语法中写这个?

This query is not so easy to transform to old syntax.这个查询不太容易转换为旧语法。 Something which worked for me is:对我有用的是:

select a.trx_num,
       listagg(b.trx_num, ',') within group (order by b.trx_num) prepayment 
  from (

    select trx_num, attribute1, 
           case when trim(translate(xx2.attribute1, ' +-.0123456789', ' ')) is null 
                then to_number(xx2.attribute1) 
           end attr_num
    from xx_base_tbl xx1, xx_dtl_tbl xx2 
    where xx1.tbl_id = xx2.tbl_id) a, 
  
    xx_base_tbl b
  
  where a.attr_num = b.tbl_id (+)
  group by a.trx_num

dbfiddle 小提琴手

Two steps, in first create joining column using case when in subquery, then use it in main query.两个步骤,首先在子查询中使用case when创建连接列,然后在主查询中使用它。

Edit:编辑:

Above query may be simplified to:上面的查询可以简化为:

select xx1.trx_num,  
       listagg(xx3.trx_num, ',') within group (order by xx3.trx_num) prepayment
from xx_base_tbl xx1, xx_dtl_tbl xx2, xx_base_tbl xx3
where xx1.tbl_id = xx2.tbl_id
  and case when trim(translate(xx2.attribute1, ' +-.0123456789', ' ')) is null 
            then to_number(xx2.attribute1) 
       end = xx3.tbl_id(+)
group by xx1.trx_num

dbfiddle 小提琴手

The syntax that is used in your first query is ANSI standard joins and properly works with oracle.在您的第一个查询中使用的语法是 ANSI 标准连接,并且可以与 oracle 一起正常工作。

Second query's syntax is old SQL-92 standard and should be avoided.第二个查询的语法是旧的 SQL-92 标准,应该避免使用。 Also, it is hard to understand and handle.此外,它很难理解和处理。

By the way, your second query will need some extra clauses as follows:顺便说一句,您的第二个查询将需要一些额外的子句,如下所示:

WHERE
    xx1.tbl_id = xx2.tbl_id
    AND to_number(xx2.attribute1) = xx3.tbl_id (+)
    AND (xx3.tbl_id is null -- this extra OR condition is needed with below condition
         or ( length(TRIM(translate(xx2.attribute1, ' +-.0123456789', ' '))) IS NULL )
        ) 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM