Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 General SQL Server Forums
 New to SQL Server Programming
 Dynamically convert Rows to Column

Author  Topic 

calvinfoo
Posting Yak Master

129 Posts

Posted - 2011-04-25 : 03:17:27
Dear Gurus,

How to convert rows to columns on dynamic basis?

I am using Sales vs Year as example. I have a Sales table and Items table as below, and I want to sum the sales by year and ItemID, then convert the rows to columns on dynamic basis.

[_items](
[itemID] [int] IDENTITY(1,1) NOT NULL,
[ItemDesc] [nvarchar](50)
)


[_Sales](
[SalesID] [int] IDENTITY(1,1) NOT NULL,
[ItemID] [int] NULL,
[Qty] [int] NULL,
[SalesDate] [datetime] NULL
)


If using ASP or fixed column name, then it is easy to generate a static report table (SQL below).

But what if:

1. The user is allowed to select a year range by themself? they could've select year 2000 to 20011 consists of total of 12 columns

2. Is there any better way to do it than my SQL below?

select _Items.itemID, _Items.ItemDesc, yr2008.yr2008, yr2009.yr2009, yr2010.yr2010, yr2011.yr2011  from _items 
left outer join
(select itemID, sum(Qty) as yr2008 from _sales where (year(SalesDate) = 2008)
group by itemID, year(SalesDate)) as yr2008 on _items.itemID = yr2008.itemID
left outer join
(select itemID, sum(Qty) as yr2009 from _sales where (year(SalesDate) = 2009)
group by itemID, year(SalesDate)) as yr2009 on _items.itemID = yr2009.itemID
left outer join
(select itemID, sum(Qty) as yr2010 from _sales where (year(SalesDate) = 2010)
group by itemID, year(SalesDate)) as yr2010 on _items.itemID = yr2010.itemID
left outer join
(select itemID, sum(Qty) as yr2011 from _sales where (year(SalesDate) = 2011)
group by itemID, year(SalesDate)) as yr2011 on _items.itemID = yr2011.itemID

madhivanan
Premature Yak Congratulator

22864 Posts

Posted - 2011-04-25 : 04:05:45
See if this helps
http://beyondrelational.com/blogs/madhivanan/archive/2008/08/27/dynamic-pivot-in-sql-server-2005.aspx

Madhivanan

Failing to plan is Planning to fail
Go to Top of Page

calvinfoo
Posting Yak Master

129 Posts

Posted - 2011-04-25 : 06:26:08
I tried the example from the link given, it didn't work. I even tried with the simplest Query. If you can give me more help I would really appreciate that

exec dynamic_pivot (
'SELECT ItemID, Qty, SalesDate From _Sales',
'year(SalesDate)',
'SUM(Qty)'
)


Msg 102, Level 15, State 1, Line 2
Incorrect syntax near 'SELECT ItemID, Qty, SalesDate From _Sales'.
Go to Top of Page

khtan
In (Som, Ni, Yak)

17689 Posts

Posted - 2011-04-25 : 06:41:09
you have got the exec syntax wrong

exec dynamic_pivot
@select = 'SELECT ItemID, Qty, SalesDate From _Sales',
@PivotCol = 'year(SalesDate)',
@Summaries = 'SUM(Qty)'



KH
[spoiler]Time is always against us[/spoiler]

Go to Top of Page

calvinfoo
Posting Yak Master

129 Posts

Posted - 2011-04-25 : 07:29:34
Hi khtan,
Thanks for the tips! It works now. This is my result that I wanted after some fine tuning:

exec dynamic_pivot
@select = 'SELECT _items.itemID, _items.ItemDesc, _Sales.Qty FROM _items LEFT OUTER JOIN _Sales ON _items.itemID = _Sales.ItemID',
@PivotCol = 'year(SalesDate)',
@Summaries = 'SUM(Qty)'


1. I have no clue why the above examples in the above URL didn't stated anywhere that I need to use @select, @PivotCol, @Summaries

2. How do I sort the result?
Go to Top of Page

khtan
In (Som, Ni, Yak)

17689 Posts

Posted - 2011-04-25 : 07:41:44
1. I have no clue why the above examples in the above URL didn't stated anywhere that I need to use @select, @PivotCol, @Summaries
using @select, @PivotCol etc is optional, it will work with below

exec dynamic_pivot
'SELECT ItemID, Qty, SalesDate From _Sales',
'year(SalesDate)',
'SUM(Qty)'

But it is good practice to specify that.

2. Sorting is perform inside the dynamic_pivot stored procedure. You can modify it (or ask madhi for help) to perform the required sorting


KH
[spoiler]Time is always against us[/spoiler]

Go to Top of Page

calvinfoo
Posting Yak Master

129 Posts

