Pivot Multiple columns together in SQL - sql

I've table like this
I need to get a result like this
RT Team PI Committed Done-Partial Done-Full
----------------------------------------------------------------
ART1 Team1 10 5 1 3
ART2 Team2 7 5 4 1
-----------------------------------------------------------------------
The way I tried is as follows
;with RecentPI as(
select * from (
select rt,Team,pi,[Finish Date],DENSE_RANK() over( partition by rt order by [Finish Date] desc) PIRank from Schedule_Manual S inner join TFS_ARTs_Teams T on T.ART=S.RT
group by RT,Team,PI,[Finish Date]
)tbl
where PIRank=1
)
select * from (select Obj.RT,Obj.[TFS Team], Obj.Type,Obj.PI,[PI Status] from Objectives Obj inner join RecentPI RP on RP.RT=Obj.RT and RP.Team=Obj.[TFS Team] and RP.PI=Obj.PI) as query
PIVOT (count(type) for [Type] in ([Committed])) p1
PIVOT (Count([PI Status]) for [pi status] in ([Done-Partial],[Done-Full])) p2
But it doesnt seems to be correct and also Im not getting full columns in the query. Sorry Im very beginner with SQL Pivot

You can Use this simple Query...your problem will be solved....
select RT,Team,[PI],
sum(case when [Type] = 'Committed' then 1 else 0 end) AS 'Committed',
sum(case when PIStatus = 'Done-Partial' then 1 else 0 end) AS 'Done-Partial',
sum(case when PIStatus = 'Done-Full' then 1 else 0 end) AS 'Done-Full'
from tbl_Pivot
group by RT,Team,[PI]
Output:-

Related

How to do this query using self join or anything but without using window function

Below is the solution but I want to know other ways to accomplish the same results (preferably in PostgreSQL).
This is the DB
Question - How many customers have churned straight after their initial free trial? what percentage is this rounded to the
nearest whole number?
WITH ranking AS (
SELECT
s.customer_id,
s.plan_id,
p.plan_name,
ROW_NUMBER() OVER (
PARTITION BY s.customer_id
ORDER BY s.plan_id) AS plan_rank
FROM dbo.subscriptions s
JOIN dbo.plans p
ON s.plan_id = p.plan_id)
SELECT
COUNT(*) AS churn_count,
ROUND(100 * COUNT(*) / (
SELECT COUNT(DISTINCT customer_id)
FROM dbo.subscriptions),0) AS churn_percentage
FROM ranking
WHERE plan_id = 4 -- Filter to churn plan
AND plan_rank = 2
You can achieve the same results with a single aggregation on customer_id with a few CASE WHEN statements:
SELECT count(*) as total_customers
,count(case when total_subscriptions = 2
and includes_free = 1
and includes_churn = 1 then 1 end) as churn_count
,100 * count(case when total_subscriptions = 2
and includes_free = 1
and includes_churn = 1 then 1 end) / count(*) as target_percent
FROM (
SELECT customer_id
,count(*) as total_subscriptions
,max(case when plan_id = 0 then 1 else 0 end) as includes_free
,max(case when plan_id = 4 then 1 else 0 end) as includes_churn
FROM dbo.subscriptions
GROUP BY customer_id
) AS tbl
-- Remove any records for people who didnt use the free trial
-- or people who are still on the free trial
WHERE includes_free = 1 AND total_subscriptions > 1
The difference between our solutions are:
Yours doesn't specify that the customer actually had a free trial
Mine doesn't include customers who went from Free -> Churn -> (something else)
Depending on your requirements you might want to make further alterations/use a different approach.

Selecting multiple rows of a column in a SQL Server query

