在 Redshift 中连接字符串的递归 CTE 替代方案
Posted
技术标签:
【中文标题】在 Redshift 中连接字符串的递归 CTE 替代方案【英文标题】:Recursive CTE Alternative for Concatenating Strings in Redshift 【发布时间】:2020-05-18 11:21:11 【问题描述】:我有一个表,其中有 4 列,即:Book_no,Prev_Book_no(上一版 book_no),Edition_no(从 0 开始到最新版 no(以 1 递增)),Tree(最初为 null)。
我打算做的是从最高版本到第0版创建一棵Book_no树。
例如: (Book_no,Prev_Book_no,Edition_no) 值: (bbb,null,0), (ccc,bbb,1), (ddd,ccc,2), (eee,ddd,3), (fff,eee,4)
那么每一行的树应该是:(bbb),(ccc,bbb),(ddd,ccc,bbb),(eee,ddd,ccc,bbb) 和 (fff,eee,ddd,ccc,bbb) .
为此,我尝试使用递归 CTE,代码如下:
with cte(book_no,prev_book_no,tree,edition_no) as
(select
book_no,prev_book_no,tree,edition_no
from books
where edition_no>=0
union all
select e.book_no,e.prev_book_no,concat(nvl(e.tree,''),','+e.prev_book_no),e.edition_no
from
cte e inner join books f
on e.prev_book_no=f.book_no
)
select distinct * from cte
order by edition_no;
但是 Redshift 不允许在 from 子句中使用 cte table 并给出错误。 '表 cte 不存在'。
P.S 表中有多种不同的书。例如我只提到了一本。
在 Redshift 中是否有任何替代方案?
编辑:
样本数据:
预期输出:
逻辑:我想获得书号的层次结构,并且正在使用 concat 操作。(当前代码可能是错误的) 谢谢!
【问题讨论】:
请提供示例数据、期望的结果以及您要实现的逻辑的清晰说明。 @GordonLinoff 我已经添加了相关图片,并试图解释我打算做什么。 【参考方案1】:从 2021 年 4 月 29 日开始,Redshift 现在使用 WITH RECURSIVE 语法支持递归 CTE:
WITH RECURSIVE cte(book_no, prev_book_no, tree, edition_no) AS (
SELECT book_no, prev_book_no, tree, edition_no
FROM books
WHERE edition_no >= 0
UNION ALL
SELECT e.book_no, e.prev_book_no, NVL(e.tree,'')||','||e.prev_book_no), e.edition_no
FROM cte e
INNER JOIN books f ON e.prev_book_no = f.book_no)
SELECT DISTINCT *
FROM cte
ORDER BY
edition_no;
https://aws.amazon.com/about-aws/whats-new/2021/04/amazon-redshift-announces-support-for-heirarchical-data-queries-with-recursive-cte/ https://docs.aws.amazon.com/redshift/latest/dg/r_WITH_clause.html#r_WITH_clause-recursive-cte
【讨论】:
【参考方案2】:您正确选择了递归 CTE。不幸的是,Redshift does not support them。另见this similar question。我不知道任何可以被视为全值解决方法的 SQL 功能。也许如果级别的数量实际上是有限的,您可以使用有限数量的左自连接(丑陋,我知道)。
(对于您的 CTE - 虽然有点偏离主题 - 似乎条件 where edition_no=0
足以作为联合第一部分的种子,第二个查询中的连接条件也应转换为 on f.prev_book_no=e.book_no
。)
【讨论】:
谢谢!但关卡数量不限,因书而异。【参考方案3】:Amazon Redshift 数据库不支持递归查询。 https://docs.aws.amazon.com/redshift/latest/dg/c_unsupported-postgresql-features.html
我不擅长这个方言。试试这个代码,可能它可以工作并解决你的问题:
select book_no,prev_book_no,edition_no,
(
select listagg(innr.book_no,',') within group (order by innr.edition_no asc) over()
from books innr
where innr.book_no=outr.book_no and innr.edition_no<=outr.edition_no
) tree
from books outr
order by book_no
带有循环的变体。尝试执行它:
create local temporary table IF NOT EXISTS
table1 (book_id bigint, book_no varchar(10), edition_no varchar(10));
insert into table1 (book_id, book_no, edition_no)
select row_number () over (order by book_no) as book_id, book_no, edition_no
from books where edition_no=0;
FOR iter IN SELECT distinct edition_no
FROM books where edition_no!=0 ORDER BY edition_no LOOP
insert into table1 (book_id, book_no, edition_no)
select t.book_id, b.book_no, b.edition_no
from table1 t join books b on t.book_id=b.prev_book_no
where b.edition_no = iter
;
commit;
END LOOP;
select book_no,prev_book_no,edition_no,
(
select listagg(innr.book_no,',') within group (order by innr.edition_no asc) over()
from books innr inner join table1 tbl_innr on innr.book_no=tbl_innr.book_no
where tbl_innr.book_id=tbl_outr.book_id and innr.edition_no<=outr.edition_no
) tree
from books outr join table1 tbl_outr on outr.book_no=tbl_outr.book_no
order by book_no
;
drop table table1;
【讨论】:
感谢您的解决方案。但它似乎只适用于一棵这样的树,当我在整个数据集上尝试它时,它会给出错误:结果大小超过 LISTAGG 限制。 (可能它将所有书籍分组为特定版本号)。你能把它也包含在你的代码中吗? @NewCoder 你有整本书的ID吗(所有版本都一样)? 不,没有这个ID栏 @NewCoder 我刚刚添加了新版本。试试看以上是关于在 Redshift 中连接字符串的递归 CTE 替代方案的主要内容,如果未能解决你的问题,请参考以下文章
Redshift:连接到由表中的 SELECT * 组成的子查询/CTE 相当于连接表本身,还是性能下降?
为啥不能在 Redshift 的 CTE 的某些子句中调用不可变的 UDF?
锚和递归查询“CTE”列“ColumnName”中的递归部分之间的类型不匹配