Home > OS >  Transposing a single column table to row
Transposing a single column table to row

Time:08-19

I'm feeling horribly stupid. I can't for the life of me figure out how to transpose the simplest of all cases from a column to a row. All pivot/unpivot whatever examples I find shows complex cases with aggregation and what not, but how the heck does one just transform this:

value
a
b

into this:

value1 value2
a b

It should be easy??

CodePudding user response:

Given a table like this:

create table so73413517 (
  value char(1)
)

insert so73413517(value) values('a'),('b') 
--,('c') -- etc.

If you will only ever want to return 1 row with 2 columns the max/min solution works best.

If you want 1 row but with more columns (but a fixed number) you can use row_number to give each row a unique, consecutive number, then you can do something like this

select max(case when r = 1 then value end) value1
, max(case when r = 2 then value end) value2
--, max(case when r = 3 then value end) value3 --etc.
from
(
    select row_number() over (order by value) r
    , value
    from so73413517
) x

Or

select [1], [2] 
--, [3] -- etc.
from
(
    select row_number() over (order by value) r
    , value
    from so73413517
) x
pivot
(
  max(value) for r in 
  (
    [1], [2]
    --,[3] --etc.
  )
) pvt

If you don't know how many rows are in the source/columns you want in your output, use dynamic SQL to create a column for each row (i.e. this uses the same logic as the pivot example above, but generates the query for the appropriate number of results:

declare @sql nvarchar(max) 

; with cte as (
    select count(1) rowNum from so73413517
    union all 
    select rowNum -1 from cte where rowNum  > 1
)
select @sql = coalesce(@sql   ',','')   quotename(rowNum) 
from cte 
order by rowNum

set @sql = 'select '   @sql   '
from
(
    select row_number() over (order by value) r
    , value
    from so73413517
) x
pivot
(
  max(value) for r in ('   @sql   ')
) pvt'

exec (@sql)

WARNING

That said; the above clearly isn't nice code... and normally that's a sign that something's wrong. If you're having to wrangle data in this way, look into whether you have the option to redesign your schema, or approach your underlying requirement with a different solution. If you're not sure how to do that feel free to ask on here.

i.e. This question feels like it may be an XY Problem.

Update

From the discussion in the comments, maybe ParseName would help.

declare @data nvarchar(128) = 'a,b,c'
 select parsename(tokenised, 3) value1
 , parsename(tokenised, 2) value2
 , parsename(tokenised, 1) value3
 from (select replace(@data, ',','.')) x(tokenised)

Note: ParseName is used to split apart instance.catalog.schema.table formatted names, so can only work with up to 4 x 128 char strings separated by dots.

More info on that trick and other options for your particular use case here: https://stackoverflow.com/a/70862904/361842

CodePudding user response:

with cte(val)as
(
  select 'a' union all
  select 'b'
)
select
  min(val) col1,max(val)col2
   from cte

Just for your example

  • Related