I have a table, let's call it Case the case table contains basic information about a case, such as the CaseNumber and the CaseOwner.
In a separate table, CaseDetails a number of phone numbers associated with a particular case are stored. The Type column within CaseDetails represents the type of phone number e.g. Home, mobile or work. These two tables link on CaseNumber.
More clearly:
Case
CaseNumber CaseOwner
------------------------
1 Bob
2 Jim
3 Gary
CaseDetails
CaseNumber Detail Type
----------------------------------
1 0123456789 1
1 1111111111 2
2 2222222222 1
1 0101001011 3
2 1234123412 2
3 0000011111 1
3 1231231231 2
I want to be able to write a query that can pull back the basic details of a case, as well as ALL of the associated phone numbers.
In my head I imagined the query to go something like the following
Select
CaseNumber, CaseOwner,
Detail where Type = 1, Detail where Type = 2, Detail where Type = 3
From
Case
Join
CaseDetails on Case.CaseNumber = CaseDetails.CaseNumber
That way each individual Detail could be extracted from the CaseDetails table using the type column. However this is syntactically incorrect and won't execute.
How exactly would I construct a query to extract this information? I can't seem to find the information on this on Google as I'm not sure what to search for.
The whole point of this is so that I can find all of the associated numbers for a particular case and store them in one location.
This is what I want the final output to look like
CaseNumber CaseOwner Detail1 Detail2 Detail3
-------------------------------------------------------------------
1 Bob 0123456789 1111111111 0000011111
You can try below using CASE WHEN expression
Select a.CaseNumber, CaseOwner, max(case when Type = 1 then detail end) as as detail1, max(case when Type = 2 then detail end) as detail2, max(case when Type = 3 then detail end) as detail3
From Case a
Join CaseDetails b on a.CaseNumber = b.CaseNumber
group by a.CaseNumber, CaseOwner
OR you can use PIVOT
with cte as
(
Select a.CaseNumber, CaseOwner, type, detail
From Case a
Join CaseDetails b on a.CaseNumber = b.CaseNumber
group by a.CaseNumber, CaseOwner
)
select casenumber, caseowner,pv.*
from cte pivot(max(detail) for type in (1,2,3)) as pv
You can use conditional aggregation:
Select c.CaseNumber, c.CaseOwner,
max(case when cd.type = 1 then cd.Detail end) as detail_1,
max(case when cd.type = 2 then cd.Detail end) as detail_2,
max(case when cd.type = 3 then cd.Detail end) as detail_3
From Case c Join
CaseDetails cd
on c.CaseNumber = cd.CaseNumber
group by c.CaseNumber, c.CaseOwner;
EDIT:
You can also do this using outer apply:
select c.*, cd.*
from case c outer apply
(select max(case when cd.type = 1 then cd.Detail end) as detail_1,
max(case when cd.type = 2 then cd.Detail end) as detail_2,
max(case when cd.type = 3 then cd.Detail end) as detail_3
from CaseDetails cd
where c.CaseNumber = cd.CaseNumber
) cd;
use pivot
select CaseNumber,CaseOwner,
[1] as detail1,
[2] as detail2 ,
[3] as detail3
from
(select c1.CaseNumber,c1.CaseOwner,c2.Detail,c2.Type
From Case c1
Join CaseDetails c2
on c1.CaseNumber = c2.CaseNumber
) src
PIVOT
(
max(Detail) for Type in ([1],[2],[3])
) pvt

Display SQL Data From Row to Column

