SQL Pivot on dates column? - sql

I'm fairly new to SQL but believe me I have searched for help before posting this.
I have a query which returns a list of people assigned to jobs, also the jobs have varying length and people who are assigned to those jobs are working different lengths.
What I am trying to do is convert what is a list of similar records with the only variable changing is the date, and some how pivot this data so that the dates become column headings and the rows represent a BOOL yes/no.
This is the data I'm getting back currently. JSON encoded
{"results":[{"role":"Vision Supervisor","familyname":"Unsworth","givenname":"Simon","skill":"10","level":"Telegenic Staff","id":"664","date":"2013-03-27"},{"role":"Vision Supervisor","familyname":"Unsworth","givenname":"Simon","skill":"10","level":"Telegenic Staff","id":"664","date":"2013-03-26"},{"role":"Vision Supervisor","familyname":"Unsworth","givenname":"Simon","skill":"10","level":"Telegenic Staff","id":"664","date":"2013-03-25"},{"role":"Vision Supervisor","familyname":"Unsworth","givenname":"Simon","skill":"10","level":"Telegenic Staff","id":"664","date":"2013-03-24"}]}
and what I would like to get back is:
{"results":[{"role":"Vision Supervisor","familyname":"Unsworth","givenname":"Simon","skill":"10","level":"Telegenic Staff","id":"664","2013-03-27":"YES","2013-03-26":"YES","2013-03-25":"YES","2013-03-24":"YES"}]}
I'm sure this is some kind of PIVOT query but I cant get it to work.
Thanks

If you are going to be running this query in SQL Server, then you can use the PIVOT function:
select *
from
(
select role, familyname, givenname, skill,
level, id, date, 'Y' flag
from yourtable
) src
pivot
(
max(flag)
for date in ([2013-03-27], [2013-03-26],
[2013-03-25], [2013-03-24])
) piv
See SQL Fiddle with Demo
Or you can use an aggregate function and a CASE statement:
select role, familyname, givenname, skill,
level, id,
max(case when date = '2013-03-27' then flag end) '2013-03-27',
max(case when date = '2013-03-26' then flag end) '2013-03-26',
max(case when date = '2013-03-25' then flag end) '2013-03-25',
max(case when date = '2013-03-24' then flag end) '2013-03-24'
from
(
select role, familyname, givenname, skill,
level, id, date, 'Y' flag
from yourtable
) src
group by role, familyname, givenname, skill,
level, id
See SQL Fiddle with Demo
Both give the result:
| ROLE | FAMILYNAME | GIVENNAME | SKILL | LEVEL | ID | 2013-03-27 | 2013-03-26 | 2013-03-25 | 2013-03-24 |
----------------------------------------------------------------------------------------------------------------------------------
| Vision Supervisor | Unsworth | Simon | 10 | Telegenic Staff | 664 | Y | Y | Y | Y |
The above works great if you know the values to transpose, but you if you don't then you can use dynamic sql similar to this:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(convert(char(10), date, 120))
from yourtable
group by date
order by date desc
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT role, familyname, givenname, skill,
level, id,' + #cols + ' from
(
select role, familyname, givenname, skill,
level, id, date, ''Y'' flag
from yourtable
) x
pivot
(
max(flag)
for date in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo

Related

query to combine multiple columns in one columns

I have table which contain the following information.
I want to run a query and show only one line. something like this
I try to use a case statement, However i maybe have other employee who work in different state, and i do not want to list 50 state since most of employee may only work 2-3 state, but in different state. Any help will appreciate. Thanks
Since you want to pivot on multiple columns, my suggestion would be to first look at unpivoting the State, StateWages and StateTax columns then apply the PIVOT function.
You didn't specify what version of SQL Server you are using but you can use either UNPIVOT to CROSS APPLY to convert these multiple columns into rows. The syntax will be similar to:
select PRCo, Employee,
col = c.col + cast(seq as varchar(50)),
c.value
from
(
select PRCo, Employee, State, StateWages, StateTax,
row_number() over(partition by PRCo, Employee
order by state) seq
from yourtable
) d
cross apply
(
select 'State', State union all
select 'StateWages', cast(StateWages as varchar(10)) union all
select 'StateTax', cast(StateTax as varchar(10))
) c (col, value);
See SQL Fiddle with Demo. You'll notice that before I implemented the CROSS APPLY, I used a subquery with row_number() to create a unique value for each row per employee. The reason for doing this is so you can return the multiple state columns, etc. Once you have done this you can use PIVOT:
select PRCo, Employee,
State1, StateWages1, StateTax1,
State2, StateWages2, StateTax2
from
(
select PRCo, Employee,
col = c.col + cast(seq as varchar(50)),
c.value
from
(
select PRCo, Employee, State, StateWages, StateTax,
row_number() over(partition by PRCo, Employee
order by state) seq
from yourtable
) d
cross apply
(
select 'State', State union all
select 'StateWages', cast(StateWages as varchar(10)) union all
select 'StateTax', cast(StateTax as varchar(10))
) c (col, value)
) src
pivot
(
max(value)
for col in (State1, StateWages1, StateTax1,
State2, StateWages2, StateTax2)
) p;
See SQL Fiddle with Demo. The above version works great if you have a limited number of value but it sounds like you need a dynamic solution. Using the code above you can easily convert it to dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col+cast(seq as varchar(10)))
from
(
select row_number()
over(partition by PRCo, Employee
order by state) seq
from yourtable
) d
cross apply
(
select 'State', 1 union all
select 'StateWages', 2 union all
select 'StateTax', 3
) c (col, so)
group by col, so, seq
order by seq, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT PRCo, Employee, ' + #cols + N'
from
(
select PRCo, Employee,
col = c.col + cast(seq as varchar(50)),
c.value
from
(
select PRCo, Employee, State, StateWages, StateTax,
row_number() over(partition by PRCo, Employee
order by state) seq
from yourtable
) d
cross apply
(
select ''State'', State union all
select ''StateWages'', cast(StateWages as varchar(10)) union all
select ''StateTax'', cast(StateTax as varchar(10))
) c (col, value)
) x
pivot
(
max(value)
for col in (' + #cols + N')
) p '
exec sp_executesql #query;
See SQL Fiddle with Demo. Both versions will give a result:
| PRCO | EMPLOYEE | STATE1 | STATEWAGES1 | STATETAX1 | STATE2 | STATEWAGES2 | STATETAX2 |
|------|----------|--------|-------------|-----------|--------|-------------|-----------|
| 1 | 304 | CA | 20162.03 | 804.42 | IN | 20162.03 | 665.90 |

