rows to columns partly - sql

Could anybody guide me how to pivot my data. I have:
RowID Dimension Value
1 Country Italy
1 Year 2011
1 GDP 4
1 Population 6
2 Country Spain
2 Year 2011
2 GDP 7
2 Population 5
I want in a such way:
RowID Country Year GDP Population
1 Italy 2011 4 6
2 Spain 2011 7 5
P.S. I use MS SQL Server 2008 R2 Express Edition. I tried to use PIVOT but it returned many rows with NULL so I could not figure out.

You can use PIVOT for this. This can be hard-coded if you know all of the values:
select *
from
(
select rowid, dimension, value
from yourtable
) src
pivot
(
max(value)
for dimension in ([Country], [Year], [GDP], [Population])
) piv
See SQL Fiddle with Demo
Or of you do not have access to the PIVOT function, then you can use an aggregate with a CASE:
select rowid,
max(case when dimension = 'country' then value end) country,
max(case when dimension = 'Year' then value end) Year,
max(case when dimension = 'GDP' then value end) GDP,
max(case when dimension = 'Population' then value end) Population
from yourtable
group by rowid
See SQL Fiddle with Demo
If you have an unknown number of values, then you can use dynamic sql:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(Dimension)
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT rowid, ' + #cols + ' from
(
select rowid, dimension, value
from yourtable
) x
pivot
(
max(value)
for dimension in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo

Related

Trying to Sum up Cross-Tab Data in SQL

I have a table where every ID has one or more places, and each place comes with a count. Places can be repeated within IDs. It is stored in rows like so:
ID ColumnName DataValue
1 place1 ABC
1 count1 5
2 place1 BEC
2 count1 12
2 place2 CDE
2 count2 6
2 place3 BEC
2 count3 9
3 place1 BBC
3 count1 5
3 place2 BBC
3 count2 4
Ultimately, I want a table where every possible place name is its own column, and the count per place per ID is summed up, like so:
ID ABC BEC CDE BBC
1 5 0 0 0
2 0 21 6 0
3 0 0 0 9
I don't know the best way to go about this. There are around 50 different possible place names, so specifically listing them out in a query isn't ideal. I know I ultimately have to pivot the data, but I don't know if I should do it before or after I sum up the counts. And whether it's before or after, I haven't been able to figure out how to go about summing it up.
Any ideas/help would be greatly appreciated. At this point, I'm having a hard time finding where to even start. I've seen a few posts with similar problems, but nothing quite as convoluted as this.
EDIT:
Right now I'm working with this to pivot the table, but this leaves me with columns named place1, place2, .... count1, count2,...
and I don't know how to appropriately sum up the counts and make new columns with the place names.
DECLARE #cols NVARCHAR(MAX), #query NVARCHAR(MAX);
SET #cols = STUFF(
(
SELECT DISTINCT
','+QUOTENAME(c.[ColumnName])
FROM #temp c FOR XML PATH(''), TYPE
).value('.', 'nvarchar(max)'), 1, 1, '');
SET #query = 'SELECT [ID], '+#cols+'from (SELECT [ID],
[DataValue] AS [amount],
[ColumnName] AS [category]
FROM #temp
)x pivot (max(amount) for category in ('+#cols+')) p';
EXECUTE (#query);
Your table structure is pretty bad. You'll need to normalize your data before you can attempt to pivot it. Try this:
;WITH IDs AS
(
SELECT DISTINCT
id
,ColId = RIGHT(ColumnName, LEN(ColumnName) - 5)
,Place = datavalue
FROM #temp
WHERE ISNUMERIC(datavalue) = 0
)
,Counts AS
(
SELECT DISTINCT
id
,ColId = RIGHT(ColumnName, LEN(ColumnName) - 5)
,Cnt = CAST(datavalue AS INT)
FROM #temp
WHERE ISNUMERIC(datavalue) = 1
)
SELECT
piv.id
,ABC = ISNULL(piv.ABC, 0)
,BEC = ISNULL(piv.BEC, 0)
,CDE = ISNULL(piv.CDE, 0)
,BBC = ISNULL(piv.BBC, 0)
FROM (SELECT i.id, i.Place, c.Cnt FROM IDs i JOIN Counts c ON c.id = i.id AND c.ColId = i.ColId) src
PIVOT ( SUM(Cnt)
FOR Place IN ([ABC], [BEC], [CDE], [BBC])
) piv;
Doing it with dynamic SQL would yield the following:
SET #query =
';WITH IDs AS
(
SELECT DISTINCT
id
,ColId = RIGHT(ColumnName, LEN(ColumnName) - 5)
,Place = datavalue
FROM #temp
WHERE ISNUMERIC(datavalue) = 0
)
,Counts AS
(
SELECT DISTINCT
id
,ColId = RIGHT(ColumnName, LEN(ColumnName) - 5)
,Cnt = CAST(datavalue AS INT)
FROM #temp
WHERE ISNUMERIC(datavalue) = 1
)
SELECT [ID], '+#cols+'
FROM
(
SELECT i.id, i.Place, c.Cnt
FROM IDs i
JOIN Counts c ON c.id = i.id AND c.ColId = i.ColId
) src
PIVOT
(SUM(Cnt) FOR Place IN ('+#cols+')) piv;';
EXECUTE (#query);
Try this out:
SELECT id,
COALESCE(ABC, 0) AS ABC,
COALESCE(BBC, 0) AS BBC,
COALESCE(BEC, 0) AS BEC,
COALESCE(CDE, 0) AS CDE
FROM
(SELECT id,
MIN(CASE WHEN columnname LIKE 'place%' THEN datavalue END) AS col,
CAST(MIN(CASE WHEN columnname LIKE 'count%' THEN datavalue END) AS INT) AS val
FROM t
GROUP BY id, RIGHT(columnname, 1)
) src
PIVOT
(SUM(val)
FOR col in ([ABC], [BBC], [BEC], [CDE])) pvt
Tested here: http://rextester.com/XUTJ68690
In the src query, you need to re-format your data, so that you have a unique id and place in each row. From there a pivot will work.
If the count is always immediately after the place, the following query will generate a data set for pivoting.
The result data set before pivoting has the following columns:
id, placename, count
select placeTable.id, placeTable.datavalue, countTable.datavalue
from
(select *, row_number() over (order by id, %%physloc%%) as rownum
from test
where isnumeric(datavalue) = 1
) as countTable
join
(select *, row_number() over (order by id, %%physloc%%) as rownum
from test
where isnumeric(datavalue) <> 1
) as placeTable
on countTable.id = placeTable.id and
countTable.rownum = placeTable.rownum
Tested on sqlfidde mssqlserver: http://sqlfiddle.com/#!6/701c91/18
Here is one other approach using PIVOT operator with dynamic style
declare #Col varchar(2000) = '',
#Query varchar(2000) = ''
set #Col = stuff(
(select ','+QUOTENAME(DataValue)
from table where isnumeric(DataValue) = 0
group by DataValue for xml path('')),1,1,'')
set #Query = 'select id, '+#Col+' from
(
select id, DataValue,
cast((case when isnumeric(DataValue) = 1 then DataValue else lead(DataValue) over (order by id) end) as int) Value
from table
) as a
PIVOT
(
sum(Value) for DataValue in ('+#Col+')
)pvt'
EXECUTE (#Query)
Note : I have used lead() function to access next rows data if i found character string values and replace with numeric data values
Result :
id ABC BBC BEC CDE
1 5 NULL NULL NULL
2 NULL NULL 21 6
3 NULL 9 NULL NULL

Converting column values (rows) to columns and aggregate count on two rows of two different tables

I have two tables
Program
Student
Student:
Name Status1 Syear SCode
--------------------
kk A 2000 1
ra A 2001 2
Paras L 2000 2
Prit L 2001 2
Poot A 2002 4
Program:
PName PCode
--------------------
Msc 1
DC 2
PO 4
Join on ID
Required output :
SELECT *
FROM
(SELECT
Program.PName AS v, Status1
FROM
Student, Program
WHERE
Student.PCode = SCode
GROUP BY
Program.PName, Student.Syear, Status1) AS src
pivot
(
count(v)
FOR Status1 IN ([A],)
) as piv
It does not display PNAME in output
A L
-----------
1 0
1 2
1 0
Desired output
PNAME A L
-----------
Msc 1 0
DC 1 2
PO 1 0
1. STATIC PIVOT
You can do this if the column names is known in advance
SELECT PName,ISNULL([A],0) [A],ISNULL([L],0)[L] FROM
(
-- Source data for pivoting
SELECT P.PName,Status1,
COUNT(Status1)OVER(PARTITION BY PNAME,Status1)CNT
FROM #PROGRAM P
JOIN #Student S ON P.PCODE=S.SCODE
) x
PIVOT
(
--Defines the values in each dynamic columns
MIN(CNT)
-- Get the names of columns to pivot
FOR Status1 IN ([A],[L])
) p
ORDER BY PName
Click here to view result
2. DYNAMIC PIVOT
Dynamic pivoting can be done if the number of columns is not known in advance.
First of all get columns dynamically to pivot
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + Status1 + ']', '[' + Status1 + ']')
FROM (SELECT DISTINCT Status1 FROM #Student) PV
ORDER BY Status1
Now the below variable is used to replace NULL with zero.
DECLARE #NulltoZeroCols NVARCHAR (MAX)
SELECT #NullToZeroCols = SUBSTRING((SELECT ',ISNULL(['+Status1+'],0) AS ['+Status1+']'
FROM (SELECT DISTINCT Status1 FROM #Student)TAB
ORDER BY Status1 FOR XML PATH('')),2,8000)
Now pivot it. I have written the logic inside
DECLARE #query NVARCHAR(MAX)
SET #query = '-- This outer query forms your pivoted result
SELECT PName,'+#NullToZeroCols+' FROM
(
-- Source data for pivoting
SELECT P.PName,Status1,
COUNT(Status1)OVER(PARTITION BY PNAME,Status1)CNT
FROM #PROGRAM P
JOIN #Student S ON P.PCODE=S.SCODE
) x
PIVOT
(
--Defines the values in each dynamic columns
MIN(CNT)
-- Get the names from the #cols variable to show as column
FOR Status1 IN (' + #cols + ')
) p
ORDER BY PName;'
EXEC SP_EXECUTESQL #query
Click here to view result

Pivot SQL table

I have a table with a column "date" and "name".
Here you can find a sample of my table: http://sqlfiddle.com/#!2/eddede/1
What I need to do is to count how many rows a person has for every year. If the person doesn't have a value in year X, you must see 0. The order of the names must be on total count of current year DESC, so at the moment: 2014.
I can count for every year/name, but not sure I need this for the pivot table: http://sqlfiddle.com/#!2/eddede/3
I would like that the result should look like this:
NAME 2012 2013 2014 TOTAL
Person B 2 2 2 6
Person C 0 1 2 3
Person A 4 3 1 8
I've tried, but I get something like this (So I don't post the wrong SQL-query...)
2012 2013 2014
6 6 5
You can get the result using an aggregate function with some conditional logic like a CASE expression:
select
name,
sum(case when year(date) = 2012 then 1 else 0 end) [2012],
sum(case when year(date) = 2013 then 1 else 0 end) [2013],
sum(case when year(date) = 2014 then 1 else 0 end) [2014],
count(*) Total
from list
group by name;
See SQL Fiddle with Demo
Now, if the year values are unknown, then you will need to use dynamic SQL. This creates a string of the SQL that you need to execute:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#orderby nvarchar(max)
select #cols
= STUFF((SELECT ',' + QUOTENAME(year(date))
from list
group by year(date)
order by year(date)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #orderby = 'ORDER BY ['+cast(year(getdate()) as varchar(4)) + '] desc'
set #query = 'SELECT name, ' + #cols + ', Total
from
(
select name, year(date) dt,
count(*) over(partition by name) Total
from list
) x
pivot
(
count(dt)
for dt in (' + #cols + ')
) p '+ #orderby
exec sp_executesql #query;
See SQL Fiddle with Demo
I think this is what you are looking for:
SELECT
name,
SUM(if(year(date) = 2012, 1, 0)) AS '2012',
SUM(if(year(date) = 2013, 1, 0)) AS '2013',
SUM(if(year(date) = 2014, 1, 0)) AS '2014'
FROM list
GROUP BY name;
and with total :)
SELECT
t.name,
t.`2012`,
t.`2013`,
t.`2014`,
t.`2012` + t.`2013` + t.`2014` as total
FROM
(
SELECT
name,
SUM(if(year(date) = 2012, 1, 0)) AS `2012`,
SUM(if(year(date) = 2013, 1, 0)) AS `2013`,
SUM(if(year(date) = 2014, 1, 0)) AS `2014`
FROM list
GROUP BY name
) as t

pivoting rows to columns in tsql

I have the following table with the following sample data
ID Language Question SubQuestion SubSubQuestion TotalCount TotalPercent
3 E 9 0 1 88527 73%
3 E 9 0 2 19684 16%
3 E 9 0 3 12960 11%
3 E 9 0 9 933 1%
I want all in one row like this
ID Language TotalCount901 TotalPercent901 TotalCount902 TotalPercent902 TotalCount903 TotalPercent903
3 E 88527 73% 19684 16% 12960 11%
I've tired using the pivot command, but it dosnt to work for me.
I made a few assumptions based on your column names, but it looks like you want to use something similar to this. This applies both an UNPIVOT and then a PIVOT to get the values in the columns you requested:
select *
from
(
select id,
language,
col + cast(QUESTION as varchar(10))
+cast(subquestion as varchar(10))
+cast(SubSubQuestion as varchar(10)) col,
value
from
(
select id, language,
cast(TotalCount as varchar(10)) TotalCount,
totalPercent,
question, subquestion, SubSubQuestion
from yourtable
) usrc
unpivot
(
value
for col in (totalcount, totalpercent)
) un
) srcpiv
pivot
(
max(value)
for col in ([TotalCount901], [totalPercent901],
[TotalCount902], [totalPercent902],
[TotalCount903], [totalPercent903],
[TotalCount909], [totalPercent909])
) p
See SQL Fiddle with Demo
Note: when performing the UNPIVOT the columns need to be of the same datatype. If they are not, then you will need to convert/cast to get the datatypes the same.
If you have an unknown number of values to transform, you can use dynamic sql:
DECLARE #query AS NVARCHAR(MAX),
#colsPivot as NVARCHAR(MAX)
select #colsPivot
= STUFF((SELECT ','
+ QUOTENAME(c.name +
cast(QUESTION as varchar(10))
+cast(subquestion as varchar(10))
+cast(SubSubQuestion as varchar(10)))
from yourtable t
cross apply sys.columns as C
where C.object_id = object_id('yourtable') and
C.name in ('TotalCount', 'TotalPercent')
group by c.name, t.question, t.subquestion, t.subsubquestion
order by t.SubSubQuestion
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'select *
from
(
select id,
language,
col + cast(QUESTION as varchar(10))
+cast(subquestion as varchar(10))
+cast(SubSubQuestion as varchar(10)) col,
value
from
(
select id, language,
cast(TotalCount as varchar(10)) TotalCount,
totalPercent,
question, subquestion, SubSubQuestion
from yourtable
) usrc
unpivot
(
value
for col in (totalcount, totalpercent)
) un
) srcpiv
pivot
(
max(value)
for col in (' + #colsPivot + ')
) p '
execute(#query)
See SQL Fiddle with Demo

Convert Rows to columns using 'Pivot' in mssql when columns are string data type

I need to know whether 'pivot' in MS SQL can be used for converting rows to columns if there is no aggregate function to be used. i saw lot of examples with aggregate function only. my fields are string data type and i need to convert this row data to column data.This is why i wrote this question.i just did it with 'case'. Can anyone help me......Thanks in advance.
You can use a PIVOT to perform this operation. When doing the PIVOT you can do it one of two ways, with a Static Pivot that you will code the rows to transform or a Dynamic Pivot which will create the list of columns at run-time:
Static Pivot (see SQL Fiddle with a Demo):
SELECT *
FROM
(
select empid, wagecode, amount
from t1
) x
pivot
(
sum(amount)
for wagecode in ([basic], [TA], [DA])
) p
Dynamic Pivot:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(wagecode)
FROM t1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT empid, ' + #cols + ' from
(
select empid, wagecode, amount
from t1
) x
pivot
(
sum(amount)
for wagecode in (' + #cols + ')
) p '
execute(#query)
Both of these will give you the same results
sample format
empid wagecode amount
1 basic 1000
1 TA 500
1 DA 500
2 Basic 1500
2 TA 750
2 DA 750
empid basic TA DA
1 1000 500 500
2 1500 750 750
THE ANSWER I GOT IS
SELECT empID , [1bas] as basic, [1tasal] as TA,[1otsal] as DA
FROM (
SELECT empID, wage, amount
FROM table) up
PIVOT (SUM(amt) FOR wgcod IN ([1bas], [1tasal],[1otsal])) AS pvt
ORDER BY empID
GO
Try this:
SELECT empid AS EmpID
, ISNULL(SUM(CASE wagecode WHEN 'basic' THEN Amount ELSE 0 END), 0) AS Basic
, ISNULL(SUM(CASE wagecode WHEN 'ta' THEN Amount ELSE 0 END), 0) AS TA
, ISNULL(SUM(CASE wagecode WHEN 'da' THEN Amount ELSE 0 END), 0) AS DA
FROM Employee
GROUP BY empid