get cross tabulated report according to data available - pivot - sql

I want to pivot and turn an existing table and generate a report(cross tabulated). You see vals column is determined by unique combination of date_a and date_e columns. I don't know how to do this.

Something like this:
Test data
CREATE TABLE #tbl(date_a DATE,date_e DATE, vals FLOAT)
INSERT INTO #tbl
VALUES
('2/29/2012','1/1/2013',28.47),
('2/29/2012','2/1/2013',27.42),
('2/29/2012','3/1/2013',24.36),
('3/1/2012','1/1/2013',28.5),
('3/1/2012','2/1/2013',27.35),
('3/1/2012','3/1/2013',24.39),
('3/6/2012','1/1/2013',27.75),
('3/6/2012','2/1/2013',26.63),
('3/6/2012','3/1/2013',23.66)
Query
SELECT
*
FROM
(
SELECT
tbl.date_a,
tbl.date_e,
vals
FROM
#tbl AS tbl
) AS SourceTable
PIVOT
(
SUM(vals)
FOR date_e IN ([1/1/2013],[2/1/2013],[3/1/2013])
) AS pvt
DROP TABLE #tbl
EDIT
If you do not know how many columns there is then you need to do a dynamic pivot. Like this:
The unique columns
DECLARE #cols VARCHAR(MAX)
;WITH CTE
AS
(
SELECT
ROW_NUMBER() OVER(PARTITION BY date_e ORDER BY date_e) AS RowNbr,
tbl.*
FROM
#tbl AS tbl
)
SELECT #cols=STUFF
(
(
SELECT
',' +QUOTENAME(date_e)
FROM
CTE
WHERE
CTE.RowNbr=1
FOR XML PATH('')
)
,1,1,'')
Dynamic pivot
DECLARE #query NVARCHAR(4000)=
N'SELECT
*
FROM
(
SELECT
tbl.date_a,
tbl.date_e,
vals
FROM
#tbl AS tbl
) AS SourceTable
PIVOT
(
SUM(vals)
FOR date_e IN ('+#cols+')
) AS pvt'
EXECUTE(#query)

Related

SQL Server - Dynamic Pivot with 2 Group Variables and 2 Aggregate Calculations

I have a dataset that is shaped like this:
I am trying to convert the data to this format:
As you can see, I'd like to sum the accounts and revenue (for each month) by State and Account Type. It is important to note that I seek a dynamic solution as these ARE NOT the only values (hard-coding is not an option!).
What SQL query can I write to accomplish this task, dynamically? (as these values are not the only ones present in the complete dataset).
Thanks!
I'm assuming you want to keep the columns in order by date, thus the top 100 percent ... order by in the section where we generate the columns
Example
Declare #SQL varchar(max) = '
Select *
From (
Select [State]
,[AccountType]
,B.*
From YourTable A
Cross Apply ( values (concat(''Accounts_'',format([Date],''MM/dd/yyyy'')),Accounts)
,(concat(''Revenue_'' ,format([Date],''MM/dd/yyyy'')),Revenue)
) B (Item,Value)
) A
Pivot (sum([Value]) For [Item] in (' + Stuff((Select ','+QuoteName('Accounts_'+format([Date],'MM/dd/yyyy'))
+','+QuoteName('Revenue_' +format([Date],'MM/dd/yyyy'))
From (Select top 100 percent [Date] from YourTable Group By [Date] Order by [Date] ) A
For XML Path('')),1,1,'') + ') ) p'
--Print #SQL
Exec(#SQL)
Returns
If it helps, the generated SQL looks like this:
Select *
From (
Select [State]
,[AccountType]
,B.*
From YourTable A
Cross Apply ( values (concat('Accounts_',format([Date],'MM/dd/yyyy')),Accounts)
,(concat('Revenue_' ,format([Date],'MM/dd/yyyy')),Revenue)
) B (Item,Value)
) A
Pivot (sum([Value]) For [Item] in ([Accounts_12/31/2017],[Revenue_12/31/2017],[Accounts_01/31/2018],[Revenue_01/31/2018]) ) p

SQL Server multiple rows and two columns into single row with multiple columns

I have a table with a columns for case ID, Action, and reason.
a single case ID can have multiple rows with different actions and codes. I can pivot and get multiple rows with columns action1, action2, action3, etc., but for the life of me, can't get case id, action1, reason1, action2, reason2, etc on a single row.
If you need to go a little more dynamic (n reasons)
Drop Table #Temp
Declare #YourTable table (ID int,Action varchar(50),Reason varchar(50))
Insert Into #YourTable values
(1,'Load Data','Boss said to'),
(1,'Run Query','It is what I do'),
(2,'Take Garbage Out','Wife makes me')
-- Convert Data to EAV Structure'ish
Declare #XML xml = (Select *,GrpSeq = Row_Number() over (Partition By ID Order By (Select NULL)) from #YourTable for XML RAW)
Select ID = r.value('#ID','int')
,ColSeq = Row_Number() over (Partition By r.value('#ID','int') Order By (Select NULL))
,Element = attr.value('local-name(.)','varchar(100)')+r.value('#GrpSeq','varchar(10)')
,Value = attr.value('.','varchar(max)')
Into #Temp
From #XML.nodes('/row') as A(r)
Cross Apply A.r.nodes('./#*') AS B(attr)
Where attr.value('local-name(.)','varchar(100)') not in ('ID','GrpSeq')
-- Get Cols in correct Order
Declare #Cols varchar(max)
Set #Cols = Stuff((Select ',' + QuoteName(Element)
From (Select Distinct Top 100 Percent ColSeq,Element From #Temp Order By ColSeq ) A
For XML Path(''), Type
).value('.', 'varchar(max)'),1,1,'')
-- Execute Dynamic Pivot
Declare #SQL varchar(max) = '
Select *
From (Select ID,Element,Value From #Temp) T
Pivot (
max(Value)
For [Element] in (' + #Cols + ')
) P '
Exec(#SQL)
Returns

Convert dynamic text data rows into multiple columns

I'm trying to convert my result table which consist of multiple rows into multiple columns. below are my sample result before and after:
Before:
After:
Got a some idea from here but still no luck.
Update 1:
select #cols = STUFF((select ',' + QUOTENAME(institution) + ',' + QUOTENAME(intstatus)
From #temp
group by refno,frmstatus
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
i have no idea how to insert each column into the pivot query
set #query = 'select refno, frmstatus,' + #cols + '
from (
select refno, frmstatus, institution, intstatus from #temp
) x
pivot
(
???????
)
Please try the below query:
CREATE TABLE #temp(refno nvarchar(20), firmstatus nvarchar(20), institution nvarchar(20), intstatus nvarchar(20), ranking int)
DECLARE #qu NVARCHAR(MAX), #pcol NVARCHAR(MAX)
INSERT INTO #temp
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY refno ORDER BY institution ASC) AS ranking
FROM temp
SELECT #pcol=
STUFF((
SELECT
DISTINCT N', Institution'+ CAST (ranking AS NVARCHAR(25)) +', '+ N'Intstatus'+ CAST (ranking AS NVARCHAR(25))
FROM #temp
FOR XML PATH('')),1,1,'')
SET #qu=N'SELECT refno, firmstatus,'+ #pcol +
N' FROM
(
select refno,firmstatus,ColData,colheader+ CAST(ranking as varchar) as colnames from
(select * from #temp)s
UNPIVOT
(ColData for colheader in ([institution], [intstatus])) up
)S
PIVOT
(MAX(ColData) FOR colnames IN ('+#pcol +N')) AS piv'
EXEC sp_executesql #qu -- execute the dynamic sql
DROP TABLE #temp -- remove the temp table
The temp table in above script was created like below
--create table temp( refno nvarchar(20), firmstatus nvarchar(20), institution nvarchar(20), intstatus nvarchar(20))
--insert into temp values
--('AAA/1','Active','InstA','Ongoing'),
--('AAA/1','Active','InstB','Ongoing'),
--('AAA/1','Active','InstC','Ongoing'),
--('AAA/2','Active','InstA','Ongoing'),
--('AAA/2','Active','InstB','Ongoing')
Result received:
If repeated columns number is small and restricted, you can use simple solution:
WITH A AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY refno, fmstatus ORDER BY institution) n
FROM myTable
),
B AS (
SELECT refno, fmstatus,
CASE WHEN n=1 THEN institution END institution,
CASE WHEN n=1 THEN intstatus END intstatus,
CASE WHEN n=2 THEN institution END institution1,
CASE WHEN n=2 THEN intstatus END intstatus1,
CASE WHEN n=3 THEN institution END institution2,
CASE WHEN n=3 THEN intstatus END intstatus2
FROM A
)
SELECT refno, fmstatus,
MAX(institution) institution,
MAX(intstatus) intstatus,
MAX(institution1) institution1,
MAX(intstatus1) intstatus1,
MAX(institution2) institution2,
MAX(intstatus2) intstatus2
FROM B
GROUP BY refno, fmstatus
Otherwise use PIVOT

Pivot multiple columns in SQL Server 2008

I am trying to convert column to rows. I am beginner to PIVOT. Tried below code but error is coming as 'Error converting data type nvarchar to datetime.
The incorrect value "Tot_GPS_Cnt" is supplied in the PIVOT operator.'
CREATE TABLE #tbl_Res1
(
CallDate DATETIME,
Tot_GPS_Cnt INT,
Tot_GND_Cnt INT,
Per_Ratio NUMERIC(10,2)
)
INSERT INTO #tbl_Res1
SELECT '2015-04-24 00:00:00.000','40','26','65.00' UNION ALL
SELECT '2015-04-25 00:00:00.000','22','14','63.64' UNION ALL
SELECT'2015-04-26 00:00:00.000','27','21','77.78' UNION ALL
SELECT'2015-04-27 00:00:00.000','41','23','56.10'
Source Table
Desired Output
I have tried with below query bu failing. Please help. Thanks in advance
SELECT CallDate=col, Tot_GPS_Cnt, Tot_GND_Cnt, Per_Ratio
FROM
( select CallDate, col, value from #tbl_Res1
cross apply
(
SELECT 'Tot_GPS_Cnt',cast(Tot_GPS_Cnt as varchar(10)) UNION ALL
SELECT 'Tot_GND_Cnt', cast(Tot_GND_Cnt as varchar(10)) UNION ALL
SELECT 'Per_Ratio', cast(Per_Ratio as varchar(10))
) c (col,value)
) d
PIVOT
(
max(value) for CallDate in ([Tot_GPS_Cnt], [Tot_GND_Cnt], [Per_Ratio])
) as piv
The correct syntax for PIVOT is:
PIVOT
(
<aggregation function>(<column being aggregated>)
FOR
[<column that contains the values that will become column headers>]
IN ( [first pivoted column], [second pivoted column],
... [last pivoted column])
) AS <alias for the pivot table>
Hence, you have to place date values not [Tot_GPS_Cnt], [Tot_GND_Cnt], [Per_Ratio] in place of pivoted columns:
SELECT CallDate=col, [2015-04-24], [2015-04-25], [2015-04-26], [2015-04-27]
FROM
( select CallDate, col, value from #tbl_Res1
cross apply
(
SELECT 'Tot_GPS_Cnt',cast(Tot_GPS_Cnt as varchar(10)) UNION ALL
SELECT 'Tot_GND_Cnt', cast(Tot_GND_Cnt as varchar(10)) UNION ALL
SELECT 'Per_Ratio', cast(Per_Ratio as varchar(10))
) c (col,value)
) d
PIVOT
(
max(value) for CallDate in ([2015-04-24], [2015-04-25], [2015-04-26], [2015-04-27])
) as piv
Thank you #Giorgos Betsos. I just made dynamic solution. Just wanted to share with all.
DECLARE #coldata VARCHAR(500);
DECLARE #sql VARCHAR(MAX);
SELECT #coldata = COALESCE(#coldata + '], [', '') + CONVERT(VARCHAR(10),calldate,110)
FROM #tbl_Res1;
SELECT #coldata = '[' + #coldata +']';
SELECT #coldata;
SET #sql = 'SELECT CallDate=col, ' + #coldata + '
FROM
( SELECT CallDate, col, value FROM #tbl_Res1
cross apply
(
SELECT ''Tot_GPS_Cnt'', CAST(Tot_GPS_Cnt as VARCHAR(10)) UNION ALL
SELECT ''Tot_GND_Cnt'', CAST(Tot_GND_Cnt as VARCHAR(10)) UNION ALL
SELECT ''Per_Ratio'', CAST(Per_Ratio as VARCHAR(10))
) c (col,value)
) d
PIVOT
(
MAX(value) FOR CallDate IN (' + #coldata + ')
) as piv'
SELECT #sql;
EXECUTE (#SQL);

Constructing a PIVOT

I think that PIVOT will help me accomplish this, but I can't get anything started. I am having serious SQL brain farts today, I need some help.
Here is the output I have now:
Id Name Question Answer
0 Test Vault A
0 Test Container 1
1 Foo Vault B
1 Foo Container 2
And this is my desired output:
Id Name Vault Container
0 Test A 1
1 Foo B 2
Can this be done?
If that is impossible or terribly complex to do, I have an alternate way to approach this. The output for my alternate query is:
Id Name VaultId ContainerId
0 Test A NULL
0 Test NULL 1
1 Foo B NULL
1 Foo NULL 2
And here I need to be able to suppress it into one row per Id/Name. I can't remember how to do either of these!
DECLARE #Test TABLE
(
Id INT
,[Name]VARCHAR(10) NOT NULL
,Question VARCHAR(10) NOT NULL,
Answer VARCHAR(10)
);
INSERT #Test VALUES (0,'test1', 'vault','a');
INSERT #Test VALUES (0,'test1', 'Container ','1');
INSERT #Test VALUES (1,'test4', 'vault','b');
INSERT #Test VALUES (1,'test4', 'Container','2');
;WITH CTE
AS
(
SELECT t.id, t.[Name], t.[Question ] ,t.Answer
FROM #Test t
)
SELECT *
FROM CTE
PIVOT ( max(answer) FOR Question IN (vault,container) ) f;
You could do this with a Static Pivot:
create table temp
(
id int,
name varchar(10),
question varchar(10),
answer varchar(10)
)
INSERT into temp VALUES (0,'test', 'vault','a');
INSERT into temp VALUES (0,'test', 'Container','1');
INSERT into temp VALUES (1,'foo', 'vault','b');
INSERT into temp VALUES (1,'foo', 'Container','2');
select *
from
(
select id, name, question, answer
from temp
) x
pivot
(
max(answer)
for question in ([container], [vault])
) p
drop table temp
or a dynamic pivot
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.question)
FROM temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT id, name, ' + #cols + ' from
(
select id, name, question, answer
from temp
) x
pivot
(
max(answer)
for question in (' + #cols + ')
) p '
execute(#query)
both will give you the same results:
Yeah PIVOT is what you need here :). Assuming your table is called MyPivot Try:
SELECT Id, Name, [Vault], [Container]
FROM (SELECT Id, Name, Question, Answer FROM MyPivot) AS SourceTable
PIVOT (MAX(Answer) FOR Question in (Vault, Container)) as p;
EDIT: To demonstrate what that syntax means, see the following breakdown:
PIVOT (<aggregate function>(<column being aggregated>)
FOR <column that contains the values that will become column headers>
IN ( [first pivoted column], [second pivoted column])