【问题标题】:Is there a way to continue on the next row after listagg() runs out of space (>4k)?listagg() 空间不足(> 4k)后有没有办法继续下一行?
【发布时间】:2021-03-20 16:12:16
【问题描述】:

我想将每周包含多个值的表格更改为每周将所有值放在一行中。但是当我使用 listagg() 时,我的空间不足。我无法更改最大字符串大小(ALTER SYSTEM SET max_string_size=extended SCOPE= SPFILE),并且我不想截断生成的字符串。但是我可以继续下一行,但我不知道该怎么做。例如,如果我的输入是:

Week                   SKU
202001                 598441
202001                 846541
202001                 77557
202001                 57813
202001                 5741651
202001                 21684135
202001                 5451516
202001                 545129
202001                 98754123
202001                 5644242
202002                 68454155
...

假设输入文件足够长,它会触发错误:

01489. 00000 -  "result of string concatenation is too long"
*Cause:    String concatenation result is more than the maximum size.
*Action:   Make sure that the result is less than the maximum size.

那我想要一段类似这样的代码:

select 
  weeknr, 
  ''''||listagg(sku, ''',''', ON OVERFLOW NEXT LINE) within group(order by weeknr)||'''' sku_numbers
from 
  input_table
group by 
  weeknr

这样我的输出将如下所示:

Week                   SKU
202001                 '598441','846541','77557','57813','5741651','21684135'
202001                 '5451516','545129','98754123','5644242'
202002                 '68454155',...
...

这可能吗?非常感谢任何帮助!

【问题讨论】:

  • this 对您的案件有帮助吗?
  • 感谢您的建议,但我尝试了 XML,但由于引号,它对我不起作用。
  • 不客气,我已经用 XMLELEMENT 修复了那里的部分,谢谢。希望你也尝试过。

标签: sql oracle listagg


【解决方案1】:

您可以使用解析函数找到字符串长度的SUM,然后将其用于子分组,如下所示:

SELECT WEEKNR,
       '''' || LISTAGG(SKU, ''',''') WITHIN GROUP(ORDER BY WEEKNR) || '''' SKU_NUMBERS
  FROM (
    SELECT WEEKNR,
           FLOOR(SUM(LENGTH(SKU) + 3) 
                   OVER(PARTITION BY WEEKNR ORDER BY SKU) / 4000) AS GROUP_NUMBER,
           SKU
      FROM INPUT_TABLE
)
 GROUP BY WEEKNR, GROUP_NUMBER

【讨论】:

  • 这仍然可以超过 4000 个字符,因为您不包括计数中 LISTAGG 中使用的分隔符。 db<>fiddle (并且LENGTH 拼写错误).
  • 啊哈!是的,你是对的。但这可以是方法,可以根据需要使用。像SUM(LENGTH(SKU)+3) 而不是 4000,我们也可以使用 3800 为任何此类额外字符制作 200 个字符的缓冲区。类似的东西可能是解决方案。
  • 感谢您的回答!我将SUM(LENGTH(SKU)+3) 调整为SUM(LENGTH(SKU)+4) 以确保所有角色都包含在内,它就像做梦一样!太棒了,谢谢!
  • 不错的答案!只想说,对于更通用的解决方案,您需要LENGTHB,以防输入包含多字节字符。
【解决方案2】:

您可以使用MATCH_RECOGNIZE 执行逐行检查,以查看字符串是否超过 4000 个字符并将值分配给少于该字符的组,以便您可以使用LISTAGG

SELECT week,
       ''''||LISTAGG( sku, ''',''' ) WITHIN GROUP ( ORDER BY sku )||'''' AS skus
FROM   table_name
MATCH_RECOGNIZE (
  PARTITION BY Week
  ORDER     BY SKU
  MEASURES
    MATCH_NUMBER() AS mno
  ALL ROWS PER MATCH
  PATTERN ( concatenated_string+ )
  DEFINE
    concatenated_string AS SUM(LENGTH(SKU)+3)-1 <= 4000
)
GROUP BY week, mno

其中,对于样本数据:

CREATE TABLE table_name ( Week, SKU ) AS
SELECT *
FROM   (
  SELECT 202001 AS week, 10000000+LEVEL AS sku FROM DUAL CONNECT BY LEVEL <= 500
  UNION ALL
  SELECT 202002, 10000000+LEVEL FROM DUAL CONNECT BY LEVEL <= 100
)
ORDER BY week, DBMS_RANDOM.VALUE; -- put the rows into a random order in the table.

