Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transpose rows and columns with no aggregate

I have the following dataset

Account Contact

1   324324324
1   674323234
2   833343432
2   433243443
3   787655455
4   754327545
4   455435435
5   543544355
5   432455553
5   432433242
5   432432432

I'd like output as follows:

Account Contact1    Contact2    Contact3    Contact4

1   324324324   674323234       
2   833343432   433243443       
3   787655455           
4   754327545   455435435       
5   543544355   432455553   432433242   432432432

The problem is also that I have an unfixed amount of Accounts & unfixed amount of Contacts

like image 500
user1627440 Avatar asked Jan 08 '14 15:01

user1627440


People also ask

Can you PIVOT without aggregation?

The answer is no: PIVOT requires aggregation.

How do you PIVOT data in SQL without aggregate function?

The 1st table (Table) holds tables name. The 2nd table (Fields) is related to (Table) to hold the fields name for each table. The 3rd table (Field Value) is related to (Fields) to hold fields value for each field.

Can you use GROUP BY without an aggregate?

GROUP BY without Aggregate Functions Although most of the times GROUP BY is used along with aggregate functions, it can still still used without aggregate functions — to find unique records.

Can we use PIVOT without aggregate function in Snowflake?

There is no any way to PIVOT without aggregating. Save this answer. Show activity on this post.


2 Answers

If you are going to apply the PIVOT function, you will need to use an aggregate function to get the result but you will also want to use a windowing function like row_number() to generate a unique sequence for each contact in the account.

First, you will query your data similar to:

select account, contact,
  'contact'
    + cast(row_number() over(partition by account
                              order by contact) as varchar(10)) seq
from yourtable

See SQL Fiddle with Demo. This will create a new column with the unique sequence:

| ACCOUNT |   CONTACT |      SEQ |
|---------|-----------|----------|
|       1 | 324324324 | contact1 |
|       1 | 674323234 | contact2 |

If you have a limited number of columns, then you could hard-code your query:

select account,
  contact1, contact2, contact3, contact4
from 
(
  select account, contact,
    'contact'
      + cast(row_number() over(partition by account
                                order by contact) as varchar(10)) seq
  from yourtable
) d
pivot
(
  max(contact)
  for seq in (contact1, contact2, contact3, contact4)
) piv;

See SQL Fiddle with Demo

If you have an unknown number of columns, then you will have to use dynamic SQL:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(seq) 
                    from
                    (
                      select 'contact'
                              + cast(row_number() over(partition by account
                                                        order by contact) as varchar(10)) seq
                      from yourtable
                    ) d
                    group by seq
                    order by seq
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT account, ' + @cols + ' 
            from 
            (
                select account, contact,
                  ''contact''
                    + cast(row_number() over(partition by account
                                              order by contact) as varchar(10)) seq
                from yourtable
            ) x
            pivot 
            (
                max(contact)
                for seq in (' + @cols + ')
            ) p '

execute sp_executesql @query;

See SQL Fiddle with Demo. Both will give you a result of:

| ACCOUNT |  CONTACT1 |  CONTACT2 |  CONTACT3 |  CONTACT4 |
|---------|-----------|-----------|-----------|-----------|
|       1 | 324324324 | 674323234 |    (null) |    (null) |
|       2 | 433243443 | 833343432 |    (null) |    (null) |
|       3 | 787655455 |    (null) |    (null) |    (null) |
|       4 | 455435435 | 754327545 |    (null) |    (null) |
|       5 | 432432432 | 432433242 | 432455553 | 543544355 |
like image 170
Taryn Avatar answered Nov 15 '22 05:11

Taryn


Just a slightly different way to generate the dynamic PIVOT:

DECLARE @c INT;

SELECT TOP 1 @c = COUNT(*) 
  FROM dbo.YourTable
  GROUP BY Account 
  ORDER BY COUNT(*) DESC;

DECLARE @dc1 NVARCHAR(MAX) = N'', @dc2 NVARCHAR(MAX) = N'', @sql NVARCHAR(MAX);

SELECT @dc1 += ',Contact' + RTRIM(i), @dc2 += ',[Contact' + RTRIM(i) + ']'
  FROM (SELECT TOP (@c) i = number + 1 
  FROM master.dbo.spt_values WHERE type = N'P' ORDER BY number) AS x;

SET @sql = N'SELECT Account ' + @dc1 + 
  ' FROM (SELECT Account, Contact, rn = ''Contact'' 
      + RTRIM(ROW_NUMBER() OVER (PARTITION BY Account ORDER BY Contact))
    FROM dbo.YourTable) AS src PIVOT (MAX(Contact) FOR rn IN (' 
      + STUFF(@dc2, 1, 1, '') + ')) AS p;';

EXEC sp_executesql @sql;

SQLiddle demo

like image 23
Aaron Bertrand Avatar answered Nov 15 '22 05:11

Aaron Bertrand