• Hi Tom,

    A generic cte is possible, but I want to show you my WHILE loop first because it can handle the data and my CTE can't!

    /* Combinations of coins - generic solution by Mister Magoo */

    -- drop our temp tables if they exist

    if object_id('tempdb..#values') is not null

    drop table #values;

    if object_id('tempdb..#coins') is not null

    drop table #coins;

    if object_id('tempdb..#results') is not null

    drop table #results;

    -- lets have a table for the coins - denomination and quantity

    create table #coins(den smallint primary key not null,number int not null);

    -- and some test data

    insert #coins(den,number)

    values(1,3),(2,2),(5,0),(10,3),(50,1); -- should come up with 63 values

    /* some more test */

    --values(1,1),(2,5000),(5,1),(10,2),(20,1),(50,7),(100,402),(200,35); -- should come up with 57596 values

    --values(1,1),(2,5000),(5,1),(10,2),(20,1),(50,7),(100,402),(200,35),(500,99); -- should come up with 107096 values

    -- let's build a tally cte

    ;with

    E1(N) AS (

    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL

    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL

    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1

    ), --10E+1 or 10 rows

    E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows

    E4(N) AS (SELECT 1 FROM E2 a, E2 b) --10E+4 or 10,000 rows max

    /* you can add more elements to the cte to handle larger numbers of coins per denomination if required */

    -- now generate all the possible values for each coin denomination

    select den,den*n as value

    into #values

    from #coins coins

    cross apply(

    select 0

    union all

    select top (coins.number) row_number() over (order by (select null)) from e4

    ) ctetally(n);

    -- index the list so we can get each denomination's values quickly

    create clustered index ix_vals_ci on #values(den);

    -- create somewhere to put the results

    -- NOTE: the use of identity on "den" is to remove the need for an index

    -- we can tell what we last processed by checking scope_identity()

    create table #results(den int identity(1,1) not null , value int not null);

    -- allow direct insertes of identity values

    set identity_insert #results on;

    -- add the first set of results for the first coin denomination

    insert #results(den,value)

    select den,value

    from #values

    where den = (Select min(den) from #values);

    -- now a single statement while loop to insert the rest of the results

    -- this works out as X inserts of sets of data where X = the number of coin denominations in the #coins table

    -- this is the same logic as a recursive cte, but many times faster

    while @@rowcount>0

    insert #results(den,value)

    -- use of distinct saves us processing duplicates as we go along

    select distinct vals.den,base.value + vals.value

    from

    ( -- let's get the last set of data we inserted from the #results table by using scope_identity() to identify them

    select distinct value

    from #results res

    where res.den = scope_identity()

    ) base

    cross join

    ( -- and cross join that with all the possible values of the next coin in the #coins table

    select den,value

    from #values vals

    where vals.den = (

    select top 1 coins.den

    from #coins coins

    where coins.den> scope_identity()

    order by coins.den)

    ) vals;

    -- finally the results, excluding zero

    select distinct value

    from #results

    where value>0

    order by value;

    The rubbish CTE is here:

    ;with

    coins(den,number) as

    (

    select den,number from (values(1,3),(2,2),(5,0),(10,3),(50,1))a(den,number)

    ),

    E1(N) AS (

    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL

    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL

    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1

    ), --10E+1 or 10 rows

    E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows

    E4(N) AS (SELECT 1 FROM E2 a, E2 b) --10E+4 or 10,000 rows max

    , vals(den,value) as

    (

    select den,den*n

    from coins

    cross apply(

    select 0

    union all

    select top (coins.number) row_number() over (order by (select null)) from e4

    ) ctetally(n)

    ), combinations as

    (

    select den,value

    from vals

    where den=(select min(den) from coins)

    union all

    select vals.den, vals.value + combi.value

    from combinations as combi

    cross apply ( select den,value

    from (

    select den,value,rank() over(order by den) as rnk

    from vals

    where vals.den>combi.den

    ) a

    where a.rnk = 1

    ) vals

    )

    select distinct value

    from combinations

    where value>0

    MM



    select geometry::STGeomFromWKB(0x0106000000020000000103000000010000000B0000001000000000000840000000000000003DD8CCCCCCCCCC0840000000000000003DD8CCCCCCCCCC08408014AE47E17AFC3F040000000000104000CDCCCCCCCCEC3F9C999999999913408014AE47E17AFC3F9C99999999991340000000000000003D0000000000001440000000000000003D000000000000144000000000000000400400000000001040000000000000F03F100000000000084000000000000000401000000000000840000000000000003D0103000000010000000B000000000000000000143D000000000000003D009E99999999B93F000000000000003D009E99999999B93F8014AE47E17AFC3F400000000000F03F00CDCCCCCCCCEC3FA06666666666FE3F8014AE47E17AFC3FA06666666666FE3F000000000000003D1800000000000040000000000000003D18000000000000400000000000000040400000000000F03F000000000000F03F000000000000143D0000000000000040000000000000143D000000000000003D, 0);

  • Forum Etiquette: How to post Reporting Services problems
  • [/url]
  • Forum Etiquette: How to post data/code on a forum to get the best help - by Jeff Moden
  • [/url]
  • How to Post Performance Problems - by Gail Shaw
  • [/url]