Generate all combinations in SQL

前端 未结 6 572
陌清茗
陌清茗 2020-12-01 04:31

I need to generate all combinations of size @k in a given set of size @n. Can someone please review the following SQL and determine first if the fo

6条回答
  •  渐次进展
    2020-12-01 05:24

    First create this UDF...

    CREATE FUNCTION [dbo].[_ex_fn_SplitToTable] (@str varchar(5000), @sep char(1) = null)                           
    
    RETURNS @ReturnVal table (n int, s varchar(5000))                           
    
    AS                          
    /*                          
    Alpha Test                          
    -----------                         
    select * from [dbo].[_ex_fn_SplitToTable_test01]('abcde','')                            
    */                          
    
    BEGIN                           
        declare @str2 varchar(5000)                     
        declare @sep2 char(1)                       
        if LEN(ISNULL(@sep,'')) = 0                     
        begin                       
            declare @i int                  
            set @i = 0                  
    
            set @str2 = ''                  
            declare @char varchar(1)                    
            startloop:                  
                set @i += 1             
                --print @i              
                set @char = substring(@str,@i,1)                
                set @str2 = @str2 + @char + ','             
                if LEN(@str) <= @i              
                    goto exitloop           
                goto startloop              
            exitloop:                   
            set @str2 = left(@str2,LEN(@str2) - 1)                  
            set @sep2 = ','                 
            --print @str2                   
    
        end                         
        else                        
        begin                       
            set @str2 = @str                    
            set @sep2 = @sep                    
        end                     
    
        ;WITH Pieces(n, start, stop) AS (                           
    
          SELECT 1, 1, CHARINDEX(@sep2, @str2)                          
    
          UNION ALL                             
    
          SELECT n + 1, stop + 1, CHARINDEX(@sep2, @str2, stop + 1)                             
    
          FROM Pieces                           
    
          WHERE stop > 0                            
    
        )                           
    
        insert into @ReturnVal(n,s)                             
        SELECT n,                           
          SUBSTRING(@str2, start, CASE WHEN stop > 0 THEN stop-start ELSE 5000 END) AS s                            
        FROM Pieces option (maxrecursion 32767)                             
    
    
        RETURN                          
    
    END                         
    
    
    
    GO                          
    

    Then create this stored proc...

    CREATE proc [CombinationsOfString]                          
    (                           
        @mystring varchar(max) = '0,5,10,15,20,25'                      
    )                           
    as                          
    
    /*                          
    ALPHA TEST                          
    ---------                           
    exec CombinationsOfString '-20,-10,0,10,20'                         
    */                          
    if object_id('tempdb..#_201606070947_myorig') is not null drop table #_201606070947_myorig                          
    
    CREATE TABLE #_201606070947_myorig                          
     (                          
       SourceId  int      not null  identity(1,1)                           
      ,Element   varchar(100)  not null                         
     )                          
    insert into #_201606070947_myorig                           
    select s from dbo._ex_fn_SplitToTable(@mystring,',')                            
    
    --select SourceId, Element from #_201606070947_myorig                           
    
    declare @mynumerics varchar(max)                            
    
    set @mynumerics = (                         
        select STUFF(REPLACE((SELECT '#!' + LTRIM(RTRIM(SourceId)) AS 'data()'                      
        FROM #_201606070947_myorig                      
        FOR XML PATH('')),' #!',', '), 1, 2, '') as Brands                      
    )                           
    
    set @mynumerics = REPLACE(@mynumerics,' ','')                           
    
    print @mynumerics                           
    
    if object_id('tempdb..#_201606070947_source') is not null drop table #_201606070947_source                          
    if object_id('tempdb..#_201606070947_numbers') is not null drop table #_201606070947_numbers                            
    if object_id('tempdb..#_201606070947_results') is not null drop table #_201606070947_results                            
    if object_id('tempdb..#_201606070947_processed') is not null drop table #_201606070947_processed                            
    
    
    
    CREATE TABLE #_201606070947_source                          
     (                          
       SourceId  int      not null  identity(1,1)                           
      ,Element   char(1)  not null                          
     )                          
    
    --declare @mynumerics varchar(max)                          
    --set @mynumerics = '1,2,3,4,5'                         
    insert into #_201606070947_source                           
    select s from dbo._ex_fn_SplitToTable(@mynumerics,',')                          
    
    -- select * from #_201606070947_source                          
    
    declare @Length int                         
    set @Length = (select max(SourceId) from #_201606070947_source)                         
    declare @columnstring varchar(max) = (SELECT REPLICATE('c.',@Length))                           
    print @columnstring                         
    declare @subs varchar(max) = (SELECT REPLICATE('substring.',@Length))                           
    print @subs                         
    
    if object_id('tempdb..#_201606070947_columns') is not null drop table #_201606070947_columns                            
    
    select s+CONVERT(varchar,dbo.PadLeft(convert(varchar,n),'0',3)) cols                            
    into #_201606070947_columns                         
    from [dbo].[_ex_fn_SplitToTable](@columnstring,'.') where LEN(s) > 0                            
    
    if object_id('tempdb..#_201606070947_subcolumns') is not null drop table #_201606070947_subcolumns                          
    select s+'(Combo,'+CONVERT(varchar,n)+',1) ' + 'c'+CONVERT(varchar,dbo.PadLeft(convert(varchar,n),'0',3)) cols                          
    into #_201606070947_subcolumns                          
    from [dbo].[_ex_fn_SplitToTable](@subs,'.') where LEN(s) > 0                            
    
    -- select * from #_201606070947_subcolumns                          
    -- select * from #_201606070947_columns                         
    
    
    declare @columns_sql varchar(max)                           
    set @columns_sql =                          
            (                   
                select distinct                 
                  stuff((SELECT distinct + cast(cols as varchar(50)) +  ' VARCHAR(1), '             
                           FROM (       
                            select cols 
                            from #_201606070947_columns     
                            ) t2    
                           --where t2.n = t1.n      
                           FOR XML PATH('')),3,0,'')        
                from (              
                    select cols         
                    from #_201606070947_columns             
                    ) t1            
            )                   
    
    declare @substring_sql varchar(max)                         
    set @substring_sql =                            
            (                   
                select distinct                 
                  stuff((SELECT distinct + cast(cols as varchar(100)) +  ', '               
                           FROM (       
                            select cols 
                            from #_201606070947_subcolumns  
                            ) t2    
                           --where t2.n = t1.n      
                           FOR XML PATH('')),3,0,'')        
                from (              
                    select cols         
                    from #_201606070947_subcolumns          
                    ) t1            
            )                   
    set @substring_sql = left(@substring_sql,LEN(@substring_sql) - 1)                           
    print @substring_sql                            
    
    set @columns_sql = LEFT(@columns_sql,LEN(@columns_sql) - 1)                         
    --SELECT @columns_sql                           
    declare @sql varchar(max)                           
    set @sql = 'if object_id(''tempdb..##_201606070947_01'') is not null drop table ##_201606070947_01 create table ##_201606070947_01 (rowid int,' + @columns_sql + ')'                            
    print @sql                          
    execute(@sql)                           
    
    CREATE TABLE #_201606070947_numbers (Number int not null)                           
    
    insert into #_201606070947_numbers                          
    select SourceId from #_201606070947_source                          
    
    CREATE TABLE #_201606070947_results                         
     (                          
       Combo   varchar(10)  not null                            
      ,Length  int          not null                            
     )                          
    
     SET NOCOUNT on                         
    
    DECLARE                         
      @Loop     int                         
     ,@MaxLoop  int                         
    
    
    --  How many elements there are to process                          
    SELECT @MaxLoop = max(SourceId)                         
     from #_201606070947_source                         
    
    
    --  Initialize first value                          
    TRUNCATE TABLE #_201606070947_results                           
    INSERT #_201606070947_results (Combo, Length)                           
     select Element, 1                          
      from #_201606070947_source                            
      where SourceId = 1                            
    
    SET @Loop = 2                           
    
    --  Iterate to add each Element after the first                         
    WHILE @Loop <= @MaxLoop                         
     BEGIN                          
    
        INSERT #_201606070947_results (Combo, Length)                       
         select distinct                        
            left(re.Combo, @Loop - nm.Number)                   
            + so.Element                    
            + RIGHT(re.Combo, nm.Number - 1)                    
           ,@Loop                       
          from #_201606070947_results re                        
           inner join #_201606070947_numbers nm                     
            on nm.Number <= @Loop                   
           inner join #_201606070947_source so                      
            on so.SourceId = @Loop                  
          where re.Length = @Loop - 1                       
    
        SET @Loop = @Loop + 1                       
     END                            
    -- select * from #_201606070947_results                         
    --  Show #_201606070947_results                         
    SELECT *                            
    into #_201606070947_processed                           
     from #_201606070947_results                            
     where Length = @MaxLoop                            
     order by Combo                         
    
    
    
    -- select * from #_201606070947_processed                           
    set @sql = 'if object_id(''tempdb..##_201606070947_02'') is not null drop table ##_201606070947_02 '                            
    print @sql                          
    execute(@sql)                           
    
    set @sql = ' ' +                            
    '  SELECT ROW_NUMBER() OVER(ORDER BY Combo Asc) AS RowID,' + @substring_sql +                           
    '  into ##_201606070947_02 ' +                          
    ' FROM #_201606070947_processed ' +                         
    ' '                         
    PRINT @sql                          
    execute(@sql)                           
    
    declare @columns_sql_new varchar(max)                           
    set @columns_sql_new = REPLACE(@columns_sql,'(1)','(100)')                          
    set @sql = 'if object_id(''tempdb..##_201606070947_03'') is not null drop table ##_201606070947_03 create table ##_201606070947_03 (RowId int,' + @columns_sql_new + ')'                            
    PRINT @sql                          
    execute(@sql)                           
    
    insert into ##_201606070947_03 (RowId)                          
    select RowId from ##_201606070947_02                            
    
    --select * from ##_201606070947_03                          
    
    
    
    
    DECLARE @ColumnId varchar(10)                           
    DECLARE @getColumnId CURSOR                         
    SET @getColumnId = CURSOR FOR                           
        select cols ColumnId from #_201606070947_columns                        
    OPEN @getColumnId                           
    FETCH NEXT                          
    FROM @getColumnId INTO @ColumnId                            
    WHILE @@FETCH_STATUS = 0                            
    BEGIN                           
        PRINT @ColumnId                     
        set @sql = ' ' +                        
        ' update ##_201606070947_03                         
          set ' + @ColumnId + ' = B.Element                         
          from ##_201606070947_03 A                         
          , (                       
    
                select A.RowID, B.*             
                from                
                (               
                    select * from ##_201606070947_02            
                ) A             
                ,               
                (               
                    select * from #_201606070947_myorig         
                ) B             
                where A.' + @ColumnId + ' = B.SourceId              
            ) B                 
           where A.RowId = B.RowId                      
        '                       
        execute(@sql)                       
        print @sql                      
    
    FETCH NEXT                          
    FROM @getColumnId INTO @ColumnId                            
    END                         
     CLOSE @getColumnId                         
    DEALLOCATE @getColumnId                         
    
    select * from ##_201606070947_03                            
    

提交回复
热议问题