Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically create columns sql

I have a table of Customers

Customer ID        Name             1               John            2               Lewis            3               Mary          

I have another table CustomerRewards

 TypeID           Description    1                Bronze    2                Silver    3                Gold    4               Platinum    5               AnotherOne 

And the final table

 RewardID          TypeID          CustomerID     1                1                 1     2                1                 1     3                2                 1     4                2                 2 

The customerTypes table is dynamic, many of these types can be added and removed. Basically all I want is the columns to be generated dynamically and a count in each, something like

CustomerName        Bronze        Silver     Gold      Platinum     AnotherOne    total    John               2             1         0           0             0           3    Lewis              0             1         0           0             0           1  Grand TOTAL          2             2         0           0             0           4 

The problem like I said it that the types are dynamic and the customers are dynamic so I need the columns to be dynamic depending on the types in the system

I have tagged c# as I need this in a DataGridView

Thanks in advance

like image 730
CR41G14 Avatar asked Sep 28 '12 15:09

CR41G14


People also ask

What are dynamic columns?

Dynamic columns is a feature that allows one to store different sets of columns for each row in a table. It works by storing a set of columns in a blob and having a small set of functions to manipulate it.

How do I get column names dynamically in SQL?

Solution 1. The only way to do that is use build your command into a string, and use EXEC to run the result: table and column name parsing is conducted early in the SQL command execution process and have been replaced before any of the actual query is executed.

Can I use CTE in dynamic SQL?

Using CTEs, for instance, you can use SELECT from <subquery> in Open SQL. In my case I needed to execute dynamic SELECT count( DISTINCT col1, col2, …) which is not possible in the regular OpenSQL.


1 Answers

You will want to use a PIVOT function for this. If you have a known number of columns, then you can hard-code the values:

select name, [Bronze], [Silver], [Gold], [Platinum], [AnotherOne] from (   select c.name,     cr.description,     r.typeid   from customers c   left join rewards r     on c.id = r.customerid   left join customerrewards cr     on r.typeid = cr.typeid ) x pivot (   count(typeid)   for description in ([Bronze], [Silver], [Gold], [Platinum], [AnotherOne]) ) p; 

See SQL Fiddle with Demo.

Now if you have an unknown number of columns, then you can use dynamic SQL to PIVOT:

DECLARE @cols AS NVARCHAR(MAX),     @query  AS NVARCHAR(MAX)  select @cols = STUFF((SELECT ',' + QUOTENAME(description)                      from customerrewards                     group by description, typeid                     order by typeid             FOR XML PATH(''), TYPE             ).value('.', 'NVARCHAR(MAX)')          ,1,1,'')  set @query = 'SELECT name,' + @cols + ' from               (                 select c.name,                   cr.description,                   r.typeid                 from customers c                 left join rewards r                   on c.id = r.customerid                 left join customerrewards cr                   on r.typeid = cr.typeid             ) x             pivot              (                 count(typeid)                 for description in (' + @cols + ')             ) p '  execute(@query) 

See SQL Fiddle With Demo

If you need to include the Total column, then you can use ROLLUP (Static Version Demo):

select name, sum([Bronze]) Bronze, sum([Silver]) Silver,    sum([Gold]) Gold, sum([Platinum]) Platinum, sum([AnotherOne]) AnotherOne from  (   select name, [Bronze], [Silver], [Gold], [Platinum], [AnotherOne]   from   (     select c.name,       cr.description,       r.typeid     from customers c     left join rewards r       on c.id = r.customerid     left join customerrewards cr       on r.typeid = cr.typeid   ) x   pivot   (     count(typeid)     for description in ([Bronze], [Silver], [Gold], [Platinum], [AnotherOne])   ) p ) x group by name with rollup 

Dynamic version (Demo):

DECLARE @cols AS NVARCHAR(MAX),     @colsRollup AS NVARCHAR(MAX),     @query  AS NVARCHAR(MAX)  select @cols = STUFF((SELECT ',' + QUOTENAME(description)                      from customerrewards                     group by description, typeid                     order by typeid             FOR XML PATH(''), TYPE             ).value('.', 'NVARCHAR(MAX)')          ,1,1,'')  select @colsRollup        = STUFF((SELECT ', Sum(' + QUOTENAME(description) + ') as '+ QUOTENAME(description)                     from customerrewards                     group by description, typeid                     order by typeid             FOR XML PATH(''), TYPE             ).value('.', 'NVARCHAR(MAX)')          ,1,1,'')   set @query            = 'SELECT name, '+ @colsRollup + '              FROM              (                 SELECT name,' + @cols + ' from                   (                     select c.name,                       cr.description,                       r.typeid                     from customers c                     left join rewards r                       on c.id = r.customerid                     left join customerrewards cr                       on r.typeid = cr.typeid                 ) x                 pivot                  (                     count(typeid)                     for description in (' + @cols + ')                 ) p                ) x1               GROUP BY name with ROLLUP'  execute(@query) 
like image 75
Taryn Avatar answered Oct 05 '22 21:10

Taryn