Oracle SQL: Return 没有 listagg 结果时的空值

Oracle SQL: Return null value when no listagg result

我正在使用以下脚本来 return 一些基本信息。脚本 returns 65 行(如预期)...

select unique
       trunc(li.cre_dat) cre_date,
       li.cre_usr,
       li.catnr,
       li.av_part_no,
       li.artist,
       li.title,
       li.prodtyp,
       li.packtyp,
       nvl(sp.name_for_customer,sp.name) pack_type 
from   leos_item li,
       scm_packtyp sp
where  li.cunr in ('816900','816901','816902')
and    li.item_type = 'FP'
and    li.av_part_no is null
and    trunc(li.cre_dat) >= '01-JAN-2016'
and    li.model_force_creation_idc != 'Y'
and    li.i_status != 'I'
and    li.packtyp = sp.packtyp

...但是,当我将 Listagg 添加到我的选择中时,报告只有 returns 55 行。 10 行没有 listagg 结果,因此从结果中省略...

select unique
       trunc(li.cre_dat) cre_date,
       li.cre_usr,
       li.catnr,
       li.av_part_no,
       li.artist,
       li.title,
       li.prodtyp,
       li.packtyp,
       nvl(sp.name_for_customer,sp.name) pack_type,
       regexp_replace(listagg(nvl(bom.av_part_no,'No'), ', ')
         within group (order by bom.item_id),'([^,]+)(,)+', '') masters
from   leos_item li,
       scm_packtyp sp,
       TABLE(leos_flatbom_pkg.GetFlatBOM(li.item_id)) bom,
       leos_item li1 
where  li.cunr in ('816900','816901','816902')
and    li.item_type = 'FP'
and    li.av_part_no is null
and    trunc(li.cre_dat) >= '01-JAN-2016'
and    li.model_force_creation_idc != 'Y'
and    li.i_status != 'I'
and    li.packtyp = sp.packtyp
and    bom.item_id = li1.item_id
and    li1.item_type = 'MT'
group by li.cre_dat,
         li.cre_usr,
         li.catnr,
         li.av_part_no,
         li.artist,
         li.title,
         li.prodtyp,
         li.packtyp,
         nvl(sp.name_for_customer,sp.name)

但是,我还需要查看这些行。有没有办法 return 没有找到 listagg 结果的 10 行。我尝试了以下 nullif 和 nvl 的组合,但没有成功;

nullif(regexp_replace(listagg(nvl(bom.av_part_no,'No'), ', ') within group (order by bom.item_id),'([^,]+)(,)+', ''),'No Master')

nvl(regexp_replace(listagg(nvl(bom.av_part_no,'No'), ', ') within group (order by bom.item_id),'([^,]+)(,)+', ''),'No Master')

问题不在于 LISTAGG,而是您正在对 leos_flatbom_pkg.GetFlatBOM(li.item_id)TABLE 集合表达式执行内部联接并且包含零行,因此父行被过滤结果出来了。

将其替换为类似这样的相关子查询:

select unique
       trunc(li.cre_dat) cre_date,
       li.cre_usr,
       li.catnr,
       li.av_part_no,
       li.artist,
       li.title,
       li.prodtyp,
       li.packtyp,
       nvl(sp.name_for_customer,sp.name) pack_type,
       ( SELECT regexp_replace(listagg(nvl(bom.av_part_no,'No'), ', ')
                  within group (order by bom.item_id),'([^,]+)(,)+', '')
         FROM   TABLE( leos_flatbom_pkg.GetFlatBOM(li.item_id)) bom
                INNER JOIN leos_item li1
                ON ( bom.item_id = li1.item_id )
         WHERE  li1.item_type = 'MT'
       ) masters
from   leos_item li
       INNER JOIN
       scm_packtyp sp
       ON ( li.packtyp = sp.packtyp )
where  li.cunr in ('816900','816901','816902')
and    li.item_type = 'FP'
and    li.av_part_no is null
and    li.cre_dat >= DATE '2016-01-01'
and    li.model_force_creation_idc != 'Y'
and    li.i_status != 'I'

其他几点:

  • 请更改为使用 ANSI 连接语法。旧的 Oracle 逗号连接语法很难知道列是如何连接的(尤其是外部连接),因为连接条件隐藏在 WHERE 子句中。
  • 不要对日期使用字符串文字(即 '01-JAN-2016')Oracle 将使用 NLS_DATE_FORMAT 会话参数作为格式掩码对它们执行隐式 TO_DATE(),如果这一旦发生变化,查询就会中断(无需更改查询的文本)并且调试起来会很痛苦。更糟糕的是,这是一个会话参数,因此一个用户可以更改它,然后它将对其他人有效,而不是对他们有效。使用日期文字(即 DATE '2016-01-01')或显式调用 TO_DATE() 并提供格式掩码并更正 NLS 设置。
  • li.cre_dat >= TRUNC( li.cre_dat ) 所以如果 TRUNC( li.cre_dat ) >= DATE '2016-01-01' 为真那么 li.cre_dat >= DATE '2016-01-01' 也将为真。结论是您可以消除 TRUNC() 调用。

您也可以使用第二个查询解决它,并将 TABLE 集合表达式转换为 OUTER JOIN:

from   leos_item li
       scm_packtyp sp,
       TABLE(leos_flatbom_pkg.GetFlatBOM(li.item_id)) (+) bom,
       leos_item li1
where  ...
and    li.packtyp = sp.packtyp
and    li1.item_id = bom.item_id (+)