管理 LISTAGG on Amazon Redshift 中的溢出

Managing overflows in LISTAGG on Amazon Redshift

使用此 post 中的示例:https://blogs.oracle.com/datawarehousing/entry/managing_overflows_in_listagg

以下语句:

SELECT
deptno,
LISTAGG(ename, ';') WITHIN GROUP (ORDER BY empno) AS namelist 
FROM emp 
GROUP BY deptno;

将生成以下输出:

DEPTNO     NAMELIST
---------- ----------------------------------------
10         CLARK;KING;MILLER
20         SMITH;JONES;SCOTT;ADAMS;FORD
30         ALLEN;WARD;MARTIN;BLAKE;TURNER;JAMES

让我们假设上面的语句不 运行 并且我们的 LISTAGG 函数中的每一行可以返回 15 个字符的限制。这实际上是 Amazon Redshift 上的 65535。

在这种情况下,我们希望返回以下内容:

DEPTNO     NAMELIST
---------- ----------------------------------------
10         CLARK;KING
10         MILLER
20         SMITH;JONES
20         SCOTT;ADAMS
20         FORD
30         ALLEN;WARD
30         MARTIN;BLAKE
30         TURNER;JAMES 

在 Amazon Redshift 中重新创建此结果以避免任何数据丢失并考虑速度的最佳方法是什么?

可以创建部分列表,然后将其余值一次性创建为单独的行,但如果行数不受限制,您确实需要一个循环语句,然后将其转换为列表,并且剩余的行等等。

所以这确实是 Apache Spark(或任何其他 map-reduce 技术)的任务。

可以通过 2 个子查询实现:

第一个:

SELECT id, field,
       sum(length(field) + 1) over 
       (partition by id order by RANDOM() rows unbounded preceding) as total_length_now
    from my_schema.my_table)

最初我们想计算 table 中每个 id 有多少个字符。我们可以使用 window 函数为每一行递增地计算它。在 'order by' 语句中,您可以使用您拥有的任何唯一字段。如果你没有,你可以简单地使用随机或哈希函数,但强制该字段是唯一的,如果没有,该函数将无法正常工作。

长度中的“+1”表示我们将在 listagg 函数中使用的分号。

第二个:

SELECT id, field, total_length_now / 65535 as sub_id
FROM (sub_query_1)

现在我们根据之前计算的长度创建一个sub_id。如果 total_length_now 超过限制大小(在本例中为 65535),该部门的其余部分将 return 一个新的 sub_id.

最后一步

SELECT id, sub_id, listagg(field, ';') as namelist
FROM (sub_query_2)
GROUP BY id, sub_id
ORDER BY id, sub_id

现在我们可以简单地调用listagg函数按id分组sub_id,因为每个组不能超过大小限制

完成查询

SELECT id, sub_id, listagg(field, ';') as namelist
FROM (
SELECT id, field, total_length_now / 65535 as sub_id
FROM (SELECT id,
       field,
       sum(length(field) + 1) over 
       (partition by id order by field rows unbounded preceding) as total_length_now
from support.test))
GROUP BY id, sub_id
order by id, sub_id

您的数据示例(大小限制 = 10)

第一次和第二次查询输出:

id, field, total_length_now, sub_id

10,KING,5,0
10,CLARK,11,1
10,MILLER,18,1
20,ADAMS,6,0
20,SMITH,12,1
20,JONES,18,1
20,FORD,23,2
20,SCOTT,29,2
30,JAMES,6,0
30,BLAKE,12,1
30,WARD,17,1
30,MARTIN,24,2
30,TURNER,31,3
30,ALLEN,37,3

最终查询输出:

id,sub_id,namelist

10,0,KING
10,1,CLARK;MILLER
20,0,ADAMS
20,1,SMITH;JONES
20,2,FORD;SCOTT
30,0,JAMES
30,1,BLAKE;WARD
30,2,MARTIN
30,3,TURNER;ALLEN