views:

320

answers:

3
+2  Q: 

SQL Server - PIVOT

We are working on a C# application, we've been using Linq to SQL or standard ADO (when performance needed) to work with SQL Server.

We have a table layed out like so:

Customer ID, Year/Month, Product Name, Quantity

Each customer has additional columns per product.

We need display this information in a data grid like so:

Customer, Year/Month, Product A Quantity, Product B Quantity, Product C Quantity, etc.

What query could give us these results? And how could it be dynamic no matter what products are added and removed? We will be using a ListView in WPF for displaying the data.

We would just store the information differently, but they can add/remove products all the time.

Will PIVOT work?

(PS - the product names are really in another table for normalization, I changed it a little for simplicity for you guys)

+2  A: 

The sql pivot command can be used but it requires the columns to be hard-coded. You could either hard-code them, use dynamic sql to generate the columns, or only get the raw data from sql without a pivot and do the data massaging in c#.

DyingCactus
+1  A: 

You can use pivot with dynamic SQL. Following T-SQL code is taken from this article on sqlteam.com. I've tried to modify the sample for your needs. Also beware of dangers using dynamic SQL, it might lead to SQL Injection if a product name contains apostrophe.

Create a stored proc first;

CREATE PROCEDURE crosstab 
@select varchar(8000),
@sumfunc varchar(100), 
@pivot varchar(100), 
@table varchar(100) 
AS

DECLARE @sql varchar(8000), @delim varchar(1)
SET NOCOUNT ON
SET ANSI_WARNINGS OFF

EXEC ('SELECT ' + @pivot + ' AS pivot INTO ##pivot FROM ' + @table + ' WHERE 1=2')
EXEC ('INSERT INTO ##pivot SELECT DISTINCT ' + @pivot + ' FROM ' + @table + ' WHERE ' 
+ @pivot + ' Is Not Null')

SELECT @sql='',  @sumfunc=stuff(@sumfunc, len(@sumfunc), 1, ' END)' )

SELECT @delim=CASE Sign( CharIndex('char', data_type)+CharIndex('date', data_type) ) 
WHEN 0 THEN '' ELSE '''' END 
FROM tempdb.information_schema.columns 
WHERE table_name='##pivot' AND column_name='pivot'

SELECT @sql=@sql + '''' + convert(varchar(100), pivot) + ''' = ' + 
stuff(@sumfunc,charindex( '(', @sumfunc )+1, 0, ' CASE ' + @pivot + ' WHEN ' 
+ @delim + convert(varchar(100), pivot) + @delim + ' THEN ' ) + ', ' FROM ##pivot

DROP TABLE ##pivot

SELECT @sql=left(@sql, len(@sql)-1)
SELECT @select=stuff(@select, charindex(' FROM ', @select)+1, 0, ', ' + @sql + ' ')

EXEC (@select)
SET ANSI_WARNINGS ON

Then try the following (I haven't test it, you might need to add qty to select statement)

EXECUTE crosstab 'select ProductID,CustomerID, YearMonth from sales group by ProductId', 'sum(qty)','ProductId','sales'
Ertugrul Tamer Kara
What kind of speed am I looking at here? Will queries calling this stored procedure be pretty slow for a table with 22 million records?
Jonathan.Peppers
The SP generates dynamic T-SQL code like `select customername,case when productid=1 then quantity else 0 END AS Product1, case .... from order group by customername`. My experience with production servers are limited, so I have no idea about 22 million records. But I suggest you to create index on productname column then test the speed with records created by sql server stress / record generating tools.
Ertugrul Tamer Kara
Your answer has to be correct, unless there is an alternate way to go about this. My issues are going to be speed vs flexibility, so we are going to have to just give it a try.
Jonathan.Peppers
A: 

If you want to try a method that doesn't involve dynamic SQL, you could go through C#.

This guy ran a test comparing the two: http://weblogs.sqlteam.com/jeffs/jeffs/archive/2005/05/12/5127.aspx

Chris
This is basically what I ended up doing in the end, I needed our solution to be pretty dynamic and using C# seemed fast enough for our purposes. We did not do any perf testing between PIVOT and C#, PIVOT seemed unwieldy and difficult to maintain since we would have had to manually use column names or dynamic SQL. C# seemed much easier to deal with.
Jonathan.Peppers