Using LISTAGG function in SQL causes error: Result size exceeds LISTAGG limit

Using LISTAGG function in SQL causes error: Result size exceeds LISTAGG limit

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

Invalid operation: Result size exceeds LISTAGG limit Details:
----------- error: Result size exceeds LISTAGG limit code: 8 ...

如何摆脱这个错误?

请参阅 https://docs.aws.amazon.com/redshift/latest/dg/r_LISTAGG.html

处的 ListAgg 函数文档

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

你描述的错误在官方文档中完全有提到

你可以考虑使用 ListAgg() 函数和 Distinct 如下来减少要连接的项目

select listagg(distinct sellerid, ', ') within group (order by sellerid) from sales
where eventid = 4337;

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


这是我尝试执行的 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";

这是我得到的错误。

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] -----------------------------------------------


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

找到 "value" 列中超出的值
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;

这是输出。

OCTET_LENGTH returns 以字节(八位字节)为单位的字符串长度。

如您所见,与 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');

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

SELECT "year_level", COUNT("value") as total_counts
FROM "school_1__acara_db"."acara_data_set"
GROUP BY "year_level"
ORDER BY total_counts;

这是输出。

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


在 Redshift 中,LISTAGG 可用作 Aggregate function or a Window function,它将来自多行的数据转换为由指定分隔符分隔的单个值列表。对于下面的示例,分隔符是 , (带空格的逗号)。

下图摘自Oracle's Listagg Function - Uses and Duplicate Removal文章,与Oracle有关,但可以得到LISTAGG函数的基本概念。

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


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

"Primary Ungraded"3412 条记录,总字节数为 50329。所以这意味着我们要将 3412 条记录合并到一个列中。当我们合并时,它应该有 50329 的总字节数。但是我们不是直接合并,我们是用分隔符合并。因此 3412 条记录之间有 3411 个分隔符。

delimiter_count = no_of_records - 1

如果不明白,请查看 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')

是的,它工作正常。但这对其他记录不起作用,因为它们不做任何事情就已经超过了 VARCHAR(MAX)65535 限制。


很多人建议在 LISTAGG 函数中使用 DISTINCT 关键字。 Aggregate function and Window function 都支持 DISTINCT 关键字作为可选。

Aggregate function

LISTAGG( [DISTINCT] aggregate_expression [, 'delimiter' ] ) 
[ WITHIN GROUP (ORDER BY order_list) ]   

Window function

LISTAGG( [DISTINCT] expression [, 'delimiter' ] ) 
[ WITHIN GROUP (ORDER BY order_list) ] 
OVER ( [PARTITION BY partition_expression] )     

没有重复,我有超过 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;

这是输出。

使用它你可以获取所有你想要的信息。在这里,我们将 total_bytes60000 切片,可以看到在 num_of_parts 列中有多少块被破坏了。 'Primary Ungraded' 没有分割成任何部分,'Secondary Ungraded' 被分割成两部分,正如我们之前调查的那样,同样它被 60000 分割成多个部分。

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