Posted - 2011-04-25 : 07:57:08
Ok, I'll try to modify it myself first... if I screwed up.. then I'll ask help again. hehehe... thanks for the tips
Go to Top of Page

madhivanan
Premature Yak Congratulator

22864 Posts

Posted - 2011-04-25 : 09:20:38
If you want to order it by the first column, use order by 1 at the end

Madhivanan

Failing to plan is Planning to fail
Go to Top of Page

calvinfoo
Posting Yak Master

129 Posts

Posted - 2011-04-25 : 21:28:23
I tried Madhi's suggestion, but doesn't work... please advise.

exec dynamic_pivot
@select = 'SELECT _items.itemID, _items.ItemDesc, _Sales.Qty FROM _items JOIN _Sales ON _items.itemID = _Sales.ItemID order by 1',
@PivotCol = 'year(SalesDate)',
@Summaries = 'SUM(Qty)'

Msg 1033, Level 15, State 1, Line 1
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified.
Go to Top of Page

khtan
In (Som, Ni, Yak)

17689 Posts

Posted - 2011-04-25 : 21:38:25
he meant to change the dynamic_pivot stored procedure

select @sql=
'
select * from
(
'+@select+'
) as t
pivot
(
'+@Summaries+' for pivot_col in ('+@pivot+')
) as p
order by 1
'



KH
[spoiler]Time is always against us[/spoiler]

Go to Top of Page

calvinfoo
Posting Yak Master

129 Posts

Posted - 2011-04-25 : 21:42:41
I managed to figure out that I think you mean put "order by 1" into your SP, that I believe I have to alter and add at least one sorting variable, or another one for desc or asc. I'll see what I can do...
Go to Top of Page

calvinfoo
Posting Yak Master

129 Posts

Posted - 2011-04-25 : 21:56:45
Hi all,

You guys been a great gurus, really appreciate that. I managed to alter the SP with my paper thin knowledge, and here is what I come out with, which seems to be working. Please verify


set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[dynamic_pivot]
(
@select varchar(2000),
@PivotCol varchar(100),
@Summaries varchar(100),
@OrderBy varchar(2),
@AscDesc varchar(4)

) as
declare @pivot varchar(max), @sql varchar(max)
select @select =replace(@select,'select ','select '+@PivotCol+' as pivot_col,')


create table #pivot_columns (pivot_column varchar(100))

Select @sql='select distinct pivot_col from ('+@select+') as t'

insert into #pivot_columns
exec(@sql)

select @pivot=coalesce(@pivot+',','')+'['+pivot_column+']'from #pivot_columns

select @sql=
'
select * from
(
'+@select+'
) as t
pivot
(
'+@Summaries+' for pivot_col in ('+@pivot+')
) as p
order by '+@OrderBy+' '+@AscDesc+'
'

exec(@sql)
Go to Top of Page

khtan
In (Som, Ni, Yak)

17689 Posts

Posted - 2011-04-25 : 22:05:06
if you want to be a bit more flexible and cater for more than 1 order by columns

ALTER procedure [dbo].[dynamic_pivot]
(
@select varchar(2000),
@PivotCol varchar(100),
@Summaries varchar(100),
@OrderBy varchar(100),
@AscDesc varchar(4)
) as

...


) as p
'
+ isnull('ORDER BY '+ @OrderBy, '')



and specify asc or desc in the @orderby paramters
example

exec dynamic_pivot
@select = 'SELECT _items.itemID, _items.ItemDesc, _Sales.Qty FROM _items JOIN _Sales ON _items.itemID = _Sales.ItemID',
@PivotCol = 'year(SalesDate)',
@Summaries = 'SUM(Qty)'
@OrderBy = 'itemID DESC, ItemDesc'


you can use column sequence ORDER BY 1 DESC, 2 etc, but personally i prefer the column name as one look at it i know the sequence of the result i am returning


KH
[spoiler]Time is always against us[/spoiler]

Go to Top of Page

calvinfoo
Posting Yak Master

129 Posts

Posted - 2011-04-25 : 22:38:43
Ok. Thanks for the tips.

The reason I split it into two variables is because I need to output it to an ASP/AJAX table which I did, where user can simply click on the row header to sort by which column number. Then if user click same column again, it sort Desc instead.

Initially I was using only one variable to control the OrderBy, but later I found that it is easier on my ASP/AJAX Table sorting with two variables.

Which IMO, I split it two variables is easier for my ASP to work around as the columns my table column is hard coded. Which means if my table has 10 columms, I have to maintain one variable with 20 values ("Column A"/"Column A DESC" x 10) Simple, neat but long. Else if I split the AscDesc, then I have to maintain two variables, but only 10+2 values. Short, messy, somehow easier from ASP/AJAX Table. You get what I mean?
Go to Top of Page

calvinfoo
Posting Yak Master

