Search code examples
sql-servert-sqlunpivot

Stack the first two columns of a table into a single column, preserving relationship with third column


This question is almost identical to this question, but at the time, I didn't realize that the ordering of the rows by unique key id wasn't actually there. Anyway, let me repeat the question with the correct schema.


I have a table that looks like the following:

| seq | code_1 | code_2 | pair_1 |
|-----|--------|--------|--------|
|   1 |     a1 |     b1 |     c1 |
|   2 |     a2 |     b2 | (null) |
|   3 |     a3 | (null) |     c2 |
|   4 |     a4 | (null) | (null) |

I want to stack code_1 and code_2 into a single column, which can be done by using:

select row_number() over (order by seq), code, pair_1 as pair
  from source
       unpivot (code for code_ in (code_1, code_2)) as unpvt;

, which produces the following output:

| seq | code |   pair |
|-----|------|--------|
|   1 |   a1 |     c1 |
|   2 |   b1 |     c1 |
|   3 |   a2 | (null) |
|   4 |   b2 | (null) |
|   5 |   a3 |     c2 |
|   6 |   a4 | (null) |

, but I also want to capture the condition that code_1 is paired with pair_1 i.e. whenever code is sourced from code_1, it will use the value in pair_1 for pair; and whenever code is sourced from code_2, it will always use null for pair.

So for example, given the original table, here is what the target table should look like:

| seq | code |   pair |
|-----|------|--------|
|   1 |   a1 |     c1 |
|   2 |   b1 | (null) |
|   3 |   a2 | (null) |
|   4 |   b2 | (null) |
|   5 |   a3 |     c2 |
|   6 |   a4 | (null) |

DDL:

http://sqlfiddle.com/#!18/3fecf/9

create table source (
  seq    int,
  code_1 varchar(10),
  code_2 varchar(10),
  pair_1 varchar(10)
);
insert into source values
(1, 'a1', 'b1', 'c1'),
(2, 'a2', 'b2', null),
(3, 'a3', null, 'c2'),
(4, 'a4', null, null);

create table target (
  seq    int,
  code   varchar(10),
  pair   varchar(10)
);
insert into target values
(1, 'a1', 'c1'),
(2, 'b1', null),
(3, 'a2', null),
(4, 'b2', null),
(5, 'a3', 'c2'),
(6, 'a4', null);

Solution

  • I might be missing something here, but I think a UNION ALL will do it:

    WITH CTE AS
    (
        SELECT seq, code_1 as code, pair_1, 1 as codeType
        FROM source
        WHERE code_1 IS NOT NULL
        UNION ALL
        SELECT seq, code_2, NULL, 2
        FROM source
        WHERE code_2 IS NOT NULL
    ) 
    SELECT seq, code, pair_1 as pair
    FROM CTE
    ORDER BY seq, codeType
    

    See a live demo on rextester.