代码之家  ›  专栏  ›  技术社区  ›  Migs Isip

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

  •  0
  • Migs Isip  · 技术社区  · 5 年前

    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');
    

    我正在使用下面的ANSI语法将xx_dtl_tbl外部连接到另一个xx_base_tbl仅显示预付款和数字值的类别。然后使用 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
    

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

    然而,当我使用 SQL Developer's tool to change the syntax to 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
        
    

    结果改变了:

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

    trx 1行突然丢失。如何用Oracle语法编写此代码?

    0 回复  |  直到 5 年前
        1
  •  2
  •   Ponder Stibbons    5 年前

    这个查询很难转换为旧语法。对我起作用的是:

    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

    两个步骤,在第一步中,使用 case when 在子查询中,然后在主查询中使用它。

    编辑:

    上述查询可简化为:

    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

        2
  •  0
  •   Popeye    5 年前

    第一个查询中使用的语法是ANSI标准联接,可与oracle正常工作。

    第二个查询的语法是旧的SQL-92标准,应该避免使用。而且,它很难理解和处理。

    顺便说一下,您的第二个查询需要一些额外的子句,如下所示:

    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 )
            )