129 Posts

Posted - 2011-04-27 : 22:24:35
Hi guys,
The tips and scripts all has been working very well to me. One problem I am encountering when I am trying to Group by Month. If the user selected a date range across more than a year, example: 2010-01-01 to 2011-12-31. If i would group by month using

@PivotCol = month(Ordered_date)

I believe it will sum all months together regardless the year.

I tried

@PivotCol = 'year(Ordered_date)+''-''+month(Ordered_date)',

but it doesn't work that way.

Please advise.

Go to Top of Page

calvinfoo
Posting Yak Master

129 Posts

Posted - 2011-04-27 : 22:56:45
I managed to work around it by

@PivotCol = 'year(Ordered_date)*100+month(Ordered_date)'

which is closest to what I need to get 201001, 201002, etc... but still doesn't give me 2010-01 as I would prefer... any suggestion?
Go to Top of Page

calvinfoo
Posting Yak Master

129 Posts

Posted - 2011-04-28 : 00:02:45
another related problem to this SP that I encountered, but I have no idea how to fix this. Hope you guys can give me some hints.
When I EXEC dynamic_pivot, it works fine with my ASP script, but it will generate error to my ASP Script when no record is returned, I never encountered this problem, all the while I have been using the same connection_open() function never encountered such problem even with zero records returned, this is the first time, please advise:

ADODB.Recordset error '800a0e78' 
Operation is not allowed when the object is closed.
/ReportPivot.asp, line 116


sqlstr = "EXEC dynamic_pivot......."
set recobj = connection_open(sqlstr)
strRecordCount = recobj.RecordCount '<-- Line 116

'connection_open(sqlstr) as below
function connection_open(sqlstring)
dim recobj01, connobj
set connobj = Server.CreateObject("ADODB.Connection")
connobj.Open "DSN=myDSN","userName","Password"
set recobj01 = Server.CreateObject("ADODB.Recordset")
recobj01.CursorLocation = 3
recobj01.Open sqlstring, connobj
set connection_open = recobj01
set recobj01 = nothing
set connobj = nothing
if err <> 0 then
response.write sqlstr
response.write "<DIV CLASS=""warning"">An error occurred: " & err.description & "</DIV>"
end if
end function
Go to Top of Page

calvinfoo
Posting Yak Master

129 Posts

Posted - 2011-05-12 : 23:43:39
Dear Gurus,

I am facing a problem here...

CREATE TABLE _TEST (SalesPerson CHAR(1),SalesVol INT, SalesDate SMALLDATETIME)
INSERT INTO _TEST VALUES ('X',0,'2000-01-01')
INSERT INTO _TEST VALUES ('Y',10,'2000-01-01')
INSERT INTO _TEST VALUES ('Z',0,'2000-01-01')
INSERT INTO _TEST VALUES ('X',0,'2000-01-02')
INSERT INTO _TEST VALUES ('Y',20,'2000-01-02')
INSERT INTO _TEST VALUES ('Z',10,'2000-01-02')
INSERT INTO _TEST VALUES ('X',10,'2000-01-03')
INSERT INTO _TEST VALUES ('Y',0,'2000-01-03')
INSERT INTO _TEST VALUES ('Z',20,'2000-01-03')

EXEC dynamic_pivot
@select = 'SELECT SalesPerson, SalesVol, SalesDate FROM _TEST',
@PivotCol = 'SalesPerson',
@Summaries = 'sum(SalesVol)'

But I keep getting this error, please advise
Msg 8624, Level 16, State 17, Line 2
Internal Query Processor Error: The query processor could not produce a query plan. For more information, contact Customer Support Services.
Go to Top of Page

khtan
In (Som, Ni, Yak)

17689 Posts

Posted - 2011-05-12 : 23:49:47
what is the edition / version of the SQL Server you are using ?

Do you have the latest SP applied ?


KH
[spoiler]Time is always against us[/spoiler]

Go to Top of Page

calvinfoo
Posting Yak Master

129 Posts

Posted - 2011-05-12 : 23:56:12
I am using SQL Server 2005 Express.

But I have been using the same dynamic_pivot for my earlier tables, it is working fine, until I tried on this... the error really puzzles me...
Go to Top of Page

calvinfoo
Posting Yak Master

129 Posts

Posted - 2011-05-12 : 23:58:54
Microsoft SQL Server Management Studio Express 9.00.2047.00
Microsoft Data Access Components (MDAC) 2000.086.3959.00 (srv03_sp2_rtm.070216-1710)
Microsoft MSXML 2.6 3.0 5.0 6.0
Microsoft Internet Explorer 8.0.6001.18702
Microsoft .NET Framework 2.0.50727.3615
Operating System 5.2.3790
Go to Top of Page
    Next Page

- Advertisement -