Search code examples
sqlsql-serverkey-valuerelationalrelational-division

Group key-value columns into a rows per group with column per key


I need to show groups of key/value pairs with n keys stored in a MS SQL Database in a table with one record per group and one column per key.

I found that example but in my case the keys (finally the columns) are not fix and must come from the database too. Group key-value columns into a single row

Example:

CREATE TABLE entries
    ([group_id] int, [attr_key] varchar(20), [attr_value] varchar(20))
;

INSERT INTO entries
   ([group_id], [attr_key], [attr_value])

VALUES
   (1, 'color', 'blue'),
   (1, 'size', 'L'),
   (2, 'color', 'red'),
   (2, 'size', 'M'),
   (3, 'color', 'green'),
   (3, 'size', 'M'),
   (3, 'Vendor', 'myVendor') ,
   (3, 'picture', 'Bear') 
;

Test in Fiddle: http://sqlfiddle.com/#!18/c1ff2/1/0

| group_id | attr_key | attr_value |
|----------|----------|------------|
|        1 |    color |       blue |
|        1 |     size |          L |
|        2 |    color |        red |
|        2 |     size |          M |
|        3 |    color |      green |
|        3 |     size |          M |
|        3 |   vendor |   myVendor |
|        3 |  picture |       Bear |

The result should look like this:

| group_id | color | size | vendor   | picture | 
|----------|-------|------|----------|---------|
|        1 | blue  |  L   | NULL     |  NULL   |
|        2 | red   |  M   | NULL     |  NULL   |
|        3 | green |  M   | myVendor |  Bear   |

When later the user adds an Attribute the table also should have one column more without chnging the SQL query.


Solution

  • You should use dynamic pivot for inconstant number of attributes

    declare @columns varchar(max)
    declare @sql varchar(max)
    
    set @columns = stuff((select ',' + quotename(attr_key) from entries
    group by attr_key
    for xml path('')), 1, 1, '')
    
    set @sql = '
        select
            group_id, ' + @columns + '
        from
            entries
        pivot (
            max(attr_value) for attr_key in (' + @columns + ')
        ) p'
    
    
    exec (@sql)