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.
| 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 columns2. 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.itemIDleft 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.itemIDleft 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.itemIDleft 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 |
|
|
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 thatexec dynamic_pivot ('SELECT ItemID, Qty, SalesDate From _Sales','year(SalesDate)','SUM(Qty)')Msg 102, Level 15, State 1, Line 2Incorrect syntax near 'SELECT ItemID, Qty, SalesDate From _Sales'. |
 |
|
|
khtan
In (Som, Ni, Yak)
17689 Posts |
Posted - 2011-04-25 : 06:41:09
|
you have got the exec syntax wrongexec dynamic_pivot @select = 'SELECT ItemID, Qty, SalesDate From _Sales', @PivotCol = 'year(SalesDate)', @Summaries = 'SUM(Qty)' KH[spoiler]Time is always against us[/spoiler] |
 |
|
|
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, @Summaries2. How do I sort the result? |
 |
|
|
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, @Summariesusing @select, @PivotCol etc is optional, it will work with belowexec 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] |
 |
|
|
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 |
 |
|
|
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 endMadhivananFailing to plan is Planning to fail |
 |
|
|
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 1The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified. |
 |
|
|
khtan
In (Som, Ni, Yak)
17689 Posts |
Posted - 2011-04-25 : 21:38:25
|
he meant to change the dynamic_pivot stored procedureselect @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] |
 |
|
|
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... |
 |
|
|
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 verifyset ANSI_NULLS ONset QUOTED_IDENTIFIER ONGOALTER 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_columnsexec(@sql)select @pivot=coalesce(@pivot+',','')+'['+pivot_column+']'from #pivot_columnsselect @sql=' select * from ( '+@select+' ) as t pivot ( '+@Summaries+' for pivot_col in ('+@pivot+') ) as p order by '+@OrderBy+' '+@AscDesc+'' exec(@sql) |
 |
|
|
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 columnsALTER 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 paramtersexampleexec 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] |
 |
|
|
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? |
 |
|
|
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. |
 |
|
|
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? |
 |
|
|
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 116sqlstr = "EXEC dynamic_pivot......."set recobj = connection_open(sqlstr)strRecordCount = recobj.RecordCount '<-- Line 116'connection_open(sqlstr) as belowfunction 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 ifend function |
 |
|
|
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 adviseMsg 8624, Level 16, State 17, Line 2Internal Query Processor Error: The query processor could not produce a query plan. For more information, contact Customer Support Services. |
 |
|
|
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] |
 |
|
|
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... |
 |
|
|
calvinfoo
Posting Yak Master
129 Posts |
Posted - 2011-05-12 : 23:58:54
|
| Microsoft SQL Server Management Studio Express 9.00.2047.00Microsoft 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.18702Microsoft .NET Framework 2.0.50727.3615Operating System 5.2.3790 |
 |
|
|
Next Page
|
|
|
|
|