我有一个查询,其中我想按某些字段分组并汇总CSV字符串中的最后一个字段。如果您像我一样来自SQL Server,则可以使用FOR XML PATH('')
。但是在Oracle 12c中则完全不同:
表定义
CREATE TABLE HCF (
ID NUMBER,
HCF_DATE DATE,
HCF_TYPE_1 NUMBER,
HCF_TYPE_2 NUMBER)
样本数据
ID HCF_DATE HCF_TYPE_1 HCF_TYPE_2
272 27/02/18 1 1
279 28/02/18 15 2
280 28/02/18 15 2
283 28/02/18 5 1
我正在使用的查询
WITH CTE_HCF AS (
SELECT HCF_DATE, HCF_TYPE_1, HCF_TYPE_2, COUNT(ID)
FROM HCF
GROUP BY HCF_DATE, HCF_TYPE_1, HCF_TYPE_2
HAVING COUNT(ID) > 0
)
SELECT a.*, b.*
FROM CTE_HCF a
CROSS APPLY (
SELECT LTRIM(MAX(SYS_CONNECT_BY_PATH(ORDRE_ID,',')) KEEP (DENSE_RANK LAST ORDER BY curr),',') AS ids
FROM ( SELECT HCF_DATE, HCF_TYPE_1, HCF_TYPE_2, ID,
ROW_NUMBER() OVER (PARTITION BY HCF_DATE ORDER BY HCF_TYPE_1, HCF_TYPE_2) AS curr,
ROW_NUMBER() OVER (PARTITION BY HCF_DATE ORDER BY HCF_TYPE_1, HCF_TYPE_2) -1 AS prev
FROM CTE_HCF
WHERE HCF_DATE = a.HCF_DATE AND HCF_TYPE_1 = a.HCF_TYPE_1 AND HCF_TYPE_2 = a.HCF_TYPE_2
)
CONNECT BY prev = PRIOR curr
AND HCF_DATE = PRIOR HCF_DATE
AND HCF_TYPE_1 = PRIOR HCF_TYPE_1
AND HCF_TYPE_2 = PRIOR HCF_TYPE_2
START WITH curr = 1 ) b
错误
ORA-00904: "a"."HCF_TYPE_2" : invalid identifier
所需的输出
HCF_DATE HCF_TYPE_1 HCF_TYPE_2 IDS
27/02/18 1 1 272
28/02/18 15 2 279,280
28/02/18 5 1 283
我认为问题在于原始查询的字段在CROSS APPLY
子查询的子查询中不可见。
PS:我尝试了this article中所述的其他方法,但是由于多种原因而失败,例如LISTAGG
超过了xK个字符。而且我没有足够的特权来创建函数,XMLAGG
可能会导致您的Oracle实例崩溃。
更新
Oracle版本是:Oracle Database 12c Enterprise Edition Release 12.1.0.2.0
答案 0 :(得分:1)
如果您超出listagg
的字符数限制,则如何处理取决于您要显示的内容。
如果您使用的是12.2,则可以使用onrun truncate子句来修剪超出限制的字符。
select hcf_date, hcf_type_1, hcf_type_2,
listagg ( id, ','
on overflow truncate
) within group (
order by id
) csv
from hcf
group by hcf_date, hcf_type_1, hcf_type_2;
HCF_DATE HCF_TYPE_1 HCF_TYPE_2 CSV
27-FEB-2018 00:00:00 1 1 272
28-FEB-2018 00:00:00 5 1 283
28-FEB-2018 00:00:00 15 2 279,280
如果您使用的是12.1,则有some other workarounds。
您可以使用行模式匹配(match_recognize)查找每个额外行的CSV长度。并返回字符串限制内的那些值:
我添加了一些额外的行,并将字符数限制设置为10以显示该原理:
insert into hcf values ( 281, to_date('28/02/18', 'dd/mm/yy'), 15, 2);
insert into hcf values ( 282, to_date('28/02/18', 'dd/mm/yy'), 15, 2);
with grps as (
select *
from hcf match_recognize (
partition by hcf_date, hcf_type_1, hcf_type_2
order by id
measures
sum(lengthb(s.id) + lengthb(';')) as len
all rows per match
after match skip past last row
pattern (s+)
define
s as 1=1
)
)
select hcf_date, hcf_type_1, hcf_type_2,
listagg ( id, ',' )
within group (
order by id
) csv
from grps
where len <= 10
group by hcf_date, hcf_type_1, hcf_type_2;
HCF_DATE HCF_TYPE_1 HCF_TYPE_2 CSV
27-FEB-2018 00:00:00 1 1 272
28-FEB-2018 00:00:00 5 1 283
28-FEB-2018 00:00:00 15 2 279,280
或者,当达到字符数限制时,您可以将行分成不同的组。并将它们显示为单独的CSV:
with grps as (
select *
from hcf match_recognize (
partition by hcf_date, hcf_type_1, hcf_type_2
order by id
measures
match_number() as grp
all rows per match
after match skip past last row
pattern (s csv*)
define csv as
lengthb(s.id) + sum(lengthb(csv.id) + lengthb(';')) < = 10
)
)
select hcf_date, hcf_type_1, hcf_type_2,
listagg ( id, ',' )
within group (
order by id
) csv
from grps
group by hcf_date, hcf_type_1, hcf_type_2, grp;
HCF_DATE HCF_TYPE_1 HCF_TYPE_2 CSV
27-FEB-2018 00:00:00 1 1 272
28-FEB-2018 00:00:00 5 1 283
28-FEB-2018 00:00:00 15 2 279,280
28-FEB-2018 00:00:00 15 2 281,282
如果您要返回的整个CSV列表的长度超过了varchar2
的限制,则需要返回clob
。您可以使用XML进行哪些操作
select hcf_date, hcf_type_1, hcf_type_2,
substr (
xmlcast (
xmlagg (
xmlelement(s, ',' || id)
order by id
) as clob
), 2
) csv
from hcf
group by hcf_date, hcf_type_1, hcf_type_2;
HCF_DATE HCF_TYPE_1 HCF_TYPE_2 CSV
27-FEB-2018 00:00:00 1 1 272
28-FEB-2018 00:00:00 5 1 283
28-FEB-2018 00:00:00 15 2 279,280,281,282
答案 1 :(得分:0)
最后,我设法要求DBA创建一个将CSV中的id
值连接起来的函数,我个人认为这是返回CLOB
值的最佳方法。
功能:
create or replace FUNCTION concatenate_list (p_cursor IN SYS_REFCURSOR)
RETURN CLOB
IS
l_return CLOB;
l_temp CLOB;
BEGIN
LOOP
FETCH p_cursor
INTO l_temp;
EXIT WHEN p_cursor%NOTFOUND;
l_return := l_return || ',' || l_temp;
END LOOP;
RETURN LTRIM(l_return, ',');
END;
查询
WITH CTE_HCF AS (
SELECT HCF_DATE, HCF_TYPE_1, HCF_TYPE_2, COUNT(ID)
FROM HCF
GROUP BY HCF_DATE, HCF_TYPE_1, HCF_TYPE_2
HAVING COUNT(ID) > 0
)
SELECT a.*
, concatenate_list(CURSOR(SELECT id FROM HCF WHERE HCF_DATE = a.HCF_DATE AND HCF_TYPE_1 = a.HCF_TYPE_1 AND HCF_TYPE_2 = a.HCF_TYPE_2)) AS CSV
FROM CTE_HCF a
PS:如果您不需要CLOB
,那么LISTAGG
是您的最佳选择。