My SQL statement is list as below:
SELECT Count(DISTINCT A.LM_PERSON_ID) AS HEAD_COUNT
,A.LM_STATUS
,To_Char(A.LM_STATUS_CHANGE_DT,'YYYY') AS YEAR
,B.LM_COURSE_NAME AS COURSE_NAME
FROM LM_ENRLOLMENT A
,LM_COURSE_TBL B
WHERE A.LM_STATUS='COMP'
AND A.LM_COURSE_ID=B.LM_CI_ID
GROUP BY A.LM_STATUS_CHANGE_DT,LM_STATUS,B.LM_COURSE_NAME
The example output I would like to display was:
COURSE_NAME 2010 2011 2012
A 4 5 1
B 2 1 1
C 6 0 3
D 1 1 2
But the main problem I am facing is that LM_STATUS_CHANGE_DT is dynamic data. Is there anyone that can show me how I could do this?
You did not specify what RDBMS you are using but you should be able to use the following in all versions:
SELECT
B.LM_COURSE_NAME AS COURSE_NAME,
count(DISTINCT case when To_Char(A.LM_STATUS_CHANGE_DT,'YYYY') = '2010' then A.LM_PERSON_ID end) as Year2010,
count(DISTINCT case when To_Char(A.LM_STATUS_CHANGE_DT,'YYYY') = '2011' then A.LM_PERSON_ID end) as Year2011,
count(DISTINCT case when To_Char(A.LM_STATUS_CHANGE_DT,'YYYY') = '2012' then A.LM_PERSON_ID end) as Year2012
FROM LM_ENRLOLMENT A
INNER JOIN LM_COURSE_TBL B
ON A.LM_COURSE_ID=B.LM_CI_ID
WHERE A.LM_STATUS='COMP'
GROUP BY B.LM_COURSE_NAME
If you are using an RDBMS that has the PIVOT function (SQL Server 2005+/Oracle 11g+) then your code will be similar to this:
SELECT *
FROM
(
SELECT DISTINCT B.LM_COURSE_NAME,
To_Char(A.LM_STATUS_CHANGE_DT,'YYYY') As Year,
A.LM_PERSON_ID
FROM LM_ENRLOLMENT A
INNER JOIN LM_COURSE_TBL B
ON A.LM_COURSE_ID=B.LM_CI_ID
WHERE A.LM_STATUS='COMP'
) src
PIVOT
(
count(LM_PERSON_ID)
for Year in ('2010', '2011', '2012')
) piv

Count records in same column with and without parameter

