Search code examples
sqlsql-servert-sqluniquecombinations

Unique combination of multiple columns, order doesn't matter


Suppose a table with 3 columns. each row represents a unique combination of each value:

a a a
a a b
a b a
b b a
b b c
c c a
...

however, what I want is,

aab = baa = aba 
cca = cac = acc
...

Finally, I want to get these values in a CSV format as a combination for each value like the image that I attached.

this

Thanks for your help!

Below is the query to generate my problem, please take a look!

--=======================================
--populate test data
--=======================================
drop table if exists #t0
;
with 
cte_tally as
(
select row_number() over (order by (select 1)) as n 
from sys.all_columns
)
select 
  char(n) as alpha
into #t0
from 
  cte_tally
where
  (n > 64 and n < 91) or
  (n > 96 and n < 123);

drop table if exists #t1
select distinct upper(alpha) alpha into #t1 from #t0

drop table if exists #t2
select
    a.alpha c1
,   b.alpha c2
,   c.alpha c3
,   row_number()over(order by (select 1)) row_num
into #t2
from #t1 a
join #t1 b on 1=1
join #t1 c on 1=1


drop table if exists #t3
select *
into #t3
from (
    select *
    from #t2
) p
unpivot
    (cvalue for c in (c1,c2,c3)
) unpvt


select
    row_num
,   c
,   cvalue
from #t3
order by 1,2

--=======================================
--these three rows should be treated equally
--=======================================
select *
from #t2
where concat(c1,c2,c3) in  ('ABA','AAB', 'BAA')

--=======================================
--what i've tried...
--row count is actually correct, but the problem is that it ommits where there're any duplicate alphabet.
--=======================================
select 
    distinct
    stuff((
        select
            distinct
        '.' + cvalue
        from #t3 a
        where a.row_num = h.row_num
    for xml path('')
    ),1,1,'') as comb
from #t3 h


Solution

  • As pointed out in the comments, you can unpivot the values, sort them in the right order and reaggregate them into a single row. Then you can group the original rows by those new values.

    SELECT *
    FROM #t2
    CROSS APPLY (
        SELECT a = MIN(val), b = MIN(CASE WHEN rn = 2 THEN val), c = MAX(val)
        FROM (
            SELECT *, rn = ROW_NUMBER() OVER (ORDER BY val)
            FROM (VALUES (c1),(c2),(c3) ) v3(val)
        ) v2
    ) v
    GROUP BY v.a, v.b, v.c;
    

    Really, what you should perhaps do, is ensure that the values are in the correct order in the first place:

    ALTER TABLE #t2
       ADD CONSTRAINT t2_ValuesOrder
           CHECK (c1 <= c2 AND c2 <= c3);