Related
I am working on a data where it looks like this
I want to pivot it in this way :
I have written in this way :
select *
from (select * FROM records) as test
PIVOT (
max(value) for
[Question_ID] in ((SELECT distinct [Question_ID] from records order by 1))) AS PivotTable
Can you please help me?
If you know the number of columns in PIVOT, you can directly hardcode like below.
SELECT * FROM
(
SELECT * FROM test
) t
PIVOT (
MAX(value) for
[Question_ID] in ([120],[122],[149],[150],[151],[189],[253],[554],[774] )) AS Pivot_Table
If not,you have to use Dynamic Pivot table for unknown number of column values.
DECLARE
#columns NVARCHAR(MAX) = '',
#sql NVARCHAR(MAX) = '';
-- select the QuestionIDs
SELECT
#columns+=QUOTENAME(Question_ID) + ','
FROM
test
GROUP BY Question_ID
ORDER BY Question_ID;
-- remove the last comma
SET #columns = LEFT(#columns, LEN(#columns) - 1);
-- construct dynamic SQL
SET #sql ='
SELECT * FROM
(
SELECT * FROM TEST
) t
PIVOT(
MAX(value) for
[Question_ID] IN ('+ #columns +')
) AS pivot_table;';
-- execute the dynamic SQL
EXECUTE sp_executesql #sql;
CHECK DEMO HERE
you got your wish result
select *
from (select * FROM #temp) as test--your table name replace with #temp
PIVOT (
max(value) for
[Question_ID] in ([120],[122],[149],[150],[151],[189],[253],[554],[774] )) AS PivotTable
or if you want to dynamic your query
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(Question_ID)
FROM #temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
--print #cols
set #query = 'SELECT ID, ' + #cols + ' from
(
select ID
, Question_ID
, [Value]
from #temp
) x
pivot
(
max([Value])
for [Question_ID] in (' + #cols + ')
) p '
execute(#query)
drop table #temp
----------
If you are sure that the number of columns will not change, then you can use case statement
CREATE TABLE #TBL (ID INT, Question_ID INT, [Value] VARCHAR(20))
INSERT INTO #TBL VALUES
(1,149,'abc'),(1,150,'def'),(1,151,'aff'),(1,122,'www'),
(2,120,'add'),(2,150,'sub'),(2,189,'asf'),(3,253,'asd'),
(3,150,'gfre'),(3,554,'cvew'),(3,744,'qwert')
-- aNSWER
SELECT
ID,
MAX(CASE WHEN Question_ID = 149 THEN [Value] END) AS '149',
MAX(CASE WHEN Question_ID = 150 THEN [Value] END) AS '150',
MAX(CASE WHEN Question_ID = 151 THEN [Value] END) AS '151',
MAX(CASE WHEN Question_ID = 122 THEN [Value] END) AS '122',
MAX(CASE WHEN Question_ID = 120 THEN [Value] END) AS '120',
MAX(CASE WHEN Question_ID = 150 THEN [Value] END) AS '150',
MAX(CASE WHEN Question_ID = 189 THEN [Value] END) AS '189',
MAX(CASE WHEN Question_ID = 253 THEN [Value] END) AS '253',
MAX(CASE WHEN Question_ID = 150 THEN [Value] END) AS '150',
MAX(CASE WHEN Question_ID = 554 THEN [Value] END) AS '554',
MAX(CASE WHEN Question_ID = 744 THEN [Value] END) AS '744'
FROM #TBL
GROUP BY ID
DROP TABLE #TBL
Output
ID 149 150 151 122 120 150 189 253 150 554 744
1 abc def aff www NULL def NULL NULL def NULL NULL
2 NULL sub NULL NULL add sub asf NULL sub NULL NULL
3 NULL gfre NULL NULL NULL gfre NULL asd gfre cvew qwert
We need to list all numbers as a flat data set, how can we do that?
Table Name: Telephone
ID TYPE NUMBER
==================================
123 MN 042153939
123 HN 2242116
123 MN 1234567890
123 HN 12345678
Create Table Telephone
(
ID Integer,
Type char(3),
Number Varchar(20)
);
insert into Telephone values
(123, 'MN', '042153939'),
(123, 'HN', '2242116'),
(123, 'MN', '1234567890'),
(123, 'HN', '12345678');
I want SQL to return data in this format
ID MN#1 Mn#2 HN#1 HN#2
================================================
123 042153939 1234567890 2242116 12345678
Dynamic approach
Init
DROP TABLE IF EXISTS #Telephone;
CREATE TABLE #Telephone(ID INT,Type CHAR(3),Number VARCHAR(20));
INSERT INTO #Telephone (ID,Type,Number) VALUES
(123, 'MN', '042153939'),
(123, 'HN', '2242116'),
(123, 'MN', '1234567890'),
(123, 'HN', '12345678');
The code
DECLARE #ColumnList NVARCHAR(MAX);
SELECT #ColumnList = STUFF((SELECT ',[' + RTRIM(t.[Type]) + '#'
+ CONVERT(NVARCHAR(255),ROW_NUMBER()OVER(PARTITION BY t.[Type] ORDER BY t.ID)) + ']'
FROM #Telephone t FOR XML PATH(''),TYPE).value('(./text())[1]','NVARCHAR(MAX)'),1,1,'')
;
DECLARE #sql NVARCHAR(MAX) = '';
SET #sql = N'
SELECT ID,' + #ColumnList + N'
FROM (
SELECT t.ID,t.Number, RTRIM(t.[Type]) + ''#'' + CONVERT(NVARCHAR(255),ROW_NUMBER()OVER(PARTITION BY t.[Type] ORDER BY t.ID)) AS [Type]
FROM #Telephone t
) a
PIVOT(MAX(a.Number) FOR a.Type IN (' + #ColumnList + N')) p
'
;
--PRINT #sql
IF #sql IS NOT NULL EXEC(#sql);
try pivoting like below :
SELECT first_column AS <first_column_alias>,
[pivot_value1], [pivot_value2], ... [pivot_value_n]
FROM
(<source_table>) AS <source_table_alias>
PIVOT
(
aggregate_function(<aggregate_column>)
FOR <pivot_column> IN ([pivot_value1], [pivot_value2], ... [pivot_value_n])
) AS <pivot_table_alias>;
We can try using a pivot query with the help of ROW_NUMBER():
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY TYPE DESC, NUMBER) rn
FROM Telephone
)
SELECT
ID,
MAX(CASE WHEN rn = 1 THEN NUMBER END) AS [MN#1],
MAX(CASE WHEN rn = 2 THEN NUMBER END) AS [MN#2],
MAX(CASE WHEN rn = 3 THEN NUMBER END) AS [HN#3],
MAX(CASE WHEN rn = 4 THEN NUMBER END) AS [HN#4]
FROM cte
GROUP BY ID;
You may try this. with row_number() and pivot.
For more info about pivot you may find this link PIVOT.
; with cte as (
select row_number() over (partition by type order by id ) as Slno, * from Telephone
)
, ct as (
select id, type + '#' + cast(slno as varchar(5)) as Type, values from cte
)
select * from (
select * from ct
) as d
pivot
( max(values) for type in ( [MN#1],[Mn#2],[HN#1],[HN#2] )
) as p
I would like some help pivoting. I have a dataset in SQL that looks like this
ID-----------VisitDate----------Metric------Value
1001---------2012-01-01---------Cajun-------40
1001---------2012-01-02---------Cajun-------30
1001---------2012-01-01---------Ham---------20
1003---------2012-01-02---------Ham---------10
1003---------2012-01-03---------Beef--------10
How can I pivot this dataset so that I transform it from long to wide format based on the ID and VisitDate Columns, so the dataset would look something like this:
ID-----------VisitDate----------Cajun------Ham--------Beef
1001---------2012-01-01---------40---------20---------Null
1001---------2012-01-02---------30---------Null-------Null
1003---------2012-01-02---------Null-------10---------Null
1003---------2012-01-03---------Null-------Null-------10
If you're sure that the value of Metric will only consists of Cajun, Ham and Beef, then this will do it:
SELECT
Id,
VisitDate,
Cajun = MAX(CASE WHEN Metric = 'Cajun' THEN Value END),
Ham = MAX(CASE WHEN Metric = 'Ham' THEN Value END),
Beef = MAX(CASE WHEN Metric = 'Beef' THEN Value END)
FROM YourTable
GROUP BY ID, VisitDate
ORDER BY ID, VisitDate
On the other hand, if you don't know the value of Metric, then you can use a Dynamic Cross Tab. For reference: http://www.sqlservercentral.com/articles/Crosstab/65048/
DECLARE #sql1 VARCHAR(4000) = ''
DECLARE #sql2 VARCHAR(4000) = ''
DECLARE #sql3 VARCHAR(4000) = ''
SELECT #sql1 =
'SELECT
ID
,VisitDate'
+ CHAR(10)
SELECT #sql2 = #sql2 +
' ,MAX(CASE WHEN Metric = ''' + Metric + ''' THEN Value END) AS [' + Metric + ']' + CHAR(10)
FROM(
SELECT DISTINCT Metric FROM YourTable
)t
SELECT #sql3 =
'FROM YourTable
GROUP BY ID, VisitDate
ORDER BY ID, VisitDate
'
PRINT(#sql1 + #sql2 + #sql3)
EXEC (#sql1 + #sql2 + #sql3)
RESULT
ID VisitDate Beef Cajun Ham
----------- ---------- ----------- ----------- -----------
1001 2012-01-01 NULL 40 20
1001 2012-01-02 NULL 30 NULL
1003 2012-01-02 NULL NULL 10
1003 2012-01-03 10 NULL NULL
Here's a link to sqlfiddle:
http://sqlfiddle.com/#!3/0445e/1
Here's how it looks like using PIVOT:
SELECT
ID,
VisitDate,
Cajun,
Ham,
Beef
FROM (
SELECT
ID,
VisitDate,
Metric,
Value
FROM
Bleh
) AS SourceTable PIVOT (
MAX (Value) FOR Metric IN (Cajun, Ham, Beef)
) AS PivotTable
Use PIVOT to get the result. Check the result in Fiddler
Ref. to learn SQL SERVER – PIVOT and UNPIVOT Table Examples
SELECT id,
visitdate,
SUM([Cajun]) AS [Cajun],
SUM([Ham]) AS [Ham],
SUM([beef]) AS [beef]
FROM Test AS A
PIVOT(MIN(A.value) FOR A.metric IN ([Cajun],[Ham],[beef])) AS B
GROUP BY id, visitdate
Currently building a SELECT statement in SQL Server 2008 but would like to make this SELECT statement dynamic, so the columns can be defined based on values in a table. I heard about pivot table and cursors, but seems kind of hard to understand at my current level, here is the code;
DECLARE #date DATE = null
IF #date is null
set # date = GETDATE() as DATE
SELECT
Name,
value1,
value2,
value3,
value4
FROM ref_Table a
FULL OUTER JOIN (
SELECT
PK_ID ID,
sum(case when FK_ContainerType_ID = 1 then 1 else null) Box,
sum(case when FK_ContainerType_ID = 2 then 1 else null) Pallet,
sum(case when FK_ContainerType_ID = 3 then 1 else null) Bag,
sum(case when FK_ContainerType_ID = 4 then 1 else null) Drum
from
Packages
WHERE
#date between PackageStart AND PackageEnd
group by PK_ID ) b on a.Name = b.ID
where
Group = 0
The following works great for me , but PK_Type_ID and the name of the column(PackageNameX,..) are hard coded, I need to be dynamic and it can build itself based on present or futures values in the Package table.
Any help or guidance on the right direction would be greatly appreciated...,
As requested
ref_Table (PK_ID, Name)
1, John
2, Mary
3, Albert
4, Jane
Packages (PK_ID, FK_ref_Table_ID, FK_ContainerType_ID, PackageStartDate, PackageEndDate)
1 , 1, 4, 1JAN2014, 30JAN2014
2 , 2, 3, 1JAN2014, 30JAN2014
3 , 3, 2, 1JAN2014, 30JAN2014
4 , 4, 1, 1JAN2014, 30JAN2014
ContainerType (PK_ID, Type)
1, Box
2, Pallet
3, Bag
4, Drum
and the result should look like this;
Name Box Pallet Bag Drum
---------------------------------------
John 1
Mary 1
Albert 1
Jane 1
The following code like I said works great, the issue is the Container table is going to grow and I need to replicated the same report without hard coding the columns.
What you need to build is called a dynamic pivot. There are plenty of good references on Stack if you search out that term.
Here is a solution to your scenario:
IF OBJECT_ID('tempdb..##ref_Table') IS NOT NULL
DROP TABLE ##ref_Table
IF OBJECT_ID('tempdb..##Packages') IS NOT NULL
DROP TABLE ##Packages
IF OBJECT_ID('tempdb..##ContainerType') IS NOT NULL
DROP TABLE ##ContainerType
SET NOCOUNT ON
CREATE TABLE ##ref_Table (PK_ID INT, NAME NVARCHAR(50))
CREATE TABLE ##Packages (PK_ID INT, FK_ref_Table_ID INT, FK_ContainerType_ID INT, PackageStartDate DATE, PackageEndDate DATE)
CREATE TABLE ##ContainerType (PK_ID INT, [Type] NVARCHAR(50))
INSERT INTO ##ref_Table (PK_ID,NAME)
SELECT 1,'John' UNION
SELECT 2,'Mary' UNION
SELECT 3,'Albert' UNION
SELECT 4,'Jane'
INSERT INTO ##Packages (PK_ID, FK_ref_Table_ID, FK_ContainerType_ID, PackageStartDate, PackageEndDate)
SELECT 1,1,4,'2014-01-01','2014-01-30' UNION
SELECT 2,2,3,'2014-01-01','2014-01-30' UNION
SELECT 3,3,2,'2014-01-01','2014-01-30' UNION
SELECT 4,4,1,'2014-01-01','2014-01-30'
INSERT INTO ##ContainerType (PK_ID, [Type])
SELECT 1,'Box' UNION
SELECT 2,'Pallet' UNION
SELECT 3,'Bag' UNION
SELECT 4,'Drum'
DECLARE #DATE DATE, #PARAMDEF NVARCHAR(MAX), #COLS NVARCHAR(MAX), #SQL NVARCHAR(MAX)
SET #DATE = '2014-01-15'
SET #COLS = STUFF((SELECT DISTINCT ',' + QUOTENAME(T.[Type])
FROM ##ContainerType T
FOR XML PATH, TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
SET #SQL = 'SELECT [Name], ' + #COLS + '
FROM (SELECT [Name], [Type], 1 AS Value
FROM ##ref_Table R
JOIN ##Packages P ON R.PK_ID = P.FK_ref_Table_ID
JOIN ##ContainerType T ON P.FK_ContainerType_ID = T.PK_ID
WHERE #DATE BETWEEN P.PackageStartDate AND P.PackageEndDate) X
PIVOT (COUNT(Value) FOR [Type] IN (' + #COLS + ')) P
'
PRINT #COLS
PRINT #SQL
SET #PARAMDEF = '#DATE DATE'
EXEC SP_EXECUTESQL #SQL, #PARAMDEF, #DATE=#DATE
Output:
Name Bag Box Drum Pallet
Albert 0 0 0 1
Jane 0 1 0 0
John 0 0 1 0
Mary 1 0 0 0
Static Query:
SELECT [Name],[Box],[Pallet],[Bag],[Drum] FROM
(
SELECT *
FROM
(
SELECT rf.Name,cnt.[Type], pk.PK_ID AS PKID, rf.PK_ID AS RFID
FROM ref_Table rf INNER JOIN Packages pk ON rf.PK_ID = pk.FK_ref_Table_ID
INNER JOIN ContanerType cnt ON cnt.PK_ID = pk.FK_ContainerType_ID
) AS SourceTable
PIVOT
(
COUNT(PKID )
FOR [Type]
IN ( [Box],[Pallet],[Bag],[Drum])
) AS PivotTable
) AS Main
ORDER BY RFID
Dynamic Query:
DECLARE #columnList nvarchar (MAX)
DECLARE #pivotsql nvarchar (MAX)
SELECT #columnList = STUFF(
(
SELECT ',' + '[' + [Type] + ']'
FROM ContanerType
FOR XML PATH( '')
)
,1, 1,'' )
SET #pivotsql =
N'SELECT [Name],' + #columnList + ' FROM
(
SELECT *
FROM
(
SELECT rf.Name,cnt.[Type], pk.PK_ID AS PKID, rf.PK_ID AS RFID
FROM ref_Table rf INNER JOIN Packages pk ON rf.PK_ID = pk.FK_ref_Table_ID
INNER JOIN ContanerType cnt ON cnt.PK_ID = pk.FK_ContainerType_ID
) AS SourceTable
PIVOT
(
COUNT(PKID )
FOR [Type]
IN ( ' + #columnList + ')
) AS PivotTable
) AS Main
ORDER BY RFID;'
EXEC sp_executesql #pivotsql
Following my tutorial below will help you to understand the PIVOT functionality:
We write sql queries in order to get different result sets like full, partial, calculated, grouped, sorted etc from the database tables. However sometimes we have requirements that we have to rotate our tables. Sounds confusing?
Let's keep it simple and consider the following two screen grabs.
SQL Table:
Expected Results:
Wow, that's look like a lot of work! That is a combination of tricky sql, temporary tables, loops, aggregation......, blah blah blah
Don't worry let's keep it simple, stupid(KISS).
MS SQL Server 2005 and above has a function called PIVOT. It s very simple to use and powerful. With the help of this function we will be able to rotate sql tables and result sets.
Simple steps to make it happen:
Identify all the columns those will be part of the desired result set.
Find the column on which we will apply aggregation(sum,ave,max,min etc)
Identify the column which values will be the column header.
Specify the column values mentioned in step3 with comma separated and surrounded by square brackets.
So, if we now follow above four steps and extract information from the above sales table, it will be as below:
Year, Month, SalesAmount
SalesAmount
Month
[Jan],[Feb] ,[Mar] .... etc
We are nearly there if all the above steps made sense to you so far.
Now we have all the information we need. All we have to do now is to fill the below template with required information.
Template:
Our SQL query should look like below:
SELECT *
FROM
(
SELECT SalesYear, SalesMonth,Amount
FROM Sales
) AS SourceTable
PIVOT
(
SUM(Amount )
FOR SalesMonth
IN ( [Jan],[Feb] ,[Mar],
[Apr],[May],[Jun] ,[Jul],
[Aug],[Sep] ,[Oct],[Nov] ,[Dec])
) AS PivotTable;
In the above query we have hard coded the column names. Well it's not fun when you have to specify a number of columns.
However, there is a work arround as follows:
DECLARE #columnList nvarchar (MAX)
DECLARE #pivotsql nvarchar (MAX)
SELECT #columnList = STUFF(
(
SELECT ',' + '[' + SalesMonth + ']'
FROM Sales
GROUP BY SalesMonth
FOR XML PATH( '')
)
,1, 1,'' )
SET #pivotsql =
N'SELECT *
FROM
(
SELECT SalesYear, SalesMonth,Amount
FROM Sales
) AS SourceTable
PIVOT
(
SUM(Amount )
FOR SalesMonth
IN ( ' + #columnList +' )
) AS PivotTable;'
EXEC sp_executesql #pivotsql
Hopefully this tutorial will be a help to someone somewhere.
Enjoy coding.
I have some data stored in a table in the following format
BatchID EntityChanged ChangeValue
EERS ABC DEF
EERS ABCD XYZ
EERS Something SomeValue
New Batch SomethingMore GHI
The list of values that can appear in the "EntityChanged" columns is finite, known beforehand and has no spaces etc in it. For the sake of argument let us say that this list is - ABC, ABCD, Something, SomethingMore
Then for the above dataset I would like an output of
BatchID ABC ABCD Something SomethingMore
EERS DEF XYZ SomeValue NULL
New Batch NULL NULL NULL GHI
Using Pivot I could only go so far.
Can someone please help me slice this data in the desired way?
You can use a PIVOT for this, either via Static PIVOT or a Dynamic PIVOT which might be good if you have a unknown number of columns.
Static (See SQL Fiddle with Demo):
create table t2
(
batchid varchar(10),
entitychanged varchar(20),
changevalue varchar(10)
)
insert into t2 values ('EERS', 'ABC', 'DEF')
insert into t2 values ('EERS', 'ABCD', 'XYZ')
insert into t2 values ('EERS', 'Something', 'SomeValue')
insert into t2 values ('New Batch', 'SomethingMore', 'GHI')
select *
from
(
select batchid, entitychanged, changevalue
from t2
) x
pivot
(
min(changevalue)
for entitychanged in ([ABC], [ABCD], [Something], [SomethingMore])
) p
Dynamic (see SQL Fiddle with Demo):
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(entitychanged)
from t2
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT batchid, ' + #cols + ' from
(
select batchid, entitychanged, changevalue
FROM t2
) x
pivot
(
min(changevalue)
for entitychanged in (' + #cols + ')
) p '
execute(#query)
Both will give you the same result.
Try this:
SELECT BatchID,
MAX(CASE WHEN EntityChanged = 'ABC' THEN ChangeValue END) 'ABC',
MAX(CASE WHEN EntityChanged = 'ABCD' THEN ChangeValue END) 'ABCD',
MAX(CASE WHEN EntityChanged = 'Something' THEN ChangeValue END) 'Something',
MAX(CASE WHEN EntityChanged = 'SomethingMore' THEN ChangeValue END) 'SomethingMore'
FROM YourTable t
GROUP BY BatchID
DEMO