I am having some trouble with a query and was hoping someone could help. I have tried searching for a solution, but can't seem to find a similar scenario with my search terms.
Here is what I am looking for:
I have a table with three columns and need to return the values in the first column, and two counts of the values in the second column. One count is based on a parameter and the other is the total count. Ideally, I would only like to return the values in column one that have equal counts.
For example:
Part Number | Make ID
ABC123 | 1<br>
ABC123 | 1<br>
ABC123 | 3<br>
DEF456 | 1<br>
DEF456 | 1
Part Number | Count of Apps Where Make ID = 1| Count of Total Apps
ABC123 | 2 | 3
DEF456 | 2 | 2
The query I have so far will return the total count of apps for part numbers that have the parameter value, but I need it to return both counts:
SELECT apps.part#,
COUNT(DISTINCT application#)apps
FROM [mytable] AS apps
INNER JOIN (SELECT part#
FROM [mytable]
WHERE make = '1') AS sz
ON sz.part# = apps.part#
GROUP BY apps.part#
ORDER BY 1
Any help is greatly appreciated!
Thanks, everyone! I received several correct answers, and selected the one that was give first:
SELECT part#,
COUNT(DISTINCT CASE WHEN make = '1' THEN application# END) make1_apps,
COUNT(DISTINCT application#) total_apps
FROM [mytable]
GROUP BY part#
HAVING COUNT(DISTINCT CASE WHEN make = '1' THEN application# END)
= COUNT(DISTINCT application#)
ORDER BY part#
Thanks again!
Try this:
SELECT
apps.part#
, COUNT(*)
, SUM(CASE WHEN make = '1' THEN 1 ELSE 0 END)
FROM [mytable]
GROUP BY apps.part#
This solution replaces counting with calculating a sum, and supplying 1 for matching items and 0 for non-matching ones.
To filter out the records where the two counts are different, add a HAVING clause:
SELECT
apps.part#
, COUNT(*)
, SUM(CASE WHEN make = '1' THEN 1 ELSE 0 END)
FROM [mytable]
GROUP BY apps.part#
HAVING SUM(CASE WHEN make = '1' THEN 1 ELSE 0 END) = COUNT(*)
SELECT [Part Number],
SUM(CASE WHEN [Make ID] = 1 THEN 1 ELSE 0 END),
COUNT(*)
FROM tableName
GROUP BY [Part Number]
SQLFiddle Demo
UPDATE 1
SELECT [Part Number],
SUM(CASE WHEN [Make ID] = 1 THEN 1 ELSE 0 END),
COUNT(*)
FROM tableName
GROUP BY [Part Number]
HAVING SUM(CASE WHEN [Make ID] = 1 THEN 1 ELSE 0 END) = COUNT(*)
SQLFiddle Demo
another way more similar to yours:
select d.[Part Number], count(d.[Part Number]), o.ones
from
data d
cross apply
(
select count(*)
from data d2
where d2.[Part Number] = d.[Part Number]
and d2.[Make ID] = 1
) o (ones)
group by d.[Part Number], o.ones
SQLFiddle
to fullfill your second request add:
having count(d.[Part Number]) = o.ones
SELECT part#,
COUNT(DISTINCT CASE WHEN make = '1' THEN application# END) make1_apps,
COUNT(DISTINCT application#) total_apps
FROM [mytable]
GROUP BY part#
HAVING COUNT(DISTINCT CASE WHEN make = '1' THEN application# END)
= COUNT(DISTINCT application#)
ORDER BY part#;
The HAVING clause ensures the two counts match.

Subselect Query Improvement

How can I improve the SQL query below (SQL Server 2008)? I want to try to avoid sub-selects, and I'm using a couple of them to produce results like this
StateId TotalCount SFRCount OtherCount
---------------------------------------------------------
AZ 102 50 52
CA 2931 2750 181
etc...
SELECT
StateId,
COUNT(*) AS TotalCount,
(SELECT COUNT(*) AS Expr1 FROM Property AS P2
WHERE (PropertyTypeId = 1) AND (StateId = P.StateId)) AS SFRCount,
(SELECT COUNT(*) AS Expr1 FROM Property AS P3
WHERE (PropertyTypeId <> 1) AND (StateId = P.StateId)) AS OtherCount
FROM Property AS P
GROUP BY StateId
HAVING (COUNT(*) > 99)
ORDER BY StateId
This may work the same, hard to test without data
SELECT
StateId,
COUNT(*) AS TotalCount,
SUM(CASE WHEN PropertyTypeId = 1 THEN 1 ELSE 0 END) as SFRCount,
SUM(CASE WHEN PropertyTypeId <> 1 THEN 1 ELSE 0 END) as OtherCount
FROM Property AS P
GROUP BY StateId
HAVING (COUNT(*) > 99)
ORDER BY StateId
Your alternative is a single self-join of Property using your WHERE conditions as a join parameter. The OtherCount can be derived by subtracting the TotalCount - SFRCount in a derived query.
Another alternative would be to use the PIVOT function like this:
SELECT StateID, [1] + [2] AS TotalCount, [1] AS SFRCount, [2] AS OtherCount
FROM Property
PIVOT ( COUNT(PropertyTypeID)
FOR PropertyTypeID IN ([1],[2])
) AS pvt
WHERE [1] + [2] > 99
You would need to add an entry for each property type which could be daunting but it is another alternative. Scott has a great answer.
If PropertyTypeId is not null then you could do this with a single join. Count is faster than Sum. But is Count plus Join faster than Sum. The test case below mimics your data. docSVsys has 800,000 rows and there are about 300 unique values for caseID. The Count plus Join in this test case is slightly faster than the Sum. But if I remove the with (nolock) then Sum is about 1/4 faster. You would need to test with your data.
select GETDATE()
go;
select caseID, COUNT(*) as Ttl,
SUM(CASE WHEN mimeType = 'message/rfc822' THEN 1 ELSE 0 END) as SFRCount,
SUM(CASE WHEN mimeType <> 'message/rfc822' THEN 1 ELSE 0 END) as OtherCount,
COUNT(*) - SUM(CASE WHEN mimeType = 'message/rfc822' THEN 1 ELSE 0 END) as OtherCount2
from docSVsys with (nolock)
group by caseID
having COUNT(*) > 1000
select GETDATE()
go;
select docSVsys.caseID, COUNT(*) as Ttl
, COUNT(primaryCount.sID) as priCount
, COUNT(*) - COUNT(primaryCount.sID) as otherCount
from docSVsys with (nolock)
left outer join docSVsys as primaryCount with (nolock)
on primaryCount.sID = docSVsys.sID
and primaryCount.mimeType = 'message/rfc822'
group by docSVsys.caseID
having COUNT(*) > 1000
select GETDATE()
go;