在 SQL 中使用 LISTAGG 函数会导致错误:结果大小超出 LISTAGG 限制

Aad*_*que 3 sql amazon-redshift listagg

我试图LISTAGG在 SQL 中使用该函数,但遇到以下错误:

无效操作:结果大小超出 LISTAGG 限制详细信息:
---------- 错误:结果大小超出 LISTAGG 限制代码:8 ...

我该如何摆脱这个错误?

Era*_*per 7

请参阅https://docs.aws.amazon.com/redshift/latest/dg/r_LISTAGG.html上的 ListAgg 函数文档

返回数据类型为 varchar(max),即 64K varchar 大小

你所描述的错误在官方文档中确实提到了

您可以考虑将 ListAgg() 函数与 Distinct 一起使用,如下所示,以减少要连接的项目

select listagg(distinct sellerid, ', ') within group (order by sellerid) from sales
where eventid = 4337;
Run Code Online (Sandbox Code Playgroud)


Kus*_*era 7

这就是我们遇到问题的原因。

在此输入图像描述


这是我尝试执行的 SQL 查询,

SELECT DISTINCT "year_level", LISTAGG("value", ', ')  WITHIN GROUP (ORDER BY "year_level") OVER (PARTITION BY "year_level")
FROM "school_1__acara_db"."acara_data_set";
Run Code Online (Sandbox Code Playgroud)

这是我得到的错误。

ERROR: Result size exceeds LISTAGG limit Detail: ----------------------------------------------- error: Result size exceeds LISTAGG limit code: 8001 context: LISTAGG limit: 65535 query: 4360256 location: string_ops.cpp:116 process: query1_127_4360256 [pid=1793] -----------------------------------------------
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


让我们把这个问题分成几个小部分。正如他们提到的,我已经超出了最大限制LISTAGG。我们可以通过下面的 SQL 查询找到"value"根据 列中超出的值"year_level"。

SELECT "year_level", SUM(OCTET_LENGTH("value")) as total_bytes
FROM "school_1__acara_db"."acara_data_set"
GROUP BY "year_level"
ORDER BY total_bytes;
Run Code Online (Sandbox Code Playgroud)

这是输出。

在此输入图像描述

OCTET_LENGTH返回字符串的长度(以字节为单位)。

Primary Ungraded正如您所看到的,与总字节数50329和Secondary Ungraded字节数相关的值61178。两者均未超过VARCHAR(MAX),65535限制。至少我可以获取LISTAGG上述两条记录的值吗?这是我要执行的查询,

SELECT DISTINCT "year_level", LISTAGG("value", ', ')  WITHIN GROUP (ORDER BY "year_level") OVER (PARTITION BY "year_level")
FROM "school_1__acara_db"."acara_data_set"
WHERE "year_level" IN ('Primary Ungraded', 'Secondary Ungraded');
Run Code Online (Sandbox Code Playgroud)

我遇到了同样的错误,Result size exceeds LISTAGG limit Detail: -----------. 正如我们在上面的结果中看到的,它没有超出VARCHAR(MAX)限制65535。但为什么?让我们看看与以下查询"value"相关的列数。"year_level"

SELECT "year_level", COUNT("value") as total_counts
FROM "school_1__acara_db"."acara_data_set"
GROUP BY "year_level"
ORDER BY total_counts;
Run Code Online (Sandbox Code Playgroud)

这是输出。

在此输入图像描述

在进行进一步解释之前,让我们看看如何LISTAGG工作。


在 Redshift 中,LISTAGG可用作聚合函数或窗口函数,它将多行数据转换为由指定分隔符分隔的单个值列表。对于下面的示例,分隔符是, (逗号加空格)。

下图取自Oracle的Listagg函数 - 使用和去重文章,与Oracle相关,但可以了解该LISTAGG函数的基本概念。

在此输入图像描述

这就是数据与分隔符合并的方式。


甚至我们的查询也使用分隔符, (逗号加空格)。我们以下图中的第一条记录为例。

在此输入图像描述

"Primary Ungraded"3412记录由50329总字节组成。所以这意味着我们要将3412记录合并到单个列中。当我们合并时,它应该有50329的总字节数。但我们不是直接合并,而是与分隔符合并。3411所以记录之间有分隔符3412。

delimiter_count = no_of_records - 1
Run Code Online (Sandbox Code Playgroud)

如果不明白,请检查Example #1数据如何合并的图像。因此,根据此,当我们在没有分隔符的情况下运行最后一个失败的查询时,它应该可以工作。

SELECT DISTINCT "year_level", LISTAGG("value", '')  WITHIN GROUP (ORDER BY "year_level") OVER (PARTITION BY "year_level")
FROM "school_1__acara_db"."acara_data_set"
WHERE "year_level" IN ('Primary Ungraded', 'Secondary Ungraded')
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

是的,工作正常。但这对于其他记录不起作用,因为在不执行任何操作的情况下VARCHAR(MAX),它们已经超出了65535限制。


很多人建议将DISTINCT关键字与LISTAGG函数一起使用。聚合函数和窗口函数都支持DISTINCT关键字作为可选。

聚合函数

LISTAGG( [DISTINCT] aggregate_expression [, 'delimiter' ] ) 
[ WITHIN GROUP (ORDER BY order_list) ]   
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

窗函数

LISTAGG( [DISTINCT] expression [, 'delimiter' ] ) 
[ WITHIN GROUP (ORDER BY order_list) ] 
OVER ( [PARTITION BY partition_expression] )     
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

没有重复,我的数据超出了VARCHAR(MAX)限制65535。所以我不能在我的情况下使用它。


我们不能将数据分割成更小的部分吗?是的,我们可以使用下面的查询来做到这一点,不要混淆,下面的解决方案是由我的一位队友 Isuru 找到的。

SELECT year_level, num_of_parts, listagg(value,',') AS listagg_data FROM (
    SELECT year_level, value, total_bytes / 60000 AS num_of_parts FROM (
        SELECT year_level, value, SUM(OCTET_LENGTH(value)) OVER (PARTION BY year_level ORDER BY value ROWS UNBOUNDED PRECEDING) AS total_bytes
            FROM "school_1__acara_db"."acara_data_set"
        )
)
GROUP BY year_level, num_of_parts
ORDER BY year_level, num_of_parts;
Run Code Online (Sandbox Code Playgroud)

这是输出。

在此输入图像描述

使用它您可以获取所有您想要的信息。total_bytes在这里,我们对旁边进行切片60000,可以看到列中有多少碎片被破坏num_of_parts。'Primary Ungraded'没有分割成任何部分,并且“中学未分级”已被分割成 2 个部分,正如我们之前调查的那样,同样它已被 分割成多个部分60000。

我们遇到的问题是数据库限制。因此,我们可以通过编程将这些LISTAGG值合并到一个位置。目前我没有看到任何其他解决方案,或者在整个互联网上找不到任何合适的解决方案。