Related
input table
country tag short
UK F1 Units
UK F2 Volume
UK F3 Value
FR T3 Units
FR T2 Volume
FR T1 Value
result output i want :
country Units Volume Value
uk f1 f2 f3
fr t1 t2 t3
If there are a fixed number of different short values, simply use case expressions to do conditional aggregation:
select country,
max(case when short = 'Units' then tag end) as Units,
max(case when short = 'Volume' then tag end) as Volume,
max(case when short = 'Value' then tag end) as val
from tablename
group by country
For solution you have to use dynamic pivoting.
create table #temp
(
country varchar(30),tag varchar(20),short varchar(300)
)
insert into #temp values ('UK', 'F1', 'Units')
insert into #temp values ('UK', 'F2' , 'Volume')
insert into #temp values ('UK' ,'F3', 'Value')
insert into #temp values ('FR', 'T3' , 'Units')
insert into #temp values ('FR' , 'T2', 'Volume')
insert into #temp values ('FR', 'T1' , 'Value')
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.short)
FROM #temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT country, ' + #cols + ' from
(
select country
, tag
, short
from #temp
) x
pivot
(
max(tag)
for short in (' + #cols + ')
) p '
execute(#query)
drop table #temp
Table Structure
CREATE TABLE tablename
(
[country] [NVARCHAR](10) NULL,
[tag] [NVARCHAR](10) NULL,
[short] [NVARCHAR](10) NULL
)
INSERT INTO tablename
VALUES
('UK','F1','Units'),
('UK','F2','Volume'),
('UK','F3','Value'),
('FR','T3','Units'),
('FR','T2','Volume'),
('FR','T1','Value');
Using Pivot function
SELECT *
FROM tablename
PIVOT ( Max(tag)
FOR short IN ([Units], [volume], [Value]) ) piv;
Online Demo: Link
Using Dynamic SQL PIVOT
DECLARE #cols AS NVARCHAR(max),
#query AS NVARCHAR(max)
SELECT #cols = Stuff((SELECT distinct ',' + Quotename(short)
FROM tablename
FOR xml path(''), type).value('.', 'NVARCHAR(MAX)'), 1, 1,'');
SET #query = 'SELECT *
FROM tablename
PIVOT ( Max(tag)
FOR short IN (' + #cols + ') ) piv;';
EXECUTE(#query);
Online Demo: Link
Result
+---------+-------+--------+-------+
| country | Units | volume | Value |
+---------+-------+--------+-------+
| FR | T3 | T2 | T1 |
| UK | F1 | F2 | F3 |
+---------+-------+--------+-------+
I am having trouble sorting a pivot based on a quite large set of data. I have looked at many examples, but none of them seems to address the issue of volume - or perhaps I am just missing something. I have had a very good look here: Sort Columns For Dynamic Pivot and PIVOT in sql 2005 and found much good advise, but I still cannot find the correct way to sort my pivot.
I am using the following sql. It pivots the columns, but the result needs to be sorted for readability:
SELECT a.* INTO #tempA
FROM (SELECT top (5000) id, email, CONVERT(varchar,ROW_NUMBER() OVER
(PARTITION BY email ORDER BY id)) AS PIVOT_CODE FROM Email) a
order by PIVOT_CODE
DECLARE #cols AS NVARCHAR(MAX),
#sql AS NVARCHAR(MAX)
SELECT #cols =STUFF((SELECT DISTINCT ', ' + QUOTENAME(col)
FROM #tempA WITH (NOLOCK)
cross apply
(
SELECT 'id_' + PIVOT_CODE, id
) c (col, so)
group by col, so
--order by col
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #sql = 'SELECT email, '
+#cols+
'INTO ##AnotherPivotTest FROM
(
SELECT email,
col,
value
FROM #tempA WITH (NOLOCK)
cross apply
(
values
(''id_'' + PIVOT_CODE, id)
) c (col, value)
) d
pivot
(
max(value)
for col in ('
+ #cols+
')
) piv'
EXEC (#sql)
SELECT * FROM ##AnotherPivotTest
The result is a chaos to look at:
==============================================================================================
| email | id_19 | id_24 | id_2 | id_16 | id_5 | id_9 | id_23 | .... | id_1 | .... | id_10 |
==============================================================================================
| xx#yy.dk | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | 1234 | NULL | NULL |
==============================================================================================
I would very much like the Ids to be sorted - beginning with id_1.
As you can see, I have attempted to place an 'order by' in the selection for 'cols', but that gives me the error: "ORDER BY items must appear in the select list if SELECT DISTINCT is specified." And without DISTINCT, I get another error: "The number of elements in the select list exceeds the maximum allowed number of 4096 elements."
I'm stuck, so any help will be greatly appreciated!
Not sure what causes the problem but I've solved my order problem in my pivot table by inserting the data coming from tempA into another temp table and ordering them there
INSERT INTO #tempB
SELECT * FROM #tempA
ORDER BY PIVOT_CODE
Then selecting distinct ones like so:
SELECT #cols = #cols + QUOTENAME(PIVOT_CODE) + ',' FROM (SELECT DISTINCT PIVOT_CODE FROM #tempB ORDER BY PIVOT_CODE)
SELECT #cols = SUBSTRING(#cols, 0, LEN(#cols)) --trims "," at end
You can also just use a cursor to determine your cols and the order them
Cursor with cols ordered
declare #gruppe nvarchar(max)
declare #gruppeSql nvarchar(max)
declare #SQL nvarchar(max)
DECLARE myCustomers CURSOR FOR
select top 10 FirstName from [dbo].[DimCustomer] Order by FirstName
set #gruppeSql = ''
OPEN myCustomers
FETCH NEXT FROM myCustomers INTO #gruppe
IF (##FETCH_STATUS>=0)
BEGIN
SET #gruppeSql = #gruppeSql +'[' +#gruppe+']'
FETCH NEXT FROM myCustomers INTO #gruppe
END
WHILE (##FETCH_STATUS<>-1)
BEGIN
IF (##FETCH_STATUS<>-2)
SET #gruppeSql = #gruppeSql + ',[' +#gruppe+']'
FETCH NEXT FROM myCustomers INTO #gruppe
END
CLOSE myCustomers
DEALLOCATE myCustomers
SET #gruppeSql = replace(#gruppesql,'''','')
/*Select to preview your cols*/
select #gruppeSql
Dynamic pivot
SET #SQL = '
Select *
from
(
SELECT SalesAmount, FirstName
FROM [AdventureWorksDW2014].[dbo].[FactInternetSales] a inner join dbo.DimCustomer b on a.CustomerKey = b.CustomerKey
) x
pivot
(
sum(SalesAmount)
for FirstName in ('+#gruppesql+')
) p'
print #sql
exec(#sql)
I have a table test which has a column Label. It has Data which is longers than 50 characters in length.
When I create a pivot table from the 'test' table , it uses those long charactered data as column name.
My requirement is to user cast function to limit the column names to say 26 characters.
I use the below script , but it doesnt work as desired.
create table V_Test as
select * from
(select * from Test) x
pivot (sum(Average) for Label in (
S03_CreatePlansdadsada,
S03_CreatePlan_T01_NavigateTosdsadsaded,
S03_CreatePlan_T03_abcdefgmanagementsdasda,
S03_CreatePlan_T16_SetStatusToOngoingasdasda,
S03_CreatePlan_T17_Ldsdssdadsadas
)
) p
Thanks in advance for the help
Since the label names are hard-coded anyway, then using aliases for them should be fine.
On MS SQL Server it would look something like this.
Just using a variable table and a temporary table for the purpose of demonstration.
DECLARE #Test TABLE (PlanID int, Label varchar(50), Average int);
insert into #Test values
(1,'S03_CreatePlansdadsada',10),
(1,'S03_CreatePlan_T01_NavigateTosdsadsaded',20),
(1,'S03_CreatePlan_T03_abcdefgmanagementsdasda',30),
(1,'S03_CreatePlan_T16_SetStatusToOngoingasdasda',40),
(1,'S03_CreatePlan_T17_Ldsdssdadsadas',50),
(1,'S03_CreatePlansdadsada',60),
(1,'S03_CreatePlan_T01_NavigateTosdsadsaded',70),
(1,'S03_CreatePlan_T03_abcdefgmanagementsdasda',80),
(1,'S03_CreatePlan_T16_SetStatusToOngoingasdasda',90),
(1,'S03_CreatePlan_T17_Ldsdssdadsadas',100);
IF OBJECT_ID('tempdb..#tmpTest') IS NOT NULL DROP TABLE #tmpTest;
select
PlanID,
S03_CreatePlansdadsada as CreatePlans,
S03_CreatePlan_T01_NavigateTosdsadsaded as T01_NavigateTo,
S03_CreatePlan_T03_abcdefgmanagementsdasda as T03_managements,
S03_CreatePlan_T16_SetStatusToOngoingasdasda as T16_SetStatusToOng,
S03_CreatePlan_T17_Ldsdssdadsadas as T17_Lsd
into #tmpTest
from #Test
pivot (sum(Average) for Label in (
S03_CreatePlansdadsada,
S03_CreatePlan_T01_NavigateTosdsadsaded,
S03_CreatePlan_T03_abcdefgmanagementsdasda,
S03_CreatePlan_T16_SetStatusToOngoingasdasda,
S03_CreatePlan_T17_Ldsdssdadsadas
)
) p;
select * from #tmpTest;
This would return the following results:
PlanID CreatePlans T01_NavigateTo T03_managements T16_SetStatusToOng T17_Lsd
1 70 90 110 130 150
The dynamic way is a bit more complicated, since the SQL needs to be constructed so it can be executed. (beware of code injection)
IF OBJECT_ID('tempdb..#tmpTestData') IS NOT NULL DROP TABLE #tmpTestData;
CREATE TABLE #tmpTestData (PlanID int, Label varchar(50), Average int);
insert into #tmpTestData values
(1,'S03_CreatePlansdadsada',10),
(1,'S03_CreatePlan_T01_NavigateTosdsadsaded',20),
(1,'S03_CreatePlan_T03_abcdefgmanagementsdasda',30),
(1,'S03_CreatePlan_T16_SetStatusToOngoingasdasda',40),
(1,'S03_CreatePlan_T17_Ldsdssdadsadas',50),
(1,'S03_CreatePlansdadsada',60),
(1,'S03_CreatePlan_T01_NavigateTosdsadsaded',70),
(1,'S03_CreatePlan_T03_abcdefgmanagementsdasda',80),
(1,'S03_CreatePlan_T16_SetStatusToOngoingasdasda',90),
(1,'S03_CreatePlan_T17_Ldsdssdadsadas',100);
declare #Columns nvarchar(max);
declare #AliasedColumns nvarchar(max);
set #Columns = STUFF((SELECT ', ' + QUOTENAME(Label) FROM #tmpTestData GROUP BY Label FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(max)') ,1,1,'');
set #AliasedColumns = STUFF((SELECT ', ' + QUOTENAME(Label) +' as '+ QUOTENAME(substring(Label,5,14)) FROM #tmpTestData GROUP BY Label FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(max)') ,1,1,'');
declare #SQL nvarchar(max);
IF OBJECT_ID('tempdb..##tmpPivotTest') IS NOT NULL DROP TABLE ##tmpPivotTest;
set #SQL = 'select PlanID, '+ #AliasedColumns + '
into ##tmpPivotTest
from #tmpTestData pivot (sum(Average) for Label in ('+ #Columns +')) p';
--select #SQL;
exec (#SQL);
select * from ##tmpPivotTest;
IF OBJECT_ID('tempdb..##tmpPivotTest') IS NOT NULL DROP TABLE ##tmpPivotTest;
For an Oracle database it would look something like this:
CREATE TABLE V_Test AS
SELECT * FROM (select Label, Average from Test)
PIVOT (
SUM(Average) AS sum_average FOR (Label) IN (
'S03_CreatePlansdadsada' as CreatePlans,
'S03_CreatePlan_T01_NavigateTosdsadsaded' as T01_NavigateTo,
'S03_CreatePlan_T03_abcdefgmanagementsdasda' as T03_Managements,
'S03_CreatePlan_T16_SetStatusToOngoingasdasda' as T16_SetStatusToOng,
'S03_CreatePlan_T17_Ldsdssdadsadas' as T17_Lsd
)
) p;
I have a data column with values like this:
Table1
ID|GROUPNAME |MEMBER
1|GRP1_ML_Unit1_Role1|GRP=User1,DC=com;GRP=User2,DC=com
2|GRP2_ML_Unit2_Role2|GRP=User3,DC=com;GRP=User4,DC=com;GRP=User5,DC=com
3|GRP3_ML_Unit3_Role3|GRP=User6,DC=com;GRP=User7,DC=com;GRP=User8,DC=com;GRP=User8,DC=com
Expected output
ID|GRP1 |GRP2|GRP3 |GRP4 |MEM1 |MEM2 |MEM3 |MEM4|MEM5|
1 |GRP1 |ML |Unit1|Role1|GRP=User1,DC=com|GRP=User2,DC=com| | |
2 |GRP2 |ML |Unit2|Role2|GRP=User3,DC=com|GRP=User4,DC=com|GRP=User5,DC=com| |
3 |GRP3 |ML |Unit3|Role3|GRP=User6,DC=com|GRP=User7,DC=com|GRP=User8,DC=com|GRP=User8,DC=com |
Thanks,
Ryl
The completed solution is below with the sample data you gave me.
First, create a temp table and fill it with data.
-- Drop the table
drop table #member;
go
-- Sample table
create table #member
(
member_id int not null,
group_name varchar(256),
member_data varchar(8000)
);
go
-- Sample data
insert into #member values
(1, 'GRP1_ML_Unit1_Role1', 'GRP=User1,DC=com;GRP=User2,DC=com'),
(2, 'GRP2_ML_Unit2_Role2', 'GRP=User3,DC=com;GRP=User4,DC=com;GRP=User5,DC=com'),
(3, 'GRP3_ML_Unit3_Role3', 'GRP=User6,DC=com;GRP=User7,DC=com;GRP=User8,DC=com;GRP=User8,DC=com');
go
-- Show the data
select * from #member;
go
Second, copy down one of the many string splitters out there. I ended up installing Jeff Moden's string spliter for 8K max strings.
The query is almost there. However, each column we want is a row. We need to dynamically pivot the table.
--
-- Almost there!
--
-- Data in columns, instead of rows
select m.member_id, m.group_name, s.Item as cols_data, 'MEM' + cast(s.ItemNumber as varchar(6)) as cols_name from #member as m
CROSS APPLY dbo.DelimitedSplit8k(m.member_data,';') s
go
Last but not least, figure out the number of columns. Write dynamic TSQL to pivot our dat and get our result.
--
-- Write dynamic sql to solve
--
DECLARE
#cols AS nvarchar(MAX),
#query AS nvarchar(MAX);
-- Get a dynamic number of columns
SET #cols = STUFF(
(
SELECT distinct ',' + QUOTENAME(c.cols_name)
FROM
(
select m.member_id, m.group_name, s.Item as cols_data, 'MEM' + cast(s.ItemNumber as varchar(6)) as cols_name from #member as m
CROSS APPLY dbo.DelimitedSplit8k(m.member_data,';') s
) as c
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
,1,1,'');
print #cols;
-- Make dynamic pivot query
set #query = 'SELECT member_id as ID1, group_name as GROUP1, ' + #cols + ' from
(
select m.member_id, m.group_name, s.Item as cols_data, ''MEM'' + cast(s.ItemNumber as varchar(6)) as cols_name from #member as m
CROSS APPLY dbo.DelimitedSplit8k(m.member_data, '';'') s
) x
pivot
(
max(cols_data)
for cols_name in (' + #cols + ')
) p ';
execute(#query)
A screen shot of the results in the desired format.
How to transform this:
ID Name Description
1 Test1a TestDesc1a
1 Test1b TestDesc1b
2 Test2a TestDesc2a
2 Test2b TestDesc2b
into this:
ID Column 1 2
1 Name test1a test1b
1 Description testDesc1a testDesc1b
You ask for complex pivoting table. Read about this here: http://msdn.microsoft.com/en-us/library/ms177410.aspx
You need to use a PIVOT query.
A basic discussion of PIVOT queries can be found at [MSDN][1].
This is a tricky question to solve, however the output specified is not proper/ conflicting. Below is similar solution which you can try
Sample Table creation:
CREATE TABLE [dbo].[TestTable](
[Id] [int] NULL,
[Name] [nvarchar](50) NULL,
[Description] [nvarchar](50) NULL)
Inserting sample values:
INSERT INTO TestTable VALUES (1,'Test1a','TestDesc1a')
INSERT INTO TestTable VALUES (2,'Test1b','TestDesc1b')
INSERT INTO TestTable VALUES (3,'Test2a','TestDesc2a')
INSERT INTO TestTable VALUES (4,'Test2b','TestDesc2b')
Query to get the desired output using Pivot:
SELECT 'Name' AS [Column], [1], [2],[3],[4]
FROM
(SELECT Name, id from TestTable) AS ST
PIVOT
(Max(Name) FOR ID IN ([1], [2],[3],[4])) AS PT
UNION
SELECT 'Description' AS [Column], [1], [2],[3],[4]
FROM
(SELECT id,[Description] from TestTable) AS ST
PIVOT
(Max([Description]) FOR ID IN ([1], [2],[3],[4])) AS PT
ORDER BY [Column] DESC
OutPut:
Column 1 2 3 4
Name Test1a Test1b Test2a Test2b
Description TestDesc1a TestDesc1b TestDesc2a TestDesc2b
Hope this helps to solve your question.
You can use a static PIVOT if you only are going to have a few columns but I am guessing that you will have more than 2 IDs so you will probably want to use a Dynamic PIVOT for this query. Using a dynamic pivot will allow you to have more than the two ids you provided, it will grab all of the Ids from the table and then PIVOT:
create table temp
(
id int,
name varchar(10),
description varchar(20)
)
insert into temp values (1, 'Test1a', 'TestDesc1a')
insert into temp values (1, 'Test1b', 'TestDesc1b')
insert into temp values (2, 'Test2a', 'TestDesc2a')
insert into temp values (2, 'Test2b', 'TestDesc2b')
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.id)
FROM temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ''Name'' as [Column], ' + #cols + ' from
(
select id
, name
from temp
) x
pivot
(
max(name)
for id in (' + #cols + ')
) p
UNION
SELECT ''Description'' as [Column], ' + #cols + ' from
(
select id
, description
from temp
) x
pivot
(
max(description)
for id in (' + #cols + ')
) p '
execute(#query)
drop table temp
Results:
Column 1 2
Description TestDesc1b TestDesc2b
Name Test1b Test2b