【问题标题】:Managing overflows in LISTAGG on Amazon Redshift在 Amazon Redshift 上管理 LISTAGG 中的溢出
【发布时间】:2016-06-27 06:58:23
【问题描述】:

使用这篇文章中的示例: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 中重新创建此结果以避免任何数据丢失并考虑速度的最佳方法是什么?

【问题讨论】:

  • 我想建议对 Hyruma92 的解决方案进行修改。因为有些字符超过 1 个字节,所以最好使用返回字节数的OCTET_LENGTH,而不是返回字符数的 LENGTH。

标签: amazon-redshift listagg


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    这可以通过 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)
    

    最初,我们想计算表中每个 id 有多少个字符。我们可以使用窗口函数对每一行进行增量计算。在“order by”语句中,您可以使用您拥有的任何唯一字段。如果你没有,你可以简单地使用随机或散列函数,但强制该字段是唯一的,如果没有,该函数将无法按我们想要的方式工作。

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

    第二:

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

    现在我们根据之前计算的长度创建一个 sub_id。如果 total_length_now 超过限制大小(在本例中为 65535),则除法的其余部分将返回一个新的 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
    

    【讨论】:

    • 快 5 年了,这看起来不错!在接受您的回答之前,我会再开放一周。
    • 谢谢,我一周前才看到你的问题,很好:)
    • 不错的解决方案,我碰巧需要什么...但 RedShift 需要我们求助于这个...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-23
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多