Convert rows to columns in MS SQL - sql-server

I'm looking for an efficient way to convert rows to columns in MS SQL server.
Example DB Table:
**ID PersonID Person201Code Person201Value**
1 1 CurrentIdNo 0556
2 1 FirstName Queency
3 1 LastName Sablan
The query result should be like this:
**CurrentIdNo FirstName LastName**
0556 Queency Sablan
I tried using PIVOT but it only return null on row values:
SELECT CurrentIdNo, FirstName, LastName
FROM
(
SELECT ID, PersonId, Person201Code, Person201Value
FROM HRPerson201
) src
PIVOT
(
MAX (ID)
FOR Person201Code in (CurrentIdNo, Firstname, LastName))
pvt;
How can I successfully convert rows to columns in MS SQL server?
Thanks!

Remove the ID from pivot source query and add Person201Value pivot aggregate
instead of ID
SELECT CurrentIdNo,
FirstName,
LastName
FROM (SELECT PersonId,
Person201Code,
Person201Value
FROM HRPerson201) src
PIVOT ( Max (Person201Value)
FOR Person201Code IN (CurrentIdNo,
Firstname,
LastName)) pvt;
SQLFIDDLE DEMO

SELECT *
FROM
(SELECT personid,Person201Code,Person201Value
FROM #pivot) Sales
PIVOT(max(Person201Value)
FOR Person201Code in (CurrentIdNo, Firstname, LastName))
AS PivotSales;

Related

JSON_VALUE is not working in where clause in SQL Server

I have JSON data in a column in my table. I am trying to apply where condition on the JSON column and fetch records.
Employee table:
Here is my SQL query:
SELECT ID, EMP_NAME
FROM EMPLOYEE
WHERE JSON_VALUE(TEAM, '$') IN (2, 3, 4, 5, 7, 10)
I am getting an empty result when I use this query. Any help on how to do this?
You need to parse the JSON in the TEAM column with OPENJSON():
Table:
CREATE TABLE EMPLOYEE (
ID int,
EMP_NAME varchar(50),
TEAM varchar(1000)
)
INSERT INTO EMPLOYEE (ID, EMP_NAME, TEAM)
VALUES
(1, 'Name1', '[2,11]'),
(2, 'Name2', '[2,3,4,5,7,10]'),
(3, 'Name3', NULL)
Statement:
SELECT DISTINCT e.ID, e.EMP_NAME
FROM EMPLOYEE e
CROSS APPLY OPENJSON(e.TEAM) WITH (TEAM int '$') j
WHERE j.TEAM IN (2,3,4,5,7,10)
Result:
ID EMP_NAME
1 Name1
2 Name2
As an additional option, if you want to get the matches as an aggregated text, you may use the following statement (SQL Server 2017 is needed):
SELECT e.ID, e.EMP_NAME, a.TEAM
FROM EMPLOYEE e
CROSS APPLY (
SELECT STRING_AGG(TEAM, ',') AS TEAM
FROM OPENJSON(e.TEAM) WITH (TEAM int '$')
WHERE TEAM IN (2,3,4,5,7,10)
) a
WHERE a.TEAM IS NOT NULL
Result:
ID EMP_NAME TEAM
1 Name1 2
2 Name2 2,3,4,5,7,10
JSON_VALUE returns a scalar value, not a data set, which you appaer to think it would. If you run SELECT JSON_VALUE('[2,3,4,5,7,10]','$') you'll see that it returns NULL, so yes, no rows will be returned.
You need to treat the JSON like a data set, not a single value:
SELECT ID, EMP_NAME
FROM EMPLOYEE E
WHERE EXISTS (SELECT 1
FROM OPENJSON (E.TEAM) OJ
WHERE OJ.Value IN (2,3,4,5,7,10))

SQL Server Group rows with multiple occurences of Group BY columns

I am trying to summarize a dataset and get the minimum and maximum date for each group. However, a group can exist multiple times if there is a gap. Here is sample data:
CREATE TABLE temp (
id int,
FIRSTNAME nvarchar(50),
LASTNAME nvarchar(50),
STARTDATE datetime2(7),
ENDDATE datetime2(7)
)
INSERT into temp values(1,'JOHN','SMITH','2013-04-02','2013-05-31')
INSERT into temp values(2,'JOHN','SMITH','2013-05-31','2013-10-31')
INSERT into temp values(3,'JANE','DOE','2013-10-31','2016-07-19')
INSERT into temp values(4,'JANE','DOE','2016-07-19','2016-08-11')
INSERT into temp values(5,'JOHN','SMITH','2016-08-11','2017-02-01')
INSERT into temp values(6,'JOHN','SMITH','2017-02-01','9999-12-31')
I am looking to summarize the data as follows:
JOHN SMITH 2013-04-02 2013-10-31
JANE DOE 2013-10-31 2016-08-11
JOHN SMITH 2016-08-11 9999-12-31
A "group by" will combine the two John Smith records together with the incorrect min and max dates.
Any help is appreciated.
Thanks.
As JNevill pointed out, this is a classic Gaps and Islands problem. Below is one solution using Row_Number().
Select FirstName
,LastName
,StartDate=min(StartDate)
,EndDate =max(EndDate)
From (
Select *
,Grp = Row_Number() over (Order by ID) - Row_Number() over (Partition By FirstName,LastName Order by EndDate)
From Temp
) A
Group By FirstName,LastName,Grp
Order By min(StartDate)
Please try the following...
SELECT firstName,
lastName,
MIN( startDate ) AS earliestStartDate,
MAX( endDate ) AS latestEndDate
FROM temp
GROUP BY firstName,
lastName;
This statement will use the GROUP BY statement to group together the records based on firstName and lastName combinations. It will then return the firstName and lastName for each group as well as the earliest startDate for that group courtesy of the MIN() function and the latest endDate for that group courtesy of the MAX() function.
If you have any questions or comments, then please feel free to post a Comment accordingly.

merge multiple rows to one in sql

I have the following result set:
ID P1Score P2Score P3Score
===================================
22117617 NULL 50 NULL
22117617 1 NULL NULL
22117617 NULL NULL 40
What I want to do is, merge these rows into one.
I want to place value of P1Score in the P1score column, same with P2score and P3score.
How would one achieve this?
select ID,
max(ISNULL(P1Score,0)),
max(ISNULL(P2Score,0)),
max(ISNULL(P3Score,0))
from [Score] group by ID
You can directly use group by with sum() function since suppose if you have multiple scores for single Id then sum would be appropriate.
SELECT ID
,sum(P1Score) AS P1Score
,sum(P2Score) AS P2Score
,sum(P3Score) AS P3Score
FROM [Score]
GROUP BY ID
If you are using SQL Server 2012 or up, you can use MAX with OVER:
SELECT DISTINCT ID,
MAX(P1Score) OVER (ORDER BY ID) P1Score,
MAX(P2Score) OVER (ORDER BY ID) P2Score,
MAX(P3Score) OVER (ORDER BY ID) P3Score
FROM YourTable
Output:
ID P1Score P2Score P3Score
22117617 1 50 40
Or even pivoting:
SELECT *
FROM YourTable
UNPIVOT (
[Values] FOR PScores IN (P1Score, P2Score, P3Score)
) unp
PIVOT (
MAX([Values]) FOR PScores IN (P1Score, P2Score, P3Score)
) piv

T-SQL group by from multiple years

I barely know how to ask this question aside from the specific example, so here goes:
We have an event registration table, and I want to match registrants that have registered for one of 4 events in each of the preceding 5 years.
The only way I can think of doing this is with verbose sub-queries, but performance-wise it's an absolute dog:
SELECT FirstName, LastName, EmailAddress
FROM RegTable
WHERE EventId IN (1,2,3,4)
AND EventYear = 2011
AND FirstName + LastName + DOB IN (SELECT FirstName + LastName + DOB FROM RegTable WHERE EventId IN (1,2,3,4) AND EventYear = 2012)
And so on for each year. Like I said, not very eloquent or efficient.
Is there a simpler way?
You can do a GROUP BY with HAVING and then do a INTERSECT with current Year events
SELECT FirstName, LastName, DOB
FROM RegTable
WHERE EventId IN (1,2,3,4)
AND EventYear IN (2011,2010,2009,2008,2007)
GROUP BY FirstName, LastName, DOB
HAVING COUNT(Distinct EventYear) = 5
INTERSECT
SELECT DISTINCT FirstName ,LastName ,DOB
FROM RegTable
WHERE EventId IN (1,2,3,4)
AND EventYear = 2012
The above query in action with sample data. SQL Fiddle
I hoestly didn't understand the question, just rewriting your query (assuming that it is doing what you need):
SELECT RT2011.FirstName, RT2011.LastName, RT2011.EmailAddress
FROM
RegTable RT2011,
RegTable RT2012
WHERE RT2011.EventId IN (1,2,3,4)
AND RT2012.EventId IN (1,2,3,4)
AND RT2011.EventYear = 2011
AND RT2012.EventYear = 2012
AND RT2011.FirstName = RT2012.FirstName
AND RT2011.LastName = RT2012.LastName
AND RT2011.DOB = RT2012.DOB

MSSQL: Joining multiple tables with dynamic values

I'm struggling to get following 3 tables into one query:
tPerson
ID FirstName
1 'Jack'
2 'Liz'
tAttribute
ID AttributeName
101 'LastName'
102 'Gender'
tData
PersonID AttributeID AttributeValue
1 101 'Nicholson'
1 102 'Male'
2 101 'Taylor'
2 102 'Female'
Important: The attributes in tAttribute are dynamic. There could be more, e.g.:
ID AttributeName
103 'Income'
104 'MostPopularMovie'
Question: How can I write my query (or queries if neccessary), so that I get following output:
PersonID FirstName LastName Gender [otherFields]
1 'Jack' 'Nicholson' 'Male' [otherValues]
2 'Liz' 'Taylor' 'Female' [otherValues]
I often read "What have you tried so far?", but posting all my failed attempts using subqueries and joins wouldn't make much sense. I'm just not that secure with SQL.
Many thanks in advance.
Thanks to #Tab Alleman, I google for "SQL PIVOT" and came up with following result:
SELECT PersonID,
FirstName,
[LastName],
[Gender]
FROM (
SELECT tPerson.ID AS PersonID,
tPerson.FirstName,
tAttribute.AttributeName,
tData.AttributeValue
FROM tAttribute
INNER JOIN tData ON (
tAttribute.ID = tData.AttributeID
)
INNER JOIN tPerson ON (
tData.PersonID = tPerson.ID
)
) AS unPivotResult
PIVOT (
MAX(AttributeValue)
FOR AttributeName IN ([LastName],[Gender])
) AS pivotResult
Addition: I didn't know how to get LastName and Gender dynamically via SQL, so I did that with ColdFusion, which I use for programming. It will look like this:
<!--- "local.attributes" gets generated by making another query,--->
<!--- I just wrote it statically here for this example --->
<cfset local.attributes = "[LastName],[Gender]" />
<cfquery name="local.persons">
SELECT PersonID,
FirstName,
#local.attributes#
FROM (
...
) AS unPivotResult
PIVOT (
MAX(AttributeValue)
FOR AttributeName IN (#local.attributes#)
) AS pivotResult
</cfquery>
It'd be cool, if I could replace the ColdFusion part with something like
SELECT AttributeName FROM tAttribute and then use that to get the brackets-definition.

Resources