Search code examples
sqlsql-serversql-server-2000pivotunpivot

Rotate/pivot 1000 columns to rows


I inherited a DB from which I need to extract data in columns that is currently arranged in rows. The query to retrieve the data is:

select distinct 
    LIMSNo, p250.*, p500.*, p750.*, p1000.* 
from 
    results as r
    inner join Points250 as p250 
    on r.resultsid = p250.resultsid and p250.channel = 1
    inner join Points500 as p500 
    on r.resultsid = p500.resultsid and p500.channel = 1
    inner join Points750 as p750 
    on r.resultsid = p750.resultsid and p750.channel = 1
    inner join Points1000 as p1000 
    on r.resultsid = p1000.resultsid and p1000.channel = 1
where 
    r.limsno between '120053698' and '120053704'

which produces

LIMSNo           P0001              P0002              P0003   ...     P1000  
120053698        251.6667           302.0196           302.2353        305.9608
120053699        291.6667           342.6545           347.9635        353.6236
120053700        243.3333           298.3206           296.7235        299.5342
120053701        308.3333           365.8397           365.4071        368.3206
120053702        315                363.4153           366.6052        373.1695

Note that there are 1000 columns (P0001...P1000)

I want to pivot/rotate the data so that I get this output:

        120053698        120053699        120053700        120053701        120053702 
P0001   251.6667         291.6667         243.3333         308.3333         315
P0002   302.0196         342.6545         298.3206         365.8397         363.4153
...
P1000   305.9608         353.6236         299.5342         368.3206         373.1695

How do I structure the SQL query? The DB is SQL 2000.

I have tried a number of approaches to this problem. My latest attempt was:

create table #tempCols
(
    ColName nchar(15)
)

insert into #tempCols
select 
    column_name 
from 
    information_schema.columns
where 
    table_name = 'Points250' 
and column_name like 'P%'

insert into #tempCols
select 
    column_name 
from 
    information_schema.columns
where 
    table_name = 'Points500' 
and column_name like 'P%'

insert into #tempCols
select 
    column_name 
from 
    information_schema.columns
where 
    table_name = 'Points750' 
and column_name like 'P%'

insert into #tempCols
select 
    column_name 
from 
    information_schema.columns
where 
    table_name = 'Points1000' 
and column_name like 'P%'

create table #LIMSList
(
    LIMSNumber nchar(9)
)
insert into #LIMSList
select LimsNo from results
where LimsNo between '100030460' and '100030500'

declare @colList varchar(max)
select @colList = COALESCE(@colList + ',' ,'') + ColName from #tempCols

declare @command varchar(max)
set @command = 
'
    select LimsNo, Point, Data
    from (
        select LimsNo, p250.*, p500.*, p750.*, p1000.* 
        from 
            results as r
            inner join Points250 as p250 
            on r.resultsid = p250.resultsid and p250.channel = 1
            inner join Points500 as p500 
            on r.resultsid = p500.resultsid and p500.channel = 1
            inner join Points750 as p750 
            on r.resultsid = p750.resultsid and p750.channel = 1
            inner join Points1000 as p1000 
            on r.resultsid = p1000.resultsid and p1000.channel = 1
        where 
            limsno in (select LimsNo from #LIMSList)) d
            unpivot (Data for Point in (' + @colList + ')) as unpvt
'

print @command

exec(@command)

drop table #tempCols
drop table #LIMSList

But the error I get is:

Msg 8156, Level 16, State 1, Line 1
The column 'ResultsID' was specified multiple times for 'd'.
Msg 8156, Level 16, State 1, Line 1
The column 'ResultsID' was specified multiple times for 'unpvt'.

Solution

  • Since SQL Server 2000 does not have either an UNPIVOT or PIVOT operator, you will have to use UNION ALL for the UNPIVOT and then a CASE statement for the PIVOT. Here is a dynamic solution that should work:

    DECLARE @query  AS NVARCHAR(MAX),
      @rowCount as int = 1,
      @unpivotCount as int = 0,
      @pivotCount as int,
      @unRow as varchar(10) = '',
      @pivotRow as varchar(10) = ''
    
    select c.Name col
    into #colsUnpivot
    from sys.columns as C
    where C.object_id = object_id('yourtable') and
        C.name LIKE 'P%'
    
    set @unpivotCount = (select count(*) from #colsUnpivot)
    
    -- unpivot the data
    while @rowCount <= @unpivotCount 
      begin
        set @unRow = (select Top 1 col from #colsUnpivot)
    
        set @query =' insert into unpivotData (LIMSNo, Val, Col)
                      select LIMSNo, ' + @unRow + ' as val, ''' 
                        + @unRow  + ''' as col
                      from yourtable'
    
         exec(@query)
    
         delete from #colsUnpivot where col = @unRow
    
         if @rowCount <= @unpivotCount 
            set @rowCount = @rowCount + 1
      end
    
    --select * 
    --from unpivotData
    
    -- pivot the data 
    select distinct LIMSNo
    into #colsPivot
    from yourtable
    
    set @pivotCount= (select COUNT(*) from #colsPivot) 
    -- reset rowcount
    set @rowCount = 1
    set @query = ''
    
    ---- create the CASE string
    while @rowCount <= @pivotCount
        begin
            set @pivotRow = (select Top 1 LIMSNo from #colsPivot)
    
            set @query = @query + ', max(case when LIMSNo = ' + @pivotRow + ' then val end) as ''' + @pivotRow + ''''
    
            delete from #colsPivot where LIMSNo = @pivotRow
    
            if @rowCount <= @pivotCount
                begin
                    set @rowCount = @rowCount + 1
                    print @rowCount
                end
        end
    
    -- add the rest of the SQL Statement
    set @query = 'SELECT col ' + @query + ' from dbo.unpivotData group by col'
    
    exec(@query)
    
    delete from dbo.unpivotData
    
    drop table #colsUnpivot
    drop table #colspivot
    

    I created a table called UnpivotData to hold the data for use with in the PIVOT process.

    See SQL Fiddle with Demo