【问题标题】:SQL split string (all possible combination)SQL 拆分字符串(所有可能的组合)
【发布时间】:2020-10-15 14:21:18
【问题描述】:

我想转换这个字符串:
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)

谢谢

【问题讨论】:

  • 用您正在使用的数据库标记您的问题。大多数数据库都有某种拆分功能。
  • 你有没有尝试过?另外,请检查:stackoverflow.com/help/someone-answers
  • 我会查看 STRING_SPLIT() 和 CROSS JOIN。

标签: sql sql-server string split


【解决方案1】:

如果您只有 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|

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

  1. 从第一组的所有值开始。
  2. 在递归步骤中,交叉连接下一组的所有值(即步骤组编号为当前组编号 + 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

我不会添加表格结果,因为它很大。但是try it by yourself

【讨论】:

  • 在你的帮助下我的代码在这里。我没有提到,但我也可以有更多或少于 3 个部分,所以我为此使用了动态 SQL:
  • @user11051593 我已经更新了我对没有任何动态 SQL 的非任意数量组的答案。
【解决方案2】:

这里是 SQL server 版本和fiddle:

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】:

在您的帮助下,这里是我的代码。我没有提到,但我也可以多于或少于 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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-03
    • 2017-11-11
    • 2015-05-06
    • 1970-01-01
    • 2021-12-01
    • 2018-06-29
    • 1970-01-01
    相关资源
    最近更新 更多