输出:

周 |品类 -----: | :------------------------------------------------ -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ------------------------------------------ 202001 | '10000001','10000002','10000003','10000004','10000005','10000006','10000007','10000008','10000009','10000010','1000010011','000000, ','10000014','10000015','10000016','10000017','10000018','10000019','10000020','10000021','10000022','10000023','100000024','025','0 '10000026','10000027','10000028','10000029','10000030','10000031','10000032','10000033','10000034','10000035','1000303036','00000, ','10000039','10000040','10000041','10000042','10000043','10000044','10000045','10000046','10000047','10000048','100000049','1 '10000051','10000052','10000053','10000054','10000055','10000056','10000057','10000058','10000059','10000060','10000606261','100000, ','10000064','10000065','10000066','10000067','10000068','10000069','10000070','10000071','10000072','10000073','10000074','075 '10000076','10000077','10000078','10000079','10000080','10000081','10000082','10000083','10000084','10000084','10000085','10000886','100000, ','10000089','10000090','10000091' ,'10000092','10000093','10000094','10000095','10000096','10000097','10000098','10000099','10000100','10000101','100000102',''100 10000104','10000105','10000106','10000107','10000108','10000109','10000110','10000111','10000112','10000113','1000016514','100'10, ,'10000117','10000118','10000119','10000120','10000121','10000122','10000123','10000124','10000125','10000126','1000027','108 10000129','10000130','10000131','10000132','10000133','10000134','10000135','10000136','10000137','10000138','1000010139','10000000, ,'10000142','10000143','10000144','10000145','10000146','10000147','10000148','10000149','10000150','10000151','100005152','100 10000154','10000155','10000156','10000157','10000158','10000159','10000160','10000161','10000162','10000163','100006564','100'10000, ,'10000167','10000168','10000169','10000170','10000171','10000172','10000173','10000174','10000175','10000176','10000177','100 10000179','10000180','10000181','10000182 ','10000183','10000184','10000185','10000186','10000187','10000188','10000189','10000190','10000191','10000192','10000193','194 '10000195','10000196','10000197','10000198','10000199','10000200','10000201','10000202','10000203','10000204','1000070205','10000000, ','10000208','10000209','10000210','10000211','10000212','10000213','10000214','10000215','10000216','10000217','10000218','10 '10000220','10000221','10000222','10000223','10000224','10000225','10000226','10000227','10000228','10000229','100020230','100000, ','10000233','10000234','10000235','10000236','10000237','10000238','10000239','10000240','10000241','10000242','100000243','100 '10000245','10000246','10000247','10000248','10000249','10000250','10000251','10000252','10000253','10000254','1000070255','00000, ','10000258','10000259','10000260','10000261','10000262','10000263','10000264','10000265','10000266','10000267','10000268','100 '10000270','10000271','10000272','1000027 3','10000274','10000275','10000276','10000277','10000278','10000279','10000280','10000281','10000282','10000283','100000284'5'1 ,'10000286','10000287','10000288','10000289','10000290','10000291','10000292','10000293','10000294','10000295','10000297','100 10000298','10000299','10000300','10000301','10000302','10000303','10000304','10000305','10000306','10000307','100000308','1000300, ,'10000311','10000312','10000313','10000314','10000315','10000316','10000317','10000318','10000319','10000320','100030321','10 10000323','10000324','10000325','10000326','10000327','10000328','10000329','10000330','10000331','10000332','100003333','100030', ,'10000336','10000337','10000338','10000339','10000340','10000341','10000342','10000343','10000344','10000345','100030346','100 10000348','10000349','10000350','10000351','10000352','10000353','10000354','10000355','10000356','10000357','10000358','1000300', ,'10000361','10000362','10000363' 202001 | '10000364','10000365','10000366','10000367','10000368','10000369','10000370','10000371','10000372','10000373','100070374','100000000, ','10000377','10000378','10000379','10000380','10000381','10000382','10000383','10000384','10000385','10000386','100000387','10 '10000389','10000390','10000391','10000392','10000393','10000394','10000395','10000396','10000397','10000398','1000100399','100004' ','10000402','10000403','10000404','10000405','10000406','10000407','10000408','10000409','10000410','10000411','100000412','13 '10000414','10000415','10000416','10000417','10000418','10000419','10000420','10000421','10000422','10000423','1000402524','10000000, ','10000427','10000428','10000429','10000430','10000431','10000432','10000433','10000434','10000435','10000436','100000437','108 '10000439','10000440','10000441','10000442','10000443','10000444','10000445','10000446','10000447','10000448','1000450449','00000, ','10000452','10000453','10000454' ,'10000455','10000456','10000457','10000458','10000459','10000460','10000461','10000462','10000463','10000464','10004665','100 10000467','10000468','10000469','10000470','10000471','10000472','10000473','10000474','10000475','10000476','10000477','1000400'700 ,'10000480','10000481','10000482','10000483','10000484','10000485','10000486','10000487','10000488','10000489','100040490','100 10000492','10000493','10000494','10000495','10000496','10000497','10000498','10000499','10000500' 202002 | '10000001','10000002','10000003','10000004','10000005','10000006','10000007','10000008','10000009','10000010','1000010011','000000, ','10000014','10000015','10000016','10000017','10000018','10000019','10000020','10000021','10000022','10000023','100000024','025','0 '10000026','10000027','10000028','10000029','10000030','10000031','10000032','10000033','10000034','10000035','1000303036','00000, ','10000039','10000040','10000041','10000042','10000043','10000044','10000045','10000046','10000047','10000048','100000049','1 '10000051','10000052','10000053','10000054','10000055','10000056','10000057','10000058','10000059','10000060','10000606261','100000, ','10000064','10000065','10000066','10000067','10000068','10000069','10000070','10000071','10000072','10000073','10000074','075 '10000076','10000077','10000078','10000079','10000080','10000081','10000082','10000083','10000084','10000084','10000085','10000886','100000, ','10000089','10000090','10000091' ,'10000092','10000093','10000094','10000095','10000096','10000097','10000098','10000099','10000100'

