This is the first time I am trying to pivot a table. I have managed to pivot the table with only one measure and failed to do it with multiple measures. Please can I get some advice? Below is the test data I have and I would like to see. Please note that I have around 20 to 30 measures.
Thanks in advance!
-- Test Data HAVE
CREATE TABLE #have
(Name VARCHAR(50),Subject Varchar(20), Marks1 INT,Marks2 INT, Result Varchar(20) )
GO
INSERT INTO #have VALUES('Jsmith','Maths',65,56,'Pass')
INSERT INTO #have VALUES('Jsmith','Science',42,72,'Failed')
GO
-- Test Data WANT
CREATE TABLE #want
(Name VARCHAR(50),Maths_Marks1 INT ,Maths_Marks2 INT,Science_Marks1 INT,Science_Marks2 INT, Maths_Result Varchar(20), Science_Result Varchar(20))
GO
INSERT INTO #want VALUES('Jsmith',65,56,42,72,'Pass','Failed')
GO
select * from #have
select * from #want
-- Pivot table
SELECT Name , [Maths] AS [Maths_Marks1], [Science] AS [Science_Marks1]
FROM
(SELECT Name, Subject, Marks1 FROM #have) as SourceTable
PIVOT
(SUM(Marks1)
FOR Subject in ([Maths],[Science])
) as PivotTable1
-- Also Tried..Unable to get it working
SELECT Name , [Maths] AS [Maths_Marks1], [Science] AS [Science_Marks1]
FROM
(SELECT Name, Subject, Marks1 FROM #have) as SourceTable
PIVOT
(SUM(Marks1)
FOR Subject in ([Maths])
) as PivotTable1
(SELECT Name, Subject, Marks2 FROM #have) as SourceTable
PIVOT
(SUM(Marks2)
FOR Subject in ([Science])
) as PivotTable2
This trick builds on Marks properties : they are integers , mark <1000, exactly 2 marks columns (Marks1, Marks2) are in the table.
CREATE TABLE #have
(Name VARCHAR(50),Subject Varchar(20), Marks1 INT,Marks2 INT )
INSERT INTO #have VALUES('Jsmith','Maths',65,56)
INSERT INTO #have VALUES('Jsmith','Science',42,72)
SELECT Name, [Maths]/1000 AS [Maths_Marks1], [Maths]%1000 AS [Maths_Marks2], [Science]/1000 AS [Science_Marks1], [Science]%1000 AS [Science_Marks2]
FROM (SELECT Name, Subject, 1000*Marks1 + Marks2 Marks FROM #have) t
PIVOT (SUM(Marks)FOR Subject in ([Maths],[Science]) ) as p1
Otherwise , generally do it with conditional aggregates
SELECT Name
, max(case Subject when 'Maths' then Marks1 end ) AS [Maths_Marks1]
, max(case Subject when 'Maths' then Marks2 end ) AS [Maths_Marks2]
, max(case Subject when 'Maths' then Result end ) AS [Maths_Result]
, max(case Subject when 'Science' then Marks1 end ) AS [Science_Marks1]
, max(case Subject when 'Science' then Marks2 end ) AS [Science_Marks2]
, max(case Subject when 'Science' then Result end ) AS [Science_Result]
FROM #have
GROUP BY name
You can try with pivot as below:
Select [Name], Max(Maths_Marks1) as Maths_Marks1, Max(Maths_Marks2) as Maths_Marks2, max([Science_Marks1]) as [Science_Marks1]
,max([Science_Marks2]) as [Science_Marks2], max([Maths_Result]) as [Maths_Result], max([Science_Result]) as [Science_Result]
from (
Select [Name], [Subject] + '_Marks1' as [Subject1], [Subject] +'_Marks2' as [Subject2], Marks1, Marks2, Result, [Subject] +'_Result' as [SubjectRes] from #have ) a
pivot (max(marks1) for [Subject1] in ([Maths_Marks1],[Science_Marks1])) p
pivot (max(marks2) for [Subject2] in ([Maths_Marks2],[Science_Marks2])) p1
pivot (max(Result) for [SubjectRes] in ([Maths_Result],[Science_Result])) p2
group by [Name]
Related
I have a query
SELECT a.Name, b.Date FROM
Table1 a
JOIN Table2 b on a.deviceID=b.id
where a.date > '2022-10-01'
Which gives me result
Name Date
A1 '2022-10-01 12:13'
A2 '2022-10-02 14:15'
A2 '2022-10-02 15:16'
A5 '2022-10-03 16:19'
etc.
The result I want to achieve is
A1 A2 A5
'2022-10-01 12:13' '2022-10-02 14:15''2022-10-03 16:19'
'2022-10-02 15:16'
The perfect result would be to receive only one date of each day. Can I do it with pivot?
Pivoting cannot be done (in T-SQL) without aggregation. As for what you want to achieve, seems you need to PIVOT/conditionally aggregate on the value of Name and group on the value of a ROW_NUMBER.
I, personally, prefer using conditional aggregation over the restrictive PIVOT operator, but I have included examples of both:
USE Sandbox;
GO
CREATE TABLE #YourData (Name char(2),
Date datetime2(0));
GO
INSERT INTO #YourData (Name,
Date)
VALUES('A1','2022-10-01T12:13:00'),
('A2','2022-10-02T14:15:00'),
('A2','2022-10-02T15:16:00'),
('A5','2022-10-03T16:19:00');
GO
WITH RNs AS(
SELECT [Name],
[Date],
ROW_NUMBER() OVER (PARTITION BY [Name] ORDER BY [Date]) AS RN
FROM #YourData)
SELECT MAX(CASE [Name] WHEN 'A1' THEN [Date] END) AS A1,
MAX(CASE [Name] WHEN 'A2' THEN [Date] END) AS A2,
MAX(CASE [Name] WHEN 'A5' THEN [Date] END) AS A5
FROM RNs
GROUP BY RN;
GO
SELECT P.A1,
P.A2,
P.A5
FROM (SELECT [Name],
[Date],
ROW_NUMBER() OVER (PARTITION BY [Name] ORDER BY [Date]) AS RN
FROM #YourData)YD
PIVOT(MAX([date])
FOR [Name] IN (A1,A2,A5))P;
GO
DROP TABLE #YourData;
If you want to pivot but keep all your related values in the same field rather than creating fake rows, you can use STRING_AGG. You can replace the "," separator with a CHAR(10), or perhaps an HTML to add a line break. You won't see it in SSMS but depending on your front end, that would render the two items, one under each other.
DROP TABLE IF EXISTS #YourData
CREATE TABLE #YourData (Name char(2),
Date datetime2(0));
GO
INSERT INTO #YourData (Name,
Date)
VALUES('A1','2022-10-01T12:13:00'),
('A2','2022-10-02T14:15:00'),
('A2','2022-10-02T15:16:00'),
('A5','2022-10-03T16:19:00');
GO
DECLARE #Seperator VARCHAR(10) = ' , '
SELECT
[A1] , [A2], [A3], [A4], [A5]
FROM
(
SELECT name, STRING_AGG(date,#Seperator) AS Dates
FROM #YourData
GROUP BY Name
) AS SourceTable
PIVOT
(
MAX(Dates)
FOR Name IN ([A1], [A2], [A3], [A4], [A5])
) AS PivotTable;
Can you please help me with the following.
I have the data with flags as the following and i need to add 5 additional columns based on those flag columns in a waterfall/Cascade way. I tried to accomplish this with case statement but the logic is becoming more confusing.
Here is the sample data and how the end result should look like.
DECLARE #T AS TABLE
(
ID INT,
Mortality VARCHAR(10),
Readmission varchar(10),
EDVisit varchar(10),
Return_to_OR varchar(10),
Sepsis varchar(10)
);
DECLARE #endresult AS TABLE
(
ID INT,
Mortality VARCHAR(10),
Readmission varchar(10),
EDVisit varchar(10),
Return_to_OR varchar(10),
Sepsis varchar(10),
Indicator1 varchar(15),
Indicator2 varchar(15),
Indicator3 varchar(15),
Indicator4 varchar(15),
Indicator5 varchar(15)
);
insert into #T VALUES
(1,'Y', 'N', 'Y','Y','Y'),
(2,'N','Y','N','Y','Y'),
(3,'N','N','N','Y','Y')
insert into #endresult VALUES
(1,'Y', 'N', 'Y','Y','Y','Mortality','EDVisit','Return_to_OR','Sepsis',null),
(2,'N','Y','N','Y','Y','Readmission','Return_to_OR','Sepsis',null,null),
(3,'N','N','N','Y','Y','Return_to_OR','Sepsis',null,null,null)
select * from #T
select * from #endresult
Here's an option which uses a bit of JSON in concert with a conditional aggregation.
On a side note: Assuming you had a typo EDVisit vs ERVisit
Example or dbFiddle
select A.*
,B.*
From #T A
Cross Apply (
Select Indicator1 = max(case when Seq=1 then [Key] end)
,Indicator2 = max(case when Seq=2 then [Key] end)
,Indicator3 = max(case when Seq=3 then [Key] end)
,Indicator4 = max(case when Seq=4 then [Key] end)
,Indicator5 = max(case when Seq=5 then [Key] end)
From (
Select [Key]
,Value
,Seq = row_number() over (order by (select null))
From OpenJson((Select A.* For JSON Path,Without_Array_Wrapper ))
Where [Key] not in ('ID')
and Value<>'N'
) B1
) B
Returns
Hi my table structure is shown as below
enter image description here
and with sql query I want to make it as below structure format
enter image description here
I have to make this with single sql query.
Currently I had made this with excel feature
Can I get any suggestion?
Questionid Response Response
1 HighlyEngaged HighlyEngaged
2 VeryPrepared VeryPrepared
2 VeryPrepared1 VeryPrepared1
to
RowLabels Count of Response
1 1
HighlyEngaged 1
2 2
VeryPrepared 1
VeryPrepared1 1
I have prepared following query, I think it can help you :
DROP TABLE QA
GO
CREATE TABLE QA
(
Questionid INT
,Response VARCHAR(100)
)
INSERT INTO QA
VALUES(1,'HighlyEngaged')
,(2,'VeryPrepared' )
,(5,'Asked' )
,(5,'Priority' )
,(5,'Explained' )
,(8,'Yes' )
,(9,'Set Agenda' )
,(9,'Take Atten' )
,(11,'Assigned')
,(11,'Individual')
,(12,'Predict')
,(12,'Questions')
SELECT
CASE
WHEN Response = '' THEN CAST(QuestionId AS VARCHAR)
ELSE ''
END QId
,Response
,ResponseTotal
FROM (SELECT
QuestionId
,'' Response
,COUNT(Response) ResponseTotal
FROM QA
GROUP BY QuestionId
UNION ALL
SELECT
QuestionId
,Response
,COUNT(1)
FROM QA
GROUP BY QuestionId
,Response) a
ORDER BY QuestionId, CASE
WHEN Response = '' THEN 0
ELSE 1
END
drop table #teee
CREATE TABLE #teee
([Questionid] int, [Response] varchar(13), [Response1] varchar(13))
;
INSERT INTO #teee
([Questionid], [Response], [Response1])
VALUES
(1, 'HighlyEngaged', 'HighlyEngaged'),
(2, 'VeryPrepared', 'VeryPrepared'),
(2, 'VeryPrepared1', 'VeryPrepared1')
;
select res,cnt from (select [Questionid],cast([Questionid]as varchar(100)) res ,count([Response]) cnt from #teee
group by [Questionid]
union all
select [Questionid],cast([Response]as varchar(100)) res,count([Response]) r1 from #teee
group by [Questionid],[Response])a
order by [Questionid],res
the following is an update for the answer given by Yogesh Sharma
select isnull([Response],[Questionid]),total from (select [Questionid], [Response], count(*) total
from #teee t
group by [Questionid], [Response] with rollup) a
where isnull([Response],[Questionid]) is not null
order by [Questionid],1
You can use roll up with aggregation :
select questionid, Response, count(*)
from table t
group by questionid, Response with roll up;
the following shows my sample dataset
PatientID PatientName
XXX-037070002 Riger, Jens^Wicki
XXX-037070002 Riger^Wicki
XXX-10052 Weier,Nicole^Peggy
XXX-10052 Weier,Nicole^Peppy
XXX-23310 Rodem^Sieglinde
XXX-23310 Sauberger, Birgit^Finja
XXX-23343 Je, Ronny^Wilma
XXX-23343 Jer, Ronny^Wilma
XXX-2349 Kel,Andy^Juka
XXX-2349 Kel^Juka
XXX-2998 Hel, Frank
XXX-2998 Hel,Frank^Fenris
XXX-3188 Mey, Marion
XXX-3188 Mey, Marion^Paula
XXX-3188 Schulz^Roma
XXX-3218 Böntgen-Simnet,Dr. Regine^Cara
XXX-3218 Simnet,Dr. Regine^Cara
XXX-3826 Mertes, Bernd Uwe^Ellie
XXX-3826 Mertes,Bernd^Ellie
XXX-3826 Mertes^Ellie
This is the query I got from my last request:
with d as
(
select distinct
patid,
patname
from dicomstudys
)
select *
from d
where d.patid in
(
select d.patid
from d
group by d.patid
having count(*) > 1
)
Now I want to adjust the query that only the following data get's an output:
PatientID PatientName
XXX-23310 Rodem^Sieglinde
XXX-23310 Sauberger, Birgit^Finja
XXX-23343 Je, Ronny^Wilma
XXX-23343 Jer, Ronny^Wilma
XXX-3188 Mey, Marion
XXX-3188 Mey, Marion^Paula
XXX-3188 Schulz^Roma
XXX-3218 Böntgen-Simnet,Dr. Regine^Cara
XXX-3218 Simnet,Dr. Regine^Cara
Last names are either seperated with a ',' or '^' . If last names are the same for the same PatientID then I dont want them being displayed. I tried fiddling with a sub select statement featuring a combination of CHARINDEX commands and others but my SQL syntax knowledge is very limited with the complexity of the request.
Please also note that for the case for XXX-3188 has two datasets with the same last name but also another dataset with a complete new patientName and thus it needs to be in the output.
Try this:
DECLARE #DataSource TABLE
(
[ID] VARCHAR(32)
,[Name] VARCHAR(256)
);
INSERT INTO #DataSource ([ID], [Name])
VALUES ('XXX-037070002', 'Riger, Jens^Wicki')
,('XXX-037070002', 'Riger^Wicki')
,('XXX-10052', 'Weier,Nicole^Peggy')
,('XXX-10052', 'Weier,Nicole^Peppy')
,('XXX-23310', 'Rodem^Sieglinde')
,('XXX-23310', 'Sauberger, Birgit^Finja')
,('XXX-23343', 'Je, Ronny^Wilma')
,('XXX-23343', 'Jer, Ronny^Wilma')
,('XXX-2349', 'Kel,Andy^Juka')
,('XXX-2349', 'Kel^Juka')
,('XXX-2998', 'Hel, Frank')
,('XXX-2998', 'Hel,Frank^Fenris')
,('XXX-3188', 'Mey, Marion')
,('XXX-3188', 'Mey, Marion^Paula')
,('XXX-3188', 'Schulz^Roma')
,('XXX-3218', 'Böntgen-Simnet,Dr. Regine^Cara')
,('XXX-3218', 'Simnet,Dr. Regine^Cara')
,('XXX-3826', 'Mertes, Bernd Uwe^Ellie')
,('XXX-3826', 'Mertes,Bernd^Ellie')
,('XXX-3826', 'Mertes^Ellie');
WITH DataSource AS
(
SELECT [ID]
,[Name]
,COUNT(*) OVER (PARTITION BY [ID], LTRIM(RTRIM(SUBSTRING([Name], 0, CHARINDEX(',', REPLACE([Name], '^', ',')))))) AS [ID_Name_Count]
,COUNT(*) OVER (PARTITION BY [ID]) AS [ID_Count]
,LTRIM(RTRIM(SUBSTRING([Name], 0, CHARINDEX(',', REPLACE([Name], '^', ','))))) AS [FamilyName]
FROM #DataSource
)
SELECT [ID]
,[Name]
FROM DataSource
WHERE [ID_Name_Count] = 1
AND [ID_Count] = 2
OR [ID] IN
(
SELECT [ID]
FROM DataSource
GROUP BY [ID]
HAVING COUNT(DISTINCT [FamilyName]) > 1
);
Тhe solution is pretty easy. Here are the interesting parts:
replace the ^ with , in order to simplify the last name extraction
extract the last name and calculation count based on ID and last name
in the final select check for unique id-last name pairs with id count equal to 2 and add ids with more then one unique family names (your special case)
You can try something like that:
Test data
drop table if exists #Patient;
create table #Patient (
PatientID varchar(20),
PatientName varchar(50)
);
insert into #Patient(PatientID,PatientName)
values ('XXX-037070002' ,'Riger, Jens^Wicki'),
('XXX-037070002' ,'Riger^Wicki'),
('XXX-10052' ,'Weier,Nicole^Peggy'),
('XXX-10052' ,'Weier,Nicole^Peppy'),
('XXX-23310' ,'Rodem^Sieglinde'),
('XXX-23310' ,'Sauberger, Birgit^Finja'),
('XXX-23343' ,'Je, Ronny^Wilma'),
('XXX-23343' ,'Jer, Ronny^Wilma'),
('XXX-2349' ,'Kel,Andy^Juka'),
('XXX-2349' ,'Kel^Juka'),
('XXX-2998' ,'Hel, Frank'),
('XXX-2998' ,'Hel,Frank^Fenris'),
('XXX-3188' ,'Mey, Marion'),
('XXX-3188' ,'Mey, Marion^Paula'),
('XXX-3188' ,'Schulz^Roma'),
('XXX-3218' ,'Böntgen-Simnet,Dr. Regine^Cara'),
('XXX-3218' ,'Simnet,Dr. Regine^Cara'),
('XXX-3826' ,'Mertes, Bernd Uwe^Ellie'),
('XXX-3826' ,'Mertes,Bernd^Ellie'),
('XXX-3826' ,'Mertes^Ellie');
My solution
with q1 as (
select
PatientID,
PatientName,
case when CHARINDEX(',',REPLACE( PatientName, '^',',')) > 0
then LEFT(PatientName,CHARINDEX(',',REPLACE( PatientName, '^',','))-1)
else PatientName end as FullName
from #Patient
) ,
q2 as (
select PatientID
from q1
group by PatientID having COUNT(1) > 1 and COUNT(DISTINCT FullName) > 1 )
select t.PatientID,t.PatientName
from #Patient t join q2 on t.PatientID = q2.PatientID;
Here is table structure, and sample data
create table #tmp ( Id int, Name varchar(100))
insert into #tmp (Id,Name)
Values (1,'Add')
insert into #tmp (Id,Name)
Values (2,'Update')
insert into #tmp (Id,Name)
Values (3,'Delete')
and expected result should be.
Add Update Delete
=== ====== ======
1 2 3
There are several ways that you can transform the data from rows into columns.
If your database has a PIVOT function, then you could use the following code to pivot the data:
select [Add], [Update], [Delete]
from
(
select id, name
from #tmp
) src
pivot
(
max(id)
for name in ([Add], [Update], [Delete])
) piv
See SQL Fiddle with Demo.
Or you could use an aggregate function with a CASE expression:
select
max(case when name = 'Add' then id end) [Add],
max(case when name = 'Update' then id end) [Update],
max(case when name = 'Delete' then id end) [Delete]
from #tmp
See SQL Fiddle with Demo
Please try:
SELECT [Add], [Update], [Delete]
FROM (select * from #tmp) up
PIVOT (sum(id) FOR Name IN ([Add], [Update], [Delete])) AS pvt