Thelonias
Thelonias

Reputation: 2935

Get permutations of ordered sets of N values

I have a table that consists of a set codes for an item. Each code's group is defined by group_id. The table is defined as follows:

CREATE TABLE item_code (
  id int PRIMARY KEY NOT NULL IDENTITY (1,1),
  item_id int DEFAULT NULL,
  group_id int NOT NULL,
  code varchar(50) NOT NULL
);

CREATE TABLE groups (
  id int PRIMARY KEY NOT NULL IDENTITY (1,1),
  name varchar(50) NOT NULL,
  order int NOT NULL
)

For each item_id in the table, I need to select 1 code from each group_id ordered by the group's order. For example:

INSERT INTO groups (id, name, order) VALUES (1, 'one', 10), (2, 'two', 20), (3, 'three', 30);

INSERT INTO item_code (item_id, group_id, [code])
VALUES
    (99, 1, 'code1-1'),
    (99, 1, 'code1-2'),
    (99, 2, 'code2-1'),
    (99, 2, 'code2-2'),
    (99, 3, 'code3-1'),
    (100,1, 'another-code');

would result in the set:

item_id code_combination 
     99   "code1-1"
     99   "code1-2"
     99   "code2-1"
     99   "code2-2"
     99   "code3-1"
     99   "code1-1, code2-1"
     99   "code1-1, code2-2"
     99   "code1-2, code2-1"
     99   "code1-2, code2-2"
     99   "code1-1, code3-1"
     99   "code1-2, code3-1"
     99   "code2-1, code3-1"
     99   "code2-2, code3-1"
     99   "code1-1, code2-1, code3-1"
     99   "code1-2, code2-1, code3-1"
     99   "code1-1, code2-2, code3-1"
     99   "code1-2, code2-2, code3-1"
     100  "another-code"

The order of the actual results does not matter. I included a row for item_id == 100 just to show that results for all item_id should be included.

What I've done so far: I've build a CTE that gets combinations of codes, but it does not respect item_id, groups or order and that's where I'm stuck:

;WITH cte ( combination, curr ) AS (
  SELECT CAST(ic.code AS VARCHAR(MAX)), ic.id
  FROM   items_code ic
  UNION ALL
  SELECT CAST( c.combination + ',' + CAST(ic.code AS VARCHAR(10) ) AS VARCHAR(MAX) ), ic.id
  FROM   item_code ic
         INNER JOIN
         cte c
         ON ( c.curr < ic.id )
)
SELECT combination FROM cte

UPDATE: I have a slightly more complicated schema than what I originally posted, and have built the schema in this fiddle. The idea is the same, it's just that "order" is defined on a different table.

Upvotes: 1

Views: 51

Answers (1)

SqlZim
SqlZim

Reputation: 38073

Adding a little more to your recursive cte, expanding the final join conditions, as well as some additional columns:

;with cte as (
  select
      ic.id
    , ic.item_id
    , ic.group_id
    , g.[order]
    , level = 0
    , combination = cast(ic.code as varchar(max))
  from item_code ic
    inner join groups g
      on ic.group_id = g.id
  union all
  select 
      ic.id
    , ic.item_id
    , ic.group_id
    , g.[order]
    , level = c.level + 1
    , combination = cast( c.combination + ',' + cast(ic.code as varchar(10) ) as varchar(max) )
  from item_code ic
    inner join groups g
      on ic.group_id = g.id
    inner join cte c
      on c.id < ic.id 
     and c.[order] < g.[order]
     and c.item_id = ic.item_id
)
select * 
from cte
order by item_id, level, combination

rextester demo: http://rextester.com/PJC44281

returns:

+----+---------+----------+-------+-------+-------------------------+
| id | item_id | group_id | order | level |       combination       |
+----+---------+----------+-------+-------+-------------------------+
|  1 |      99 |        1 |    10 |     0 | code1-1                 |
|  2 |      99 |        1 |    10 |     0 | code1-2                 |
|  3 |      99 |        2 |    20 |     0 | code2-1                 |
|  4 |      99 |        2 |    20 |     0 | code2-2                 |
|  5 |      99 |        3 |    30 |     0 | code3-1                 |
|  3 |      99 |        2 |    20 |     1 | code1-1,code2-1         |
|  4 |      99 |        2 |    20 |     1 | code1-1,code2-2         |
|  5 |      99 |        3 |    30 |     1 | code1-1,code3-1         |
|  3 |      99 |        2 |    20 |     1 | code1-2,code2-1         |
|  4 |      99 |        2 |    20 |     1 | code1-2,code2-2         |
|  5 |      99 |        3 |    30 |     1 | code1-2,code3-1         |
|  5 |      99 |        3 |    30 |     1 | code2-1,code3-1         |
|  5 |      99 |        3 |    30 |     1 | code2-2,code3-1         |
|  5 |      99 |        3 |    30 |     2 | code1-1,code2-1,code3-1 |
|  5 |      99 |        3 |    30 |     2 | code1-1,code2-2,code3-1 |
|  5 |      99 |        3 |    30 |     2 | code1-2,code2-1,code3-1 |
|  5 |      99 |        3 |    30 |     2 | code1-2,code2-2,code3-1 |
|  6 |     100 |        1 |    10 |     0 | another-code            |
+----+---------+----------+-------+-------+-------------------------+

Upvotes: 1

Related Questions