The most elegant way to generate permutations in SQL server

后端 未结 10 1344
面向向阳花
面向向阳花 2020-11-29 07:00

Given a the following table:

Index | Element
---------------
  1   |    A
  2   |    B
  3   |    C
  4   |    D

We want to generate all th

相关标签:
10条回答
  • 2020-11-29 07:30

    Just using SQL, without any code, you could do it if you can crowbar yourself another column into the table. Clearly you need to have one joined table for each of the values to be permuted.

    with llb as (
      select 'A' as col,1 as cnt union 
      select 'B' as col,3 as cnt union 
      select 'C' as col,9 as cnt union 
      select 'D' as col,27 as cnt
    ) 
    select a1.col,a2.col,a3.col,a4.col
    from llb a1
    cross join llb a2
    cross join llb a3
    cross join llb a4
    where a1.cnt + a2.cnt + a3.cnt + a4.cnt = 40
    
    0 讨论(0)
  • 2020-11-29 07:30

    Assuming your table is named Elements and has 4 rows, this is as simple as:

    select e1.Element + e2.Element + e3.Element + e4.Element
    from Elements e1
        join Elements e2 on e2.Element != e1.Element 
        join Elements e3 on e3.Element != e2.Element AND e3.Element != e1.Element 
        join Elements e4 on e4.Element != e3.Element AND e4.Element != e2.Element AND e4.Element != e1.Element
    
    0 讨论(0)
  • 2020-11-29 07:34

    Am I correctly understanding that you built Cartesian product n x n x n x n, and then filter out unwanted stuff? The alternative would be generating all the numbers up to n! and then using factorial number system to map them via element encoding.

    0 讨论(0)
  • 2020-11-29 07:34

    Simpler than a recursive CTE:

    declare @Number Table( Element varchar(MAX), Id varchar(MAX) )
    Insert Into @Number Values ( 'A', '01')
    Insert Into @Number Values ( 'B', '02')
    Insert Into @Number Values ( 'C', '03')
    Insert Into @Number Values ( 'D', '04')
    
    select a.Element, b.Element, c.Element, d.Element
    from @Number a
    join @Number b on b.Element not in (a.Element)
    join @Number c on c.Element not in (a.Element, b.Element)
    join @Number d on d.Element not in (a.Element, b.Element, c.Element)
    order by 1, 2, 3, 4
    

    For an arbitrary number of elements, script it out:

    if object_id('tempdb..#number') is not null drop table #number
    create table #number (Element char(1), Id int, Alias as '_'+convert(varchar,Id))
    insert #number values ('A', 1)
    insert #number values ('B', 2)
    insert #number values ('C', 3)
    insert #number values ('D', 4)
    insert #number values ('E', 5)
    
    declare @sql nvarchar(max)
    set @sql = '
    select '+stuff((
      select char(13)+char(10)+'+'+Alias+'.Element'
      from #number order by Id for xml path (''), type
      ).value('.','NVARCHAR(MAX)'),3,1,' ')
    
    set @sql += '
    from #number '+(select top 1 Alias from #number order by Id)
    
    set @sql += (
      select char(13)+char(10)+'join #number '+Alias+' on '+Alias+'.Id not in ('
        +stuff((
          select ', '+Alias+'.Id'
          from #number b where a.Id > b.Id
          order by Id for xml path ('')
          ),1,2,'')
        + ')'
      from #number a where Id > (select min(Id) from #number)
      order by Element for xml path (''), type
      ).value('.','NVARCHAR(MAX)')
    
    set @sql += '
    order by 1'
    
    print @sql
    exec (@sql)
    

    To generate this:

    select 
     _1.Element
    +_2.Element
    +_3.Element
    +_4.Element
    +_5.Element
    from #number _1
    join #number _2 on _2.Id not in (_1.Id)
    join #number _3 on _3.Id not in (_1.Id, _2.Id)
    join #number _4 on _4.Id not in (_1.Id, _2.Id, _3.Id)
    join #number _5 on _5.Id not in (_1.Id, _2.Id, _3.Id, _4.Id)
    order by 1
    
    0 讨论(0)
提交回复
热议问题