What SQL query would produce these results? - sql

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)

Related

Convert couple of rows into single row based on Duplicate ID in sql server from single/same table

I am going to do some arthimetic calulation for particular Id with two different category code.
So i have the below two row as input,
I am expecting the below output as,
I am trying to do some self join but i am not luckly getting the desired output.
Can you pls help me out here?
Note: Only two row will come for particular ID (Two type of categories will come [A or C]).
Only two row will come for particular ID (Two type of categories will
come [A or C]).
Then this query should be sufficient:
SELECT ID,
ValueA = SUM(CASE WHEN Category = 'A' THEN Value END),
ValueC = SUM(CASE WHEN Category = 'C' THEN Value END)
FROM dbo.TableName
GROUP BY ID
Sql-Fiddle
Method 1:
Try this
SELECT *
FROM Table1
Pivot (MAX(value) for Category in ([A],[C])) as Value
Fiddle Demo
Output:
+-------------+-------+-------+
| ID | A |C |
+-------------+-------+-------+
| WD559606479 | 0.748 |2.088 |
+-------------+-------+-------+
Method 2: Dynamic Pivot
If you have one more column in the input then
Try this
DECLARE #cols AS NVARCHAR(MAX)
DECLARE #query AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.category)
FROM table1 c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query ='SELECT id, ' + #cols + ' from
(
SELECT id
, category
, value
FROM table1
) x
Pivot
(
Max(value)
For category In (' + #cols + ')
) P'
Execute(#query)
Fiddle Demo
Output:
+-------------+-------+-------+
| ID | A |C |
+-------------+-------+-------+
| WD559606479 | 0.748 |2.088 |
+-------------+-------+-------+

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 |

Pivoting a Table with Text and no Aggregation

I've been looking through StackOverflow questions and haven't really found one for this issue.
I have a table that's arranged basically like:
Table.LineID
Table.QuestionGroup
Table.Question
Table.Answer
And I'd like to "pivot" the table, but the questions and answers don't have anything to aggregate. They're all just text fields for example, one might read:
Table.LineID = 00001
Table.QuestionGroup = Color
Table.Question = Outside Color
Table.Answer = Dark Green
Table.LineID = 00001
Table.QuestionGroup = Size
Table.Question = Height
Table.Answer = 180 3/4
I'm trying to pivot the table so that I can get the associated ID of line and make that the spreading function the Questions So it would look like
LineID | Question 1 | Question 2 | Etc...
| Answer 1 | Answer 2 | Etc...
I've looked at this https://stackoverflow.com/a/7744347/839330 except this seems to be more in reference to some VB code (maybe I'm wrong?). Does anyone have an idea on how I should approach this?
Just because you have text data in your table does not mean that you cannot use an aggregate function on it.
You did not specify what RDBMS you are using but this type of data transformation is a pivot. This converts row data into columns. Some databases has a pivot function but if you are using one without that function, then you will need to use an aggregate function with a CASE expression:
select lineid,
max(case when question ='Height' then answer else '' end) Height,
max(case when question ='Outside Color' then answer else '' end) [Outside Color]
from yourtable
group by lineid
See SQL Fiddle with Demo
If you have a database that has the pivot function (SQL Server 2005+/Oracle 11g+), then your code will be similar to this:
select *
from
(
select lineid,
Question,
Answer
from yourtable
) src
pivot
(
max(answer)
for question in ([Height], [Outside Color])
) piv;
See SQL Fiddle with Demo
Now if you are using SQL Server 2005+ and you have an unknown number of questions that you want to turn into columns, then you can use dynamic sql similar to this:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(Question)
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT lineid,' + #cols + ' from
(
select lineid,
Question,
Answer
from yourtable
) x
pivot
(
max(Answer)
for Question in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with demo
Based on your sample data the result is:
| LINEID | HEIGHT | OUTSIDE COLOR |
------------------------------------
| 1 | 180 3/4 | Dark Green |

SQL Pivot on dates column?

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

How do i transform rows into columns in sql server 2005

There is a question here in stackoverflow with the same title but that is not what I am looking for.
I have a table like the one below
Name | Count
----------------
Chery | 257
Drew | 1500
Morgon | 13
Kath | 500
Kirk | 200
Matt | 76
I need to trasform this result set into something like this
Chery | Drew | Morgon | Kath | Kirk | Matt
-------------------------------------------
257 1500 13 500 200 76
How do i acheive this using sql server 2005?
There are similar questions here,here answered in stackoverflow.
You need to use the operator PIVOT in your query to acheive this.Here is the example and explanation on how you can do that.The example is referenced from this source.
---I assumed your tablename as TESTTABLE---
DECLARE #cols NVARCHAR(2000)
DECLARE #query NVARCHAR(4000)
SELECT #cols = STUFF(( SELECT DISTINCT TOP 100 PERCENT
'],[' + t.Name
FROM TESTTABLE AS t
ORDER BY '],[' + t.Name
FOR XML PATH('')
), 1, 2, '') + ']'
SET #query = N'SELECT '+ #cols +' FROM
(SELECT t1.Name , t1.Count FROM TESTTABLE AS t1) p
PIVOT (MAX([Count]) FOR Name IN ( '+ #cols +' ))
AS pvt;'
EXECUTE(#query)
Explanation
1.The first part of the query
SELECT #cols = STUFF(( SELECT DISTINCT TOP 100 PERCENT
'],[' + t.Name
FROM TESTTABLE AS t
ORDER BY '],[' + t.Name
FOR XML PATH('')
), 1, 2, '') + ']'
gives you a nice flattened result of your Name column values in a single row as follow
[Cheryl],[Drew],[Karen],[Kath],[Kirk],[Matt]
You can learn more about the STUFF and XML PATH here and here.
2.SELECT + #cols + FROM will select all the rows as coloumn names for the final result set (pvt - step 3)
i.e
Select [Chery],[Drew],[Morgan],[Kath],[Kirk],[Matt]
3.This query pulls all the rows of data that we need to create the cross-tab results. The (p) after the query is creating a temporary table of the results that can then be used to satisfy the query for step 1.
(SELECT t1.Name, t1.Count FROM TESTTABLE AS t1) p
4.The PIVOT expression
PIVOT (MAX (Count) FOR Name IN ( #cols) AS pvt
does the actual summarization and puts the results into a temporary table called pvt as
Chery | Drew | Morgon | Kath | Kirk | Matt
-------------------------------------------
257 1500 13 500 200 76
See Using PIVOT and UNPIVOT.
You can use the PIVOT and UNPIVOT
relational operators to change a
table-valued expression into another
table. PIVOT rotates a table-valued
expression by turning the unique
values from one column in the
expression into multiple columns in
the output, and performs aggregations
where they are required on any
remaining column values that are
wanted in the final output. UNPIVOT
performs the opposite operation to
PIVOT by rotating columns of a
table-valued expression into column
values.
The quick answer is
SELECT Chery, Drew, Morgon, Kath, Kirk, Matt
FROM
(SELECT [Name], [Count] From Foo)
PIVOT
(
MIN([Count])
FOR [Name] IN (Chery, Drew, Morgon, Kath, Kirk, Matt)
) AS PivotTable
If you want to avoid anything complicated like a pivot or the accepted answer, you can do this! (most of the code is just setting up the test data just in case anyone wants to try it)
/* set up your test table */
declare #TestData table (Name Varchar(80),[Count] int)
insert into #TestData (Name, [count])
Select 'Chery' as name, 257 as [count]
union all select 'Drew', 1500
union all select 'Morgon',13
union all select 'Kath', 500
union all select 'Kirk', 200
union all select 'Matt', 76
/* the query */
Declare #Query Varchar(max)
Select #Query=Coalesce(#query+', ','SELECT ') +Convert(VarChar(5),[count]) +' as ['+name+']'
from #TestData
Execute (#Query)
/* result
Chery Drew Morgon Kath Kirk Matt
----------- ----------- ----------- ----------- ----------- -----------
257 1500 13 500 200 76
*/