The most elegant way to generate permutations in SQL server

后端 未结 10 1379
面向向阳花
面向向阳花 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: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
    

提交回复
热议问题