Percy Dobbelsteyn Percy Dobbelsteyn - 29 days ago 6
SQL Question

T-SQL shape table in desired form

I've got two tables: TableA and TableB

TableA: columnBLA, objName, objID

TableB: objID, ColumnIndex, StringValue, NumberValue, DateValue

TableA has a row: bla, Object Name, 21

TableB also has data for objID=21:

21, 1, a, NULL, NULL
21, 1, b, NULL, NULL
21, 1, c, NULL, NULL
21, 2, NULL, 11, NULL
21, 2, NULL, 22, NULL
21, 2, NULL, 33, NULL
21, 3, NULL, NULL, 1/1/2012
21, 3, NULL, NULL, 1/1/2013
21, 3, NULL, NULL, 1/1/2014


Now I want to reshape this data to the following form:

a, 11, 1/1/2012
b, 22, 1/1/2013
c, 33, 1/1/2014


I've got this far:

Select StringValue, NumberValue, DateValue
From
(Select StringValue
From TableA ta WITH (NOLOCK), TableB tb WITH (NOLOCK)
WHERE ta.objID = tb.objID
AND t.objName = N'Object Name'
AND d.ColumnIndex = 1) As StringValues
,
(Select NumberValue
From TableA ta WITH (NOLOCK), TableB tb WITH (NOLOCK)
WHERE ta.objID = tb.objID
AND t.objName = N'Object Name'
AND d.ColumnIndex = 2) As NumberValues
,
(Select DateValue
From TableA ta WITH (NOLOCK), TableB tb WITH (NOLOCK)
WHERE ta.objID = tb.objID
AND t.objName = N'Object Name'
AND d.ColumnIndex = 1) As DateValues


But I got unwanted result.
Somebody told me I should use a PIVOT for that, but My SQL knowledge doesn't span that far.

Answer

Here is one way to do this using both an UNPIVOT and a PIVOT:

select objid, stringvalue, numbervalue, datevalue
from
(
  select objid, col, value,
    row_number() over(partition by objid, col order by value) rn
  from
  (
    select a.objid,
      b.stringvalue,
      b.numbervalue,
      b.datevalue
    from tablea a
    left join tableb b
      on a.objid = b.objid
  ) src
  unpivot
  (
    value
    for col in (stringvalue, numbervalue, datevalue)
  ) unpiv
) s
pivot
(
  max(value)
  for col in (stringvalue, numbervalue, datevalue)
) piv

See SQL Fiddle with Demo. The unpivot takes the values from your separate columns and converts it into rows. I then apply a row_number() to the data and then apply the pivot to turn it back into columns.

Another way to do this without using the PIVOT and UNPIVOT functions is to apply a row_number() and then use an aggregate function with a CASE expression:

select objid,
  max(case when columnindex = 1 then stringvalue end) stringvalue,
  max(case when columnindex = 2 then numbervalue end) numbervalue,
  max(case when columnindex = 3 then datevalue end) datevalue
from
(
  select a.objid,
    b.stringvalue,
    b.numbervalue,
    b.datevalue,
    b.columnindex,
    row_number() over(partition by a.objid, b.columnindex 
                      order by b.columnindex) rn
  from tablea a
  left join tableb b
    on a.objid = b.objid
) src
group by objid, rn

See SQL Fiddle with Demo

The result of both query is:

| OBJID | STRINGVALUE | NUMBERVALUE | DATEVALUE |
-------------------------------------------------
|    21 |           a |          11 |  1/1/2012 |
|    21 |           b |          22 |  1/1/2013 |
|    21 |           c |          33 |  1/1/2014 |
Comments