【问题标题】:Using LISTAGG function in SQL causes error: Result size exceeds LISTAGG limit在 SQL 中使用 LISTAGG 函数会导致错误:结果大小超出 LISTAGG 限制
【发布时间】:2021-02-12 11:29:05
【问题描述】:

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

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

如何摆脱这个错误?

【问题讨论】:

  • 使用更小的字符串?错误很明显。请说明您希望如何处理太大的数据。

标签: sql amazon-redshift listagg


【解决方案1】:

请参阅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;

【讨论】:

  • 如果你有重复的值,这很好用。所以使用 distinct 将解决。但是时间呢,我们没有重复,并且在这种情况下不同是行不通的。在这种情况下我们应该怎么做?
  • @MaryamPashmi 我已经添加了解决方案,请参考这个答案 --> stackoverflow.com/a/68831944/6194097
  • 就我而言,我不能使用 distinct。我怎样才能简单地整理列表?
【解决方案2】:

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


这是我尝试执行的 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 的最大限制。根据"year_level",我们可以通过下面的SQL查询找到"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 以字节(八位字节)为单位返回字符串的长度。

如您所见,与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 functionWindow 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 functionWindow 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_bytes 切片60000 并可以查看num_of_parts 列中损坏了多少块。 'Primary Ungraded' 没有分割成任何部分,并且“二级未分级”已被分割成 2 个部分,正如我们之前调查的那样,它已被 60000 分割成多个部分。

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

【讨论】:

  • 感谢 Isuru Mahesh 抽出宝贵时间参与本期。再次非常感谢您。
猜你喜欢
  • 2021-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-30
  • 1970-01-01
  • 2016-07-25
相关资源
最近更新 更多