jlimited
jlimited

Reputation: 685

Pivot rows to columns without aggregate

Trying to figure how to write a dynamic pivot SQL statement. Where TEST_NAME could have up to 12 different values (thus having 12 columns). Some of the VAL will be Int, Decimal, or Varchar data types. Most of the examples I have seen have some from of aggregate included. I am looking to for a straight value pivot.

Source Table 

╔═══════════╦══════╦═══════╗
║ TEST_NAME ║ SBNO ║  VAL  ║
╠═══════════╬══════╬═══════╣
║ Test1     ║    1 ║ 0.304 ║
║ Test1     ║    2 ║ 0.31  ║
║ Test1     ║    3 ║ 0.306 ║
║ Test2     ║    1 ║ 2.3   ║
║ Test2     ║    2 ║ 2.5   ║
║ Test2     ║    3 ║ 2.4   ║
║ Test3     ║    1 ║ PASS  ║
║ Test3     ║    2 ║ PASS  ║
╚═══════════╩══════╩═══════╝


Desired Output 
╔══════════════════════════╗
║ SBNO Test1 Test2   Test3 ║
╠══════════════════════════╣
║ 1    0.304  2.3    PASS  ║
║ 2    0.31   2.5    PASS  ║
║ 3    0.306  2.4    NULL  ║
╚══════════════════════════╝

Upvotes: 37

Views: 151891

Answers (3)

Taryn
Taryn

Reputation: 247720

The PIVOT function requires an aggregation to get it to work. It appears that your VAL column is a varchar so you will have to use either the MAX or MIN aggregate functions.

If the number of tests is limited, then you can hard-code the values:

select sbno, Test1, Test2, Test3
from
(
  select test_name, sbno, val
  from yourtable
) d
pivot
(
  max(val)
  for test_name in (Test1, Test2, Test3)
) piv;

See SQL Fiddle with Demo.

In your OP, you stated that you will have an larger number of rows to turn into columns. If that is the case, then you can use dynamic SQL:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(TEST_NAME) 
                    from yourtable
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT sbno,' + @cols + '
             from 
             (
                select test_name, sbno, val
                from yourtable
            ) x
            pivot 
            (
                max(val)
                for test_name in (' + @cols + ')
            ) p '

execute(@query)

See SQL Fiddle with Demo.

Both versions will give the same result:

| SBNO | TEST1 | TEST2 |  TEST3 |
---------------------------------
|    1 | 0.304 |   2.3 |   PASS |
|    2 |  0.31 |   2.5 |   PASS |
|    3 | 0.306 |   2.4 | (null) |

Upvotes: 55

Samina
Samina

Reputation: 154

There is no any way to PIVOT without aggregating.

CREATE TABLE #table1
(
    TEST_NAME VARCHAR(10),
    SBNO VARCHAR(10),
    VAL VARCHAR(10)
);

INSERT INTO #table1 (TEST_NAME, SBNO, VAL)
VALUES ('Test1' ,'1', '0.304'),
       ('Test1' ,'2', '0.31'),
       ('Test1' ,'3', '0.306'),
       ('Test2' ,'1', '2.3'),
       ('Test2' ,'2', '2.5'),
       ('Test2' ,'3', '2.4'),
       ('Test3' ,'1', 'PASS'),
       ('Test3' ,'2', 'PASS')

WITH T AS
(
    SELECT SBNO, VAL, TEST_NAME    
      FROM #table1
) 
SELECT *
  FROM T
 PIVOT (MAX(VAL) FOR TEST_NAME IN([Test1], [Test2], [Test3])) P

Upvotes: 5

pnuts
pnuts

Reputation: 59475

A workaround might be to ensure that the obligatory aggregation is only ever applied to a single record. In Excel for example the output could be:

SO15674373 example

where Row Labels includes at the bottom a column of cells with unique index numbers.

Upvotes: 0

Related Questions