Selecting data for columns based on a range of dates

I have a table that has a week_id and net_sales for that week (as well as a lot of other columns).
style_number, week_id, net_sales
ABCD, 1, 100.00
ABCD, 2, 125.00
EFGH, 1, 50.00
EFGH, 2, 75.00
I am trying to write a statement that will list the
style_number, net_sales
for the
MAX(week_id), net_sales for the MAX(week_id)-1 .... , MAX(week_id) - n
So that the results look like:
ABCD, 125.00, 100.00
EFGH, 75.00, 50.00
What is the best way to approach this, especially when n can be rather large (i.e. looking back 52 weeks)?
I hope this makes sense! I am using SQL Server 2008 R2. Thanks a lot in advance!
You can use PIVOT and dynamic SQL to deal with your large number of weeks
DECLARE #cols NVARCHAR(MAX), #sql NVARCHAR(MAX)
SET #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(week_id)
FROM sales
ORDER BY 1 DESC
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'),1,1,'')
SET #sql = 'SELECT style_number, ' + #cols +
' FROM
(
SELECT style_number, week_id, net_sales
FROM sales
) x
PIVOT
(
MAX(net_sales) FOR week_id IN (' + #cols + ')
) p
ORDER BY style_number'
EXECUTE(#sql)
Here is SQLFiddle demo.
If you know the number of weeks, since you are using SQL Server 2008, you can use the PIVOT command, or you can use MAX with CASE:
Here's an example using MAX with CASE:
select
style_number,
max(case when week_id = 2 then net_sales end) week2sales,
max(case when week_id = 1 then net_sales end) week1sales
from yourtable
group by style_number
SQL Fiddle Demo
If you do not know the number of weeks, you'll need to look into using dynamic SQL. Just do a search, lots of posts on SO on it.
You might consider using the PIVOT command: http://msdn.microsoft.com/en-us/library/ms177410(v=sql.105).aspx
OR
If you are okay with the result being a comma separated list, you could use the STUFF and FOR XML commands like so.
SELECT DISTINCT
style_name,
STUFF(
(SELECT ',' + CAST(net_sales AS VARCHAR(20))
FROM MyTable AS SubTable
WHERE SubTableUser.style = MyTable.style_name
ORDER BY week_id DESC --DESC will get your max ID to be first
FOR XML PATH('')), 1, 1, '') AS net_sales_list
FROM MyTable
ORDER BY style_name
This will provide you with:
style_name | net_sales_list
---------------------------
ABCD | 100.00,125.00
EFGH | 75.00,50.00

How to change row into column?

How can I change this table
Name subject Mark
Aswin physics 100
Aswin chemistry 300
Aswin maths 200
Into
Aswin Physics 100 Chemistry 300 Maths 200
Any one please help me.
you can use PIVOT operator to do this job in sql server.
check these links link1 and link2 they will show how to change row into column.
hope this helps you!
SQLFiddle demo
select Name,
sum(CASE
when [subject]='physics' then Mark
end) as Physics,
sum(CASE
when [subject]='chemistry' then Mark
end) as chemistry,
sum(CASE
when [subject]='maths' then Mark
end) as maths
from t group by Name
Or if you need it in one line:
SQLFiddle demo
SELECT
t1.name,
MemberList = substring((SELECT ( ', ' + subject+' - '+
cast(Mark as varchar(100)) )
FROM t t2
WHERE t1.name = t2.name
ORDER BY
name,
subject
FOR XML PATH( '' )
), 3, 1000 )FROM t t1
GROUP BY name
You need to use SQL Pivoting, check the examples at SQL SERVER – PIVOT and UNPIVOT Table Examples. Using Sql Pivoting you can change the rows to columns and Unpivoting is for columns to rows conversion.
Please note: I am checking if I can provide you exact script but for now the link would help you out.
UPDATE
Code example
Though I have not tested this with actual data but it parses fine.
-- Pivot Table ordered by Name of Student
SELECT Name, Physics, Chemistry, Maths
FROM (
SELECT Name, Subject, Mark
FROM Student) up
PIVOT (SUM(Mark) FOR Student IN (Physics, Chemistry, Maths)) AS pvt
ORDER BY Name
-- Result should be something like
----------------------------------
Name Physics Chemistry Maths
----------------------------------
Aswin 100 300 200
----------------------------------
For creating pivot you need to know the actual rows values to convert into columns.
I have wrote before about dynamic pivoting here if you find it useful.
It is not exactly clear if you want this data in separate columns or in one column.
If you want this in separate columns, then you can apply the PIVOT function which became available in SQL Server 2005.
If you know all of the values that you want to transform or have a limited number, then you can hard-code the query:
select *
from
(
select name, subject +' '+ cast(mark as varchar(9)) as sub_mark,
'Subject_'+cast(row_number() over(partition by name
order by subject) as varchar(10)) col_name
from subjects
) s
pivot
(
max(sub_mark)
for col_name in (Subject_1, Subject_2, Subject_3)
) piv;
See SQL Fiddle with Demo. You will notice that I did this slightly different from the other pivot answer. I placed both the subject/mark in the same column with a column name of Subject_1, etc.
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('Subject_'+cast(row_number() over(partition by name
order by subject) as varchar(10)))
from subjects
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT name,' + #cols + ' from
(
select name, subject +'' ''+ cast(mark as varchar(9)) as sub_mark,
''Subject_''+cast(row_number() over(partition by name
order by subject) as varchar(10)) col_name
from subjects
) x
pivot
(
max(sub_mark)
for col_name in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo. The dynamic sql version will increase in the number of columns if a name has more than 3 subjects.
The result of both queries is:
| NAME | SUBJECT_1 | SUBJECT_2 | SUBJECT_3 |
---------------------------------------------------
| Aswin | chemistry 300 | maths 200 | physics 100 |

What SQL query would produce these results?

Given this data:
Name Property Value
---------- ---------- ----------
Bob Hair Red
Bob Eyes Blue
Fred Hair Brown
Fred Height Tall
what SQL would be required to produce these results?
Property Bob Fred
---------- ---------- ----------
Hair Red Brown
Eyes Blue
Height Tall
I'm using SQL Server 2008, but a generic solution would be nice.
You did not specify what RDBMS you are using but this is a pivot. You can use an aggregate function and a CASE expression in all databases:
select property,
max(case when name='Bob' then value else '' end) Bob,
max(case when name='Fred' then value else '' end) Fred
from yourtable
group by property
See SQL Fiddle with Demo
If you are using a database that has a PIVOT function (SQL Server 2005+/Oracle 11g+), then your code will be similar to this:
select *
from
(
select property, name, value
from yourtable
) src
pivot
(
max(value)
for name in (Bob, Fred)
) piv
See SQL Fiddle with Demo
The above queries work great, if you know the name values ahead of time, but if you don't then you will want to use dynamic sql:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(name)
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT property,' + #cols + ' from
(
select property, name, value
from yourtable
) x
pivot
(
max(value)
for name in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo
All three will produce the same result:
| PROPERTY | BOB | FRED |
---------------------------
| Eyes | Blue | |
| Hair | Red | Brown |
| Height | | Tall |
Here's the old-fashioned way, of course assuming that each (Name, Property) is unique:
SELECT Properties.Property, Bob.Value, Fred.Value
FROM
(
SELECT DISTINCT Property
FROM myTable
) Properties
LEFT OUTER JOIN
(
SELECT Property, Value
FROM myTable
WHERE Name = 'Bob'
) Bob ON Properties.Property = Bob.Property
LEFT OUTER JOIN
(
SELECT Property, Value
FROM myTable
WHERE Name = 'Fred'
) Fred ON Properties.Property = Fred.Property
Of course you can only do this if you know the columns ahead of time. You could make and execute dynamic SQL if you did not, but this is not without its issues.
Depending on your RDBMS you may be able to use a pivot query instead, which will simplify the syntax (or make it possible if you have an unknown number/names of people)

SQL Server query for grouping row as column and getting corresponding designation

I have 2 tables in SQL Server:
Table 1 : Department
DeptId Dept Name
------------------
1 Software Development
2 Testing
3 Customization
Table 2 : Designation
DesigId Desig Name DeptId
---------------------------
1 TL 1
2 PL 1
3 TestEngg 2
4 SE 3
I want the following output which takes department as column heading and group designation under the corresponding department column,
Software Development Testing Customization
TL TestEngg SE
PL
I tried with the below query but Im able to get only the Id's
DECLARE #deptcols AS VARCHAR(MAX);
DECLARE #querystr AS VARCHAR(MAX);
select #deptcols = STUFF((SELECT distinct ',' + QUOTENAME(Dept_Id)
FROM Designation
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #querystr = 'SELECT ' + #deptcols + ' from
(
select Desig_Name, Dept_Id,Desig_Id
from Designation
) p
pivot
(
count(Desig_Id) FOR Dept_Id in (' + #deptcols + ')
) pv '
execute(#querystr)
Your code was so very close. My suggestion when working with PIVOT especially a dynamic version is to write the static version first, then convert it to dynamic sql.
The static version, where you hard-code values is like this:
SELECT [Software Development], [Testing], [Customization]
from
(
select d.[Dept Name],
s.[Desig Name],
row_number() over(partition by s.deptid order by s.desigid) rn
from Designation s
left join department d
on s.[DeptId] = d.[DeptId]
) p
pivot
(
max([Desig Name])
FOR [Dept Name] in ([Software Development], [Testing], [Customization])
) pv
See SQL Fiddle with Demo. The static version allows you to be sure that the syntax is correct and all values, columns etc are in the right places.
Then once you have the syntax it is easy to convert to the dynamic SQL version:
DECLARE #deptcols AS VARCHAR(MAX)
DECLARE #querystr AS VARCHAR(MAX)
select #deptcols = STUFF((SELECT ',' + QUOTENAME([Dept Name])
FROM Department
GROUP BY [Dept Name], DeptId
ORDER BY DeptId
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #querystr =
'SELECT ' + #deptcols + ' from
(
select d.[Dept Name],
s.[Desig Name],
row_number() over(partition by s.deptid order by s.desigid) rn
from Designation s
left join department d
on s.[DeptId] = d.[DeptId]
) p
pivot
(
max([Desig Name])
FOR [Dept Name] in (' + #deptcols + ')
) pv '
execute(#querystr)
See SQL Fiddle with Demo
Both give the result:
| SOFTWARE DEVELOPMENT | TESTING | CUSTOMIZATION |
---------------------------------------------------
| TL | TestEngg | SE |
| PL | (null) | (null) |
You will notice that I added the line row_number() over(partition by s.deptid order by s.desigid) rn to the SELECT statement. This allows you to return more than one Desig Name for each Dept Name, without this you will only return one value.
I think PIVOT keyword is what you should use here. PIVOT can be used to transform datasets such that columns become rows.
No need to construct dynamic queries
Take a look at this article: http://archive.msdn.microsoft.com/SQLExamples/Wiki/View.aspx?title=PIVOTData
More info: http://msdn.microsoft.com/en-us/library/ms177410(v=sql.105).aspx