December 27, 2007 at 12:52 am
Hi Guys,
I have table which retrives values and i want to convert those values to columns but the values are not fixed it may increase.
example
ID TEXT
------------------------
1 US
2 UK
3 SINGAPORE
etc....
Required Result.
US UK SINGAPORE 'ETC.......(It may be around 20 to 30 values max to convert to columns)
Note: Using SQL SERVER 2000
Please advise.
December 27, 2007 at 12:59 am
[font="Courier New"]
use PIVOT
example:
DECLARE @tbl TABLE (Country VARCHAR(10), Sales MONEY)
INSERT INTO @tbl (Country, Sales)
SELECT 'UK', 250 UNION ALL
SELECT 'USA', 400 UNION ALL
SELECT 'INDIA', 500
SELECT * FROM @tbl
PIVOT (
SUM(Sales)
FOR Country IN (UK, USA, India)
) a
/*
OUTPUT
UK USA India
------------------------------
250.0000 400.0000 500.0000
*/[/font]
.
December 27, 2007 at 1:20 am
Hi Jacob,
Thanks for the query, the country filed is dynamic i cannot check using the case statment.
Please advise.
December 27, 2007 at 1:20 am
Hi Jacob,
Thanks for the query, the country filed is dynamic i cannot check using the case statment.
Please advise.
December 27, 2007 at 1:20 am
Hi Jacob,
Thanks for the query, the country filed is dynamic i cannot check using the case statment.
Please advise.
December 27, 2007 at 2:36 am
I guess we need to know the columns in advance to do a pivot operation. Probably you should make a list of countries and add it in the FOR() clause.
[font="Courier New"]DECLARE @tbl TABLE (Country VARCHAR(10), Sales MONEY)
INSERT INTO @tbl (Country, Sales)
SELECT 'UK', 250 UNION ALL
SELECT 'USA', 400 UNION ALL
SELECT 'INDIA', 500
SELECT * FROM @tbl
PIVOT (
SUM(Sales)
FOR Country IN (UK, USA, India, china, France, Italy, Spanish)
) a[/font]
.
December 27, 2007 at 5:42 am
Hi
You could do something like this...
Select all the countries and then use the EXECUTE Statement
declare @countries varchar(200)
declare @country varchar(20)
declare @piv varchar(1000)
set @countries = ''
declare cur cursor for
select country from tbl
open cur
Fetch NEXT From cur
Into @country
IF @@FETCH_STATUS <> 0
PRINT ' '
While @@FETCH_STATUS = 0
Begin
set @countries = @countries + @country
Fetch NEXT From cur
Into @country
if @@FETCH_STATUS = 0
set @countries = @countries + ', '
End
Close cur
Deallocate cur
set @piv = '
select * from tbl
pivot (sum(sales)
for country in (' + @countries + ')) a'
execute (@piv)
December 27, 2007 at 8:20 am
Oh my... we've gone from using a new function that (apparently) can't do the job, to using a Cursor...
Heh... Let's get back to the basics folks!
Consider the following PIVOT example from Books Online...
USE AdventureWorks;
GO
SELECT VendorID, [164] AS Emp1, [198] AS Emp2, [223] AS Emp3, [231] AS Emp4, [233] AS Emp5
FROM
(SELECT PurchaseOrderID, EmployeeID, VendorID
FROM Purchasing.PurchaseOrderHeader) p
PIVOT
(
COUNT (PurchaseOrderID)
FOR EmployeeID IN
( [164], [198], [223], [231], [233] )
) AS pvt
ORDER BY VendorID
Personnally, I'm not impressed... everything is hard-coded so far as the EmployeeID goes. Also, within the "IN" in the Pivot, I've been pretty much unsuccessful using a simple SELECT instead of a hard-coded list. Even if I could, the outer SELECT seems to require some hard-coding to identiry column names in this case.
Now, take a look at the "old school" Dynamic SQL method of Cross-Tabs...
--===== Identify the database to use
USE AdventureWorks
GO
--===== Declare the dynamic SQL variables
DECLARE @SQL1 VARCHAR(MAX)
DECLARE @SQL2 VARCHAR(MAX)
DECLARE @SQL3 VARCHAR(MAX)
--===== Create the SELECT and non-aggragated column(s)
SELECT @SQL1 = 'SELECT VendorID,' + CHAR(10)
--===== Create the aggragated list using the data as the driver for column names
SELECT @SQL2 = ISNULL(@SQL2 + ','+ CHAR(10),'')
+ 'STR(SUM(CASE WHEN EmployeeID = ' + CONVERT(VARCHAR(10),EmployeeID)
+ ' THEN 1 ELSE 0 END),12) AS Emp' + CONVERT(VARCHAR(10),EmployeeID) + '_Count' + ',' + CHAR(10)
+ 'STR(SUM(CASE WHEN EmployeeID = ' + CONVERT(VARCHAR(10),EmployeeID)
+ ' THEN SubTotal ELSE 0 END),12,2) AS Emp' + CONVERT(VARCHAR(10),EmployeeID) + '_Sales'
FROM Purchasing.PurchaseOrderHeader
GROUP BY EmployeeID
--===== Add line totals to the aggragated column names
SELECT @SQL2 = @SQL2 + ','+ CHAR(10)
+ 'STR(COUNT(*),12) AS Total_Count' + ',' + CHAR(10)
+ 'STR(SUM(SubTotal),12,2) AS Total_Sales' + CHAR(10)
--===== Create the FROM, GROUP BY, and ORDER BY
SELECT @SQL3 = 'FROM Purchasing.PurchaseOrderHeader' + CHAR(10)
+ 'GROUP BY VendorID' + CHAR(10)
+ 'ORDER BY VendorID'
--===== Execute the dynamic SQL to create the report
-- Uncomment the PRINT statement if you want to see the SQL
--PRINT @SQL1 + @SQL2 + @SQL3
EXEC (@SQL1 + @SQL2 + @SQL3)
Dunno... maybe I'm an idiot and just don't know how to use Pivot. If that's true, someone show us how to use Pivot to dynamically expand the columns like I did in the code above. In fact, show us how to use PIVOT to make the same report as I did in the code above. Not being a smart-guy about this... would really like to learn because, right now, PIVOT is looking pretty useless to me.
--Jeff Moden
Change is inevitable... Change for the better is not.
December 27, 2007 at 8:49 am
I agree with Jeff that PIVOT operator is not that useful in the specific task that we saw. Infact, it is not very helpful with variable number of columns.
However, i am using PIVOT in several reports where I had fixed number of columns. It includes sales figures of last 12 months, of quarter, weekly data etc etc.
I saw that PIVOT is much faster than doing subqueries or CASE statements to transform aggregated data to columns.
I think, even when we need dynamic columns, a DYNAMIC PIVOT QUERY might be a better choice. But of course, do not use a CURSOR. Just use a SELECT query to build the columns list...
DECLARE @countries VARCHAR(1000)
SET @countries = ''
select @countries = @countries + CASE WHEN LEN(@Countries) > 1 THEN ',' ELSE '' END + CountryName
FROM Countries
.
December 27, 2007 at 9:07 am
I saw that PIVOT is much faster than doing subqueries or CASE statements to transform aggregated data to columns
Jacob, if you have an example of that and a smattering of what the data is, I sure wouldn't mind doing a performance test.
--Jeff Moden
Change is inevitable... Change for the better is not.
December 27, 2007 at 10:46 am
Hi Jeff,
I must admit that my previous statement did not turn to be 100% correct. I just created a test table and populated some test data. I did some testing, and I did not really find a considerable performance differences by using PIVOT.
I was under the impression that PIVOT is much better than the old approach after trying it while optimizing a stored procedure recently. Though it was helpful on that specific situation, there does not seem to be a difference in most of the cases I tested. I think behind the screens SQL Server does the same old trick.
Thank you for clearing this point.
.
December 27, 2007 at 11:06 am
Thanks for the feedback, Jacob...
Yeah, the "same old trick" you're be speaking about is the aggragates themselves.
--Jeff Moden
Change is inevitable... Change for the better is not.
December 27, 2007 at 11:44 am
Dynamic pivot has limited flexibility, as you can only aggregate one column at a time. There are plenty workarounds to accomplish multiple aggregations but the logic becomes overly complicated.
I will continue to stick to the basics and PIVOT will continue to sit on the backburner, until it becomes useful.
As an FYI, this is an example of how one would gather dynamic columns versus "hard coding"
DECLARE @cols NVARCHAR(2000)
SELECT @cols = STUFF(( SELECT DISTINCT TOP 100 PERCENT
'],[' + LTRIM(STR(t2.EmployeeID))
FROM Purchasing.PurchaseOrderHeader AS t2
ORDER BY '],[' + LTRIM(STR(t2.EmployeeID))
FOR XML PATH('')
), 1, 2, '') + ']'
DECLARE @sql AS nvarchar(MAX)
SET @sql = N'
SELECT VendorID, ' + @cols + '
FROM
(SELECT PurchaseOrderID, EmployeeID, VendorID
FROM Purchasing.PurchaseOrderHeader) p
PIVOT
(
COUNT (PurchaseOrderID)
FOR EmployeeID IN
( ' + @cols + ' )
) AS pvt
ORDER BY VendorID'
EXEC sp_executesql @sql
December 27, 2007 at 12:21 pm
Heh... that about does it for me, Adam... I'm going to pivot the PIVOT command right into Unit 0 and use the old school methods I demo'd for this type of stuff.
--Jeff Moden
Change is inevitable... Change for the better is not.
December 28, 2007 at 4:19 am
Real world example using dynamic pivot:
I have processes where I want to run a series of tests gathering specific record counts at specific times. I collect the results of those tests in a table. I then want to display the results where each row is one test and each column is a test time.
Here is the table with two records:
CREATE TABLE [dbo].[XREF_RecordCountResults](
[Ind] [int] IDENTITY(1,1) NOT NULL,
[Test] [varchar](250) NOT NULL,
[SubTest] [varchar](250) NULL,
[TestOrder] [int] NOT NULL,
[ResultDesc] [varchar](250) NULL,
[Results] [int] NULL,
[TestDate] [datetime] NOT NULL,
CONSTRAINT [PK_XREF_RecordCountResults] PRIMARY KEY CLUSTERED
([Ind] ASC) ON [PRIMARY]
) ON [PRIMARY]
INSERT INTO xref_recordcountresults ([Test], [SubTest], [TestOrder], [ResultDesc], [Results], [TestDate])
VALUES ('Jackets', 'SubTest Classification', 300, 'Jacket test', 1389, '2007-12-21 17:00:28')
INSERT INTO xref_recordcountresults ([Test], [SubTest], [TestOrder], [ResultDesc], [Results], [TestDate])
VALUES ('Jackets', 'SubTest Classification', 300, 'Jacket test', 211145, '2007-12-22 01:31:30')
Here is the code that generates the report:
DECLARE@sqlvarchar(8000)
SET @sql = 'SELECT TestOrder, Test, SubTest, ResultDesc'
SELECT@sql = @sql + ', [' + CONVERT(varchar, TestDate, 121) + ']'
FROMXREF_RecordCountResults
GROUP BY TestDate
ORDER BY TestDate
FROM ( SELECT TestOrder, Test, SubTest, ResultDesc, Results, CONVERT(varchar, TestDate, 121) AS TestDateChar
FROM XREF_RecordCountResults) x
PIVOT (
SUM(Results) FOR TestDateChar IN ('
SELECT@sql = @sql + ', [' + CONVERT(varchar, TestDate, 121) + ']'
FROMXREF_RecordCountResults
GROUP BY TestDate
ORDER BY TestDate
) AS p'
SET @sql = REPLACE(@SQL, ' IN (, ', ' IN (') --Removes unneeded leading comma
--print @sql
exec (@SQL)
Add one more test:
INSERT INTO xref_recordcountresults ([Test], [SubTest], [TestOrder], [ResultDesc], [Results], [TestDate])
VALUES ('Jackets', 'SubTest Classification', 300, 'Jacket test', 211792, '2007-12-22 05:28:12')
Rerun the report and note that the new column is picked up.
Viewing 15 posts - 1 through 15 (of 18 total)
You must be logged in to reply to this topic. Login to reply