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 (+)
我正在使用以下脚本来 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 (+)