繁体   English   中英

SQL 拆分字符串(所有可能的组合)

[英]SQL split string (all possible combination)

我想转换这个字符串:
A1+A2+A3.B1+B2.C1
进入
A1.B1.C1
A1.B2.C1
A2.B1.C1
A2.B2.C1
A3.B1.C1
A3.B2.C1

我怎样才能做到这一点? (请注意,每个维度(= 由 . 分隔的组)可以有 x 值,我的意思是它可以是 A1+A2.B1.C1 或 A1+A2.B1+B2+B3+B4+B5.C1+C2)

谢谢

这是 SQL 服务器版本和小提琴

with lst(s) as (select * from STRING_SPLIT('A1+A2.B1+B2+B3+B4+B5.C1+C2','.'))
select t1+'.'+t2+'.'+t3 as res from
(select * from STRING_SPLIT((select s from lst where s like 'A%'), '+')) s1(t1) cross join
(select * from STRING_SPLIT((select s from lst where s like 'B%'), '+')) s2(t2) cross join
(select * from STRING_SPLIT((select s from lst where s like 'C%'), '+')) s3(t3);

当然,如果维度数量增加,您可以以常规方式增加它。

这是一个 Postgresql 解决方案:

with x(s) as (select string_to_array('A1+A2.B1+B2+B3+B4+B5.C1+C2','.'))
select t1||'.'||t2||'.'||t3 as res from 
unnest((select string_to_array(s[1],'+') from x)) t1 cross join 
unnest((select string_to_array(s[2],'+') from x)) t2 cross join 
unnest((select string_to_array(s[3],'+') from x)) t3;

结果:

res     |
--------|
A1.B1.C1|
A1.B2.C1|
A1.B3.C1|
A1.B4.C1|
A1.B5.C1|
A2.B1.C1|
A2.B2.C1|
A2.B3.C1|
A2.B4.C1|
A2.B5.C1|
A1.B1.C2|
A1.B2.C2|
A1.B3.C2|
A1.B4.C2|
A1.B5.C2|
A2.B1.C2|
A2.B2.C2|
A2.B3.C2|
A2.B4.C2|
A2.B5.C2|

如果您只有 3 列,则只需使用STRING_SPLIT :从第一次拆分开始为您的组编号,然后进行 3 次连接并在相应的连接中选择每个组。

with a as  (
 select s2.value as v, dense_rank() over(order by s1.value) as rn
  from STRING_SPLIT('A1+A2+A3.B1+B2.C1', '.') as s1
        cross apply STRING_SPLIT(s1.value, '+') as s2
)
select
  a1.v + '.' + a2.v + '.' + a3.v as val
from a as a1
  cross join a as a2
  cross join a as a3
where a1.rn = 1
  and a2.rn = 2
  and a3.rn = 3
|   val  |
----------
|A1.B1.C1|
|A2.B1.C1|
|A3.B1.C1|
|A1.B2.C1|
|A2.B2.C1|
|A3.B2.C1|

如果您有无限数量的组,那么最好使用递归 CTE而不是动态 SQL。 你应该做什么:

  1. 从第一组的所有值开始。
  2. 在递归步骤 crossjoin 下一组的所有值(即步骤组编号是当前组编号 + 1)。
  3. 选择您将获得结果的最后一个递归步骤。

代码如下:

with a as  (
 select s2.value as v, dense_rank() over(order by s1.value) as rn
  from STRING_SPLIT('A1+A2+A3.B1+B2+B3+B4.C1+C2.D1+D2+D3', '.') as s1
        cross apply STRING_SPLIT(s1.value, '+') as s2
)
, b (val, lvl) as (
  /*Recursion base*/
  select cast(v as nvarchar(1000)) as val, rn as lvl
  from a
  where rn = 1

  union all
  /*Increase concatenation on each iteration*/
  select cast(concat(b.val, '.', a.v) as nvarchar(1000)) as val, b.lvl + 1 as lvl
  from b
    join a
      on b.lvl + 1 = a.rn /*Recursion step*/
)
select *
from b
where lvl = (select max(rn) from a) /*You need the last step*/
order by val

我不会添加表格结果,因为它很大。 不过还是自己试试吧

这是我在您的帮助下的代码。 我没有提到,但我也可以有多于或少于 3 个部分,所以我为此使用了动态 SQL:

declare @FILTER varchar(max)='B+C+D.A+G.T+Y+R.E' 
-- Works also with A.B.C
-- Works also with A+B+C.D.E+F
-- Works also with A+B+C.D+E+F+G+H
declare @NB int
declare @SQL varchar(max)=''

select @NB=count(*) from STRING_SPLIT(@FILTER,'.')


set @SQL='
;with T(A,B) as
    (select *, row_number() over (order by (select NULL)) 
     from STRING_SPLIT(''' + @FILTER + ''',''.'')
     )
     select '

;with T(V,N) as (
    select *, row_number() over (order by (select NULL))
    from STRING_SPLIT(@FILTER,'.')
)
select @SQL=@SQL + 'T' + cast(N as varchar(max)) + ' + ''.'' + ' from T
set @SQL=left(@SQL,len(@SQL)-1) + ' as res from'

;with T(V,N) as (
    select *, row_number() over (order by (select NULL))
    from STRING_SPLIT(@FILTER,'.')
)
select @SQL=@SQL + '
(select * from STRING_SPLIT((select A from T where B=' + cast(N as varchar(max)) + '),     ''+'')) s' + cast(N as varchar(max)) + '(t' + cast(N as varchar(max)) + ') cross join'
from T

set @SQL=left(@SQL,len(@SQL)-len('cross join'))
exec(@SQL)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM