在oracle sql中将字符串行连接到最大长度
Posted
技术标签:
【中文标题】在oracle sql中将字符串行连接到最大长度【英文标题】:Concatenate string rows up to length in oracle sql 【发布时间】:2021-03-06 16:45:07 【问题描述】:我想要实现的是使用回车连接长度不超过 10 的字符串。如果该行的长度超过 10,则应将其添加到下一个串联行。
例如,有以下数据集
SELECT '0123' col FROM DUAL
UNION ALL
SELECT '45 67' FROM DUAL
UNION ALL
SELECT '89A' FROM DUAL
UNION ALL
SELECT 'BC' FROM DUAL
UNION ALL
SELECT 'DEFGHI' FROM DUAL
我对结果的期望
SELECT '0123
45 67' col FROM DUAL
UNION ALL
SELECT '89A
BC' FROM DUAL
UNION ALL
SELECT 'DEFGHI' FROM DUAL
我正在运行 oracle 12.1,由于性能原因,我不想在 PLSQL 中执行此操作。我正在处理更高的数字。 我发布了一个简单的例子,这样会更容易。我的最终目标是以某种方式使用 listagg ,其中每行最多有 4k 个字符
【问题讨论】:
如果要回车,则需要将 CHR(13) 连接到字符串。奖励拆分字符串并添加到下一行,我不知道如何在直接 SQL 中完成,但也许比我更聪明的人有一个解决方案......你在谈论多少行,多久做一次你需要这样做吗? “处理更高的数字”是什么意思?数千?百万?你将如何控制数据的顺序?您可能需要的不仅仅是一列来确保预期的顺序。源表是否以任何方式编入索引? 1. newline 需要使用什么字符(或多个字符)?chr(10)
单独,在 Oracle 中是标准的,在 Unix 中是行终止符?或者chr(13) || chr(10)
在 DOS/Windows 中?或者是其他东西? 2. 你还需要在字符串末尾添加换行符吗? 3. 您的 Oracle 版本是多少? 4. 如果输入字符串(在单行中)已经长于限制,应该怎么办?也许在您的实际用例(4000,而不是 10)中不会发生,但它可能仍然是一个问题 如果您必须在末尾添加换行符(请参阅前面的问题)。
【参考方案1】:
这是一个match_recognize
解决方案,它需要Oracle 12.1 或更高版本。我做了以下额外的假设:换行符是 Unix 中的chr(10)
,最后一行不需要换行符,并且所有输入行字符串的长度最多等于限制。 (可以将限制 10 更改为绑定变量。)我假设还有一个排序列,我称之为 ORD。
with
sample_data (ord, col) as (
select 1, '0123' from dual union all
select 2, '45 67' from dual union all
select 3, '89A' from dual union all
select 4, 'BC' from dual union all
select 5, 'DEFGHI' from dual
)
select rn, listagg(col, chr(10)) within group (order by ord) as fragment
from sample_data
match_recognize (
order by ord
measures match_number() as rn
all rows per match
pattern (a+)
define a as sum(length(col)) + count(*) - 1 <= 10
)
group by rn
order by rn
;
RN FRAGMENT
----- ------------
1 0123
45 67
2 89A
BC
3 DEFGHI
【讨论】:
很好的假设。谢谢你。我将阅读有关 match_recognize 的更多信息【参考方案2】:如果您想将成对的相邻行组合在一起,那么您需要一个定义行顺序的列。让我假设您有这样的列,称为id
。
然后,您可以使用递归查询。这个想法是逐行遍历数据集,连接值直到长度超过 10,此时必须有一个新组。外部查询返回每个组中的最新行:
with
data (id, col, rn) as (
select t.*, row_number() over(order by id) rn
from mytable t
),
cte (id, rn, newcol, grp) as (
select id, rn, col, 1 from data d where rn = 1
union all
select d.id, d.rn,
case when length(c.newcol) + length(d.col) < 10
then c.newcol || chr(13) || d.col
else d.col
end,
case when length(c.newcol) + length(d.col) < 10
then c.grp
else d.rn
end
from cte c
inner join data d on d.rn = c.rn + 1
)
select max(newcol) as newcol
from cte
group by grp order by min(id)
Demo on DB Fiddle
【讨论】:
您似乎没有理解这个问题。要求是当字符串的总长度接近设定的限制时停止添加新行(作为单个字符串的附加行)。 (示例中为 10,但实际用例中为 4000)。这与输入行的数量无关,也与一次将它们分成两组无关。您的回答无助于解决 OP 提出的问题。否决我,因为“没有帮助”。 @mathguy:是的,你是对的,我误读了这个问题。我更改了解决方案以提供递归查询,这似乎可以满足此处的要求。 好的,我撤回了我的反对票。您的新解决方案将在 11.2 或更高版本中运行(也许您可以将其添加到您的答案中)。这意味着它是 11.2 的一个很好的答案 - 对于 >= 12.1 的版本,match_recognize
会更有效率。 (另外,递归查询可以在其他没有match_recognize
的db产品中实现。)【参考方案3】:
您可以使用MATCH_RECOGNIZE
对行进行分组,然后使用LISTAGG
将它们连接起来:
SELECT LISTAGG( col, CHR(10) ) WITHIN GROUP ( ORDER BY rn ) AS col
FROM ( SELECT ROWNUM AS rn, col FROM table_name )
MATCH_RECOGNIZE(
ORDER BY rn
MEASURES
MATCH_NUMBER() AS mno
ALL ROWS PER MATCH
PATTERN ( short_strings* last_string )
DEFINE short_strings AS NEXT(LENGTH(col)) <= 10 - SUM(LENGTH(col) + 1)
)
GROUP BY mno;
其中,对于样本数据:
CREATE TABLE table_name ( col ) AS
SELECT '0123' FROM DUAL UNION ALL
SELECT '45 67' FROM DUAL UNION ALL
SELECT '89A' FROM DUAL UNION ALL
SELECT 'BC' FROM DUAL UNION ALL
SELECT 'DEFGHI' FROM DUAL;
输出:
|色彩 | | :----- | | 0123 | | 45 67 | | ------ | | 89A | |卑诗省 | | ------ | |德吉 |
db小提琴here
【讨论】:
以上是关于在oracle sql中将字符串行连接到最大长度的主要内容,如果未能解决你的问题,请参考以下文章