Sorry I know similar question have been asked many times before but like most I haven't been able to find one that fits what I need or gives me enough information to be able to expand towards the solution.
I suspect I could use the PIVOT or UNPIVOT commands of T-SQL but I can't follow the MSDN explanation and there don't seem to be any "idiots" type guides out there!
Onto my problem - I need to convert a wide table consisting of many columns to a row based result set for a report. No aggregation needed and I'm trying to avoid a repeated UNION based select if possible.
The table result set is formatted as such (there are actually many more person columns! :s ):
Person1 | Person2 | Person3 | Person4 | Person5 | Person6 | Person7 | Person8
-----------------------------------------------------------------------------
Bob Sam Tom Alex Paul Ann Jill Jane
What I really need is to be able to produce the following:
Person
--------------------
Bob
Sam
Tom
Alex
Paul
Ann
Jill
Jane
A bonus would be able to create a result set such as:
Column Person
--------------------
Person1 Bob
Person2 Sam
Person3 Tom
Person4 Alex
Person5 Paul
Person6 Ann
Person7 Jill
Person8 Jane
How can this be achieved using T-SQL in SQL Server 2005?
Thanks for any help,
Paul.
--Edit--
Thanks to Martin I've learnt something new this morning and I've managed to get exactly what I needed. In the end I had to modify the example slightly to get what I needed but that's because my original example left out some detail that I hadn't realised would be important!
My final piece of code looked like this for anyone else that has such a problem:
WITH Query_CTE([Person1 Title],[Person2 Title],[Person3 Title])
AS
--CTE Expression and column list
(
SELECT
--Converted to create a common data type.
CONVERT(NVARCHAR(MAX),Person1Title) AS 'Person1 Title',
CONVERT(NVARCHAR(MAX),Person2Title) AS 'Person2 Title',
CONVERT(NVARCHAR(MAX),Person3Title) AS 'Person3 Title'
FROM Table_Name
WHERE KeyId = 'XXX'
)
SELECT *
FROM Query_CTE
UNPIVOT
(Person FOR [Column] IN
(
[Person1 Title],
[Person2 Title],
[Person3 Title]
)
)AS unpvt;
WITH T(Person1,Person2 /* etc....*/) AS
(
SELECT 'Bob','Sam' /* etc....*/
)
SELECT *
FROM T
UNPIVOT
(Person FOR [Column] IN
(Person1,Person2 /* etc....*/)
)AS unpvt;
To easily transpose columns into rows with its names you should use XML. In my blog I was described this with example: http://sql-tricks.blogspot.com/2011/04/sql-server-rows-transpose.html
Related
This is a clarification/follow-up on the earlier question where I didn't specify the requirement for null values.
Given this input:
Row id app_date inventor.name inventor.country
1 id_1 01-15-2022 Steve US
Ashley US
2 id_2 03-16-2011 Pete US
<null> US
Mary FR
I need to extract name from inventor struct and concatenate them for each id, like so:
Row id app_date inventors
1 id_1 01-15-2022 Steve, Ashley
2 id_2 03-16-2011 Pete, ^, Mary
Note custom filling for null value - which, to me, seems like it means I need to use ARRAY_TO_STRING specifically that supports this.
The closest example I found doesn't work with nulls. How can one do this?
Use below
SELECT * EXCEPT(inventor),
(SELECT STRING_AGG(IFNULL(name, '^'), ', ') FROM t.inventor) inventors
FROM sample t
with output
Due to the way a particular table is written I need to do something a little strange in SQL and I can't find a 'simple' way to do this
Table
Name Place Amount
Chris Scotland
Chris £1
Amy England
Amy £5
Output
Chris Scotland £1
Amy England £5
What I am trying to do is above, so the null rows are essentially ignored and 'grouped' up based on the Name
I have this working using For XML however it is incredibly slow, is there a smarter way to do this?
This is where MAX would work
select
Name
,Place = Max(Place)
,Amount = Max(Amount)
from
YourTable
group by
Name
Naturally, if you have more than one occurance of a place for a given name, you may get unexpected results.
Example
Team | Person1 | Person2 | Person3
change to
Team | Person 1
Team | Person 2
Team | Person 3
I wasn't sure how to describe exactly what it was that I wanted to do, sorry if this is a duplicate.
Use UNPIVOT
For table dbo.Teams, using your example:
SELECT Team, Person
FROM
(
SELECT Team, Person1, Person2, Person3
FROM dbo.Teams
) as cp
UNPIVOT
(
Person FOR Persons IN (Person1, Person2, Person3)
) AS up;
While using UNIONs works, it does not scale well, costing a lot more for each additional column you need to transpose.
NOTE: This is basically a clone of the solution to a near identical question at this article, provided in the initial question comments by user #Goat CO. See the link for additional information and expansion on this solution.
I think you might try (besides the the solution user #GoatCO mentioned in the comments) the following query:
SELECT Team, [Person 1] As Person_Column
FROM _table
UNION ALL
SELECT Team, [Person 2] As Person_Column
FROM _table
UNION ALL
SELECT Team, [Person 3] As Person_Column
FROM _table
I have taken a good look around and not been able to find any questions that match mine. Maybe I am not using the right language when searching or whatever, but here goes.
I have an SQL table called Classes that looks something like this
Student_Name | Class
--------------------
Edgar | Chemistry
Allan | Chemistry
Burt | Chemistry
Edgar | Math
Sue | Math
Hamilton | Math
Edgar | English
Sue | English
Edgar | German
Ben | German
I want to count how many students are taking both Math and German.
Assuming the following in this example:
- Student names are unique
- One student can have many classes
Logically I would use a select statement to get a result set of students who are taking Math. Then I would go through each Student_Name from the result set and check them against the table to see how many are taking German.
In this case I would expect a return of 1 as only Edgar is taking both Math and German.
Here are some of the queries I have tried so far to no avail :-(
This one was after doing some research on DISTINCT:
SELECT COUNT(DISTINCT Student_Name) FROM Classes WHERE Class = 'Math' AND Class = 'German';
And this one was after finding out more about GROUP BY:
SELECT COUNT(*) FROM (
SELECT DISTINCT Student_Name FROM Classes
WHERE Class IN ( 'Math', 'German' )
GROUP BY Student_Name
);
Neither of these came out quite right any help would be highly appreciated.
SELECT COUNT(*) totalStudent
FROM
(
SELECT student_name
FROM Classes
WHERE class IN ('Math','German')
GROUP BY student_name
HAVING COUNT(*) = 2
) subAlias
SQLFiddle Demo
OUTPUT
╔══════════════╗
║ TOTALSTUDENT ║
╠══════════════╣
║ 1 ║
╚══════════════╝
Could also do the following:
select count(distinct a.Student_name)
from Classes a inner join Classes b on
a.Class = 'German' and
b.Class = 'Math' and
a.Student_Name = b.Student_name;
This solves the problem where the table contains duplicate rows (as pointed out by a commenter to another answer)
I have the following data structure :
FIRSTNAME AGE NICKNAME
Jack 28 Benny
Robert 30 Benny
Pascal 20 Benny
Charles 19 Lence
Anthony 20 Lence
The first column is unique.
The idea is that I have to count how many times the "nickname" is used and I want to output it so that I can I have the following result :
Benny 3
Lence 2
What is the best performant way to do so knowing that I have millions of lines?
Try this:
SELECT NICKNAME,COUNT(NICKNAME)
FROM MyTable
GROUP BY NICKNAME
SELECT NICKNAME, SUM(1) FROM table GROUP BY NICKNAME
agregate count group by firstname, there's no other solution.
by the way, the benny nickname only appears 3 times, not 4.