db小提琴here

【讨论】:

  • 感谢您的回答!它非常优雅!我最终使用了大力水手的答案,因为我明白那里做得更好,但我也很喜欢这个答案!
【解决方案3】:

由于LISTAGG 有4000 个字符的限制,请切换到XML。我认为你不能“强迫”LISTAGG 采取不同的行动。

SQL> SELECT LISTAGG (dname, ', ') WITHIN GROUP (ORDER BY dname) result FROM dept;

RESULT
--------------------------------------------------------------------------------
ACCOUNTING, OPERATIONS, RESEARCH, SALES

SQL>
SQL> SELECT RTRIM (
  2            XMLAGG (XMLELEMENT (e, dname || ', ') ORDER BY dname).EXTRACT (
  3               '//text()'),
  4            ', ') result
  5    FROM dept;

RESULT
--------------------------------------------------------------------------------
ACCOUNTING, OPERATIONS, RESEARCH, SALES

SQL>

【讨论】:

  • 嗨,谢谢。我尝试使用 XML,但不幸的是我需要引号中的每个值,当我使用 XML 时,我得到了一个奇怪的引号符号。不过感谢您的分析!
【解决方案4】:

您可以创建自己的函数:

CREATE OR REPLACE TYPE VARCHAR_TABLE_TYPE AS TABLE OF VARCHAR2(30000);

CREATE OR REPLACE FUNCTION JoinTable(tab IN VARCHAR_TABLE_TYPE, Joiner IN VARCHAR2) RETURN VARCHAR2 IS
    res VARCHAR2(30000); -- or even: res CLOB;
BEGIN
    IF tab IS NULL THEN 
        RETURN NULL; 
    END IF;
    IF tab.COUNT = 0 THEN 
        RETURN NULL; 
    END IF;
    
    FOR i IN tab.FIRST..tab.LAST-1 LOOP 
        res := res || tab(i) || Joiner; 
    END LOOP;
    RETURN res || tab(tab.LAST);
END JoinTable;

那么你可以这样使用它:

SELECT 
   JoinTable(
      CAST(MULTISET(SELECT sku FROM input_table order by weeknr) AS VARCHAR_TABLE_TYPE),
      ','
   ) as sku_numbers
from dual

【讨论】:

  • 感谢您的回答。我不认为函数对我来说是解决这个特定问题的最佳解决方案,但我非常感谢您的意见!谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-09
  • 2013-02-23
  • 2011-06-20
  • 2011-09-03
  • 1970-01-01
相关资源
最近更新 更多