Union with Group By - sql

Had a look at other questions, tried different things but still returning more than one row.
Problem with Union on 2 tables, with group by clause. There should only be one row returned, grouped by the serviceID.
SELECT
serviceID,
serviceName,
FullCount,
WaitingCount,
InProgressCount
from (
select
a.serviceID,
serviceName,
count(applicantID) FullCount,
ISNULL(SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END),0) AS WaitingCount,
ISNULL(SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END),0) AS InProgressCount
from Products s
left join Assigns a on a.serviceID = s.productID
WHERE s.clientID = #ClientID
group by serviceID, serviceName
UNION
select
s.serviceID,
p.serviceName,
count(s.ApplicantID) FullCount,
ISNULL(SUM(CASE WHEN s.status = 0 THEN 1 ELSE 0 END),0) AS WaitingCount,
ISNULL(SUM(CASE WHEN s.status = 1 THEN 1 ELSE 0 END),0) AS InProgressCount
from Legacies s
Left Join Products p on s.serviceID = p.productID
WHERE s.client = #CompanyName
group by serviceID, serviceName
) t
GROUP BY serviceID, serviceName
I'm always getting 2 rows returned, one from each of the tables. I need to group them both together so it only returns 1 row, based on the servicedID.
The data I'm trying to return is from the following tables..
Products Table
productID serviceName
-------------------------
1 Gold Service
2 Silver Service
3 Bronze Service
Assigns Table
ApplicantID serviceID status
-------------------------------------
1 1 0
2 1 0
3 1 1
4 2 0
5 1 1
Legacies Table
ApplicantID serviceID status
-------------------------------------
1 1 0
2 1 0
3 1 0
4 2 0
5 1 1
The result I'm trying to get is one row per serviceID, to show how many applicants are on this service in both the Legacies and Assigns table, something like:-
serviceID serviceName FullCount WaitingCount InProgressCount
----------------------------------------------------------------
1 Gold Service 8 5 3
2 Silver Service 2 2 0
3 Bronze Service 0 0 0
FullCount is a total number of applicants on each service, WaitingCount is the number of applicants on the service with a status of '0' and InProgressCount is the number on this service with a status of '1'

Based on additional information, I think you can just union all the Legacies and Assigns tables.
still untested
select serviceID, servicename, count(*) fullcount
,sum(case when status = 0 THEN 1 ELSE 0 END) AS WaitingCount
,SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS InProgressCount
from (
select ApplicantID, serviceID, status
from Assigns
WHERE clientID = #ClientID
union all
select ApplicantID, serviceID, status
from Legacies
WHERE clientID = #ClientID
) combined
left join Products P on P.productID = combined.serviceID
group by serviceID, servicename
below is before edit
It's hard to tell because you do not post enough information (no sample data, no table structures, no expected output). But I think you can probably combine it all into 1 query:
untested which should be obvious with the lack of information.
SELECT isnull(a.serviceID, L.serviceID) serviceID, p.serviceName
,count(*) FullCount, SUM(CASE WHEN isnull(a.status, L.status) = 0 THEN 1 ELSE 0 END) WaitingCount
,sum(CASE WHEN isnull(a.status, L.status) = 1 THEN 1 ELSE 0 END) InProgressCount
from Legacies L
full outer join Assigns a on a.serviceID = L.serviceID
right outer join Products P on P.productID = isnull(a.serviceID, L.serviceID)
where (P.clientID = #ClientID
or L.client = #CompanyName
)
group by isnull(a.serviceID, L.serviceID), p.serviceName

Related

Adding a dummy identifier to data that varies by position and value

I am working on a project in SQL Server with diagnosis codes and a patient can have up to 4 codes but not necessarily more than 1 and a patient cannot repeat a code more than once. However, codes can occur in any order. My goal is to be able to count how many times a Diagnosis code appears in total, as well as how often it appears in a set position.
My data currently resembles the following:
PtKey
Order #
Order Date
Diagnosis1
Diagnosis2
Diagnosis3
Diagnosis 4
345
1527
7/12/20
J44.9
R26.2
NULL
NULL
367
1679
7/12/20
R26.2
H27.2
G47.34
NULL
325
1700
7/12/20
G47.34
NULL
NULL
NULL
327
1710
7/12/20
I26.2
J44.9
G47.34
NULL
I would think the best approach would be to create a dummy column here that would match up the diagnosis by position. For example, Diagnosis 1 with A, and Diagnosis 2 with B, etc.
My current plan is to rollup the diagnosis using an unpivot:
UNPIVOT ( Diag for ColumnALL IN (Diagnosis1, Diagnosis2, Diagnosis3, Diagnosis4)) as unpvt
However, this still doesn’t provide a way to count the diagnoses by position on a sales order.
I want it to look like this:
Diagnosis
Total Count
Diag1 Count
Diag2 Count
Diag3 Count
Diag4 Count
J44.9
2
1
1
0
0
R26.2
1
1
0
0
0
H27.2
1
0
1
0
0
I26.2
1
1
0
0
0
G47.34
3
1
0
2
0
You can unpivot using apply and aggregate:
select v.diagnosis, count(*) as cnt,
sum(case when pos = 1 then 1 else 0 end) as pos_1,
sum(case when pos = 2 then 1 else 0 end) as pos_2,
sum(case when pos = 3 then 1 else 0 end) as pos_3,
sum(case when pos = 4 then 1 else 0 end) as pos_4
from data d cross apply
(values (diagnosis1, 1),
(diagnosis2, 2),
(diagnosis3, 3),
(diagnosis4, 4)
) v(diagnosis, pos)
where diagnosis is not null;
Another way is to use UNPIVOT to transform the columns into groupable entities:
SELECT Diagnosis, [Total Count] = COUNT(*),
[Diag1 Count] = SUM(CASE WHEN DiagGroup = N'Diagnosis1' THEN 1 ELSE 0 END),
[Diag2 Count] = SUM(CASE WHEN DiagGroup = N'Diagnosis2' THEN 1 ELSE 0 END),
[Diag3 Count] = SUM(CASE WHEN DiagGroup = N'Diagnosis3' THEN 1 ELSE 0 END),
[Diag4 Count] = SUM(CASE WHEN DiagGroup = N'Diagnosis4' THEN 1 ELSE 0 END)
FROM
(
SELECT * FROM #x UNPIVOT (Diagnosis FOR DiagGroup IN
([Diagnosis1],[Diagnosis2],[Diagnosis3],[Diagnosis4])) up
) AS x GROUP BY Diagnosis;
Example db<>fiddle
You can also manually unpivot via UNION before doing the conditional aggregation:
SELECT Diagnosis, COUNT(*) As Total Count
, SUM(CASE WHEN Position = 1 THEN 1 ELSE 0 END) As [Diag1 Count]
, SUM(CASE WHEN Position = 2 THEN 1 ELSE 0 END) As [Diag2 Count]
, SUM(CASE WHEN Position = 3 THEN 1 ELSE 0 END) As [Diag3 Count]
, SUM(CASE WHEN Position = 4 THEN 1 ELSE 0 END) As [Diag4 Count]
FROM
(
SELECT PtKey, Diagnosis1 As Diagnosis, 1 As Position
FROM [MyTable]
UNION ALL
SELECT PtKey, Diagnosis2 As Diagnosis, 2 As Position
FROM [MyTable]
WHERE Diagnosis2 IS NOT NULL
UNION ALL
SELECT PtKey, Diagnosis3 As Diagnosis, 3 As Position
FROM [MyTable]
WHERE Diagnosis3 IS NOT NULL
UNION ALL
SELECT PtKey, Diagnosis4 As Diagnosis, 4 As Position
FROM [MyTable]
WHERE Diagnosis4 IS NOT NULL
) d
GROUP BY Diagnosis
Borrowing Aaron's fiddle, to avoid needing to rebuild the schema from scratch, and we get this:
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=d1f7f525e175f0f066dd1749c49cc46d

SQL return only specific rows with specific status

I have this table below with two columns
Order_No Order_Status
A Receiving
A Active
A Retired
A Ordering
B Receiving
B Ordering
C Active
C Retired
D Receiving
E Ordering
I would like to get Order_no B, D and E records because it's order status is ( Receiving/Ordering ). It should filter out A and C because both have Active and Retired status.
I tried the below query but it's not showing up the results.
select ORDER_NUMBER
from table ror
where ror.use_Status
Order_Status not in ('Active', 'Retired')
and Order_Status in ('Receiving', 'Ordering').
Could anyone please tell me what wrong I am doing or I'm missing any joins?
You can use group by and having:
select order_no
from mytable
group by order_no
having max(case when status = 'Receiving' then 1 else 0 end) = 1
and max(case when status = 'Ordering' then 1 else 0 end) = 1
and max(case when status not in ('Receiving', 'Ordering') then 1 else 0 end) = 0
This phrases as: get all orders that have both "Receiving" and "Ordering" statuses, and no other status.
If a given order cannot have the same status twice, then the having can be simplified a little:
having sum(case when status in ('Receiving', 'Ordering') then 1 else 0 end) = 2
and sum(case when status not in ('Receiving', 'Ordering') then 1 else 0 end) = 0
Edit - if you want order that have either "Receiving" and "Ordering" statuses (not necessarily both), then a single condition is sufficient:
having max(case when status not in ('Receiving', 'Ordering') then 1 else 0 end) = 0

SQL Group By with multiple counts

I'm trying to group a list of services together along with the number of applicants in each service, but I also need a count on the status each applicant is in.
Applicants table
serviceID clientID applicantID status
----------------------------------------------------
1 41 1 1 (Processing)
1 41 16 1 (Processing)
1 41 15 2 (Ready)
2 41 12 1 (Processing)
2 41 18 3 (Complete)
Service table:
serviceID serviceName
--------------------------
1 Full Service
2 Part Service
Results need to look like:
serviceName totalApplicants processingCount readyCount completeCount
---------------------------------------------------------------------------
Full Service 3 2 1 0
Part Service 2 1 0 1
I've got the following, but it's returning the same count in each of the columns:-
SELECT
Services.serviceName,
(COUNT(Applicants.applicantID)) AS totalApplicants,
ISNULL(SUM(CASE WHEN Applicants.status = 1 THEN 1 ELSE 0 END), 0) AS processingCount,
ISNULL(SUM(CASE WHEN Applicants.status = 2 THEN 1 ELSE 0 END), 0) AS readyCount,
ISNULL(SUM(CASE WHEN Applicants.status = 3 THEN 1 ELSE 0 END), 0) AS completeCount
FROM
Applicants
LEFT JOIN
Services ON Applicants.serviceID = Services.serviceID
WHERE
Applicants.clientID = #CompanyID
GROUP BY
Services.serviceName
You can do conditional aggregation:
select
s.serviceName,
count(s.serviceID) totalApplicants,
sum(case when status = 1 then 1 else 0 end) processingCount,
sum(case when status = 2 then 1 else 0 end) readyCount,
sum(case when status = 3 then 1 else 0 end) completeCount
from service s
left join applicants a on a.serviceID = s.serviceID AND a.clientID = #CompanyID
group by s.serviceID, s.serviceName
The conditional expression use standard case expression; depending on the database that you are actually using, neater alternatives may exists.
Your query should be fine, but it can be simplified to:
SELECT s.serviceName,
COUNT(a.AppicantId) AS totalApplicants,
SUM(CASE WHEN a.status = 1 THEN 1 ELSE 0 END) AS processingCount,
SUM(CASE WHEN a.status = 2 THEN 1 ELSE 0 END) AS readyCount,
SUM(CASE WHEN a.status = 3 THEN 1 ELSE 0 END) AS completeCount
FROM Services s LEFT JOIN
Applicants a
ON a.serviceID = s.serviceID AND
a.clientID = #CompanyID
GROUP BY s.serviceName ;
Notes:
It looks like you want a row for every service, so that should be the first table in the LEFT JOIN.
Hence the filtering on the company goes into the ON clause.
Table aliases make the query easier to write and to read.
No ISNULL() is needed (and I prefer COALESCE() over ISNULL()).

Count Between three tables

I have three tables
Data
Service
Status
Data table
Serno | ServiceId | Status | Datetime
1 2 4 12/12/2014
2 1 3 08/12/2014
Service
ServiceId | Service Name
1 Deployment
2 Designing
Status
StatusId | Status
1 Done
2 Pending
3 20%done
4 Canceled
I want a Sql code for Count (that is the status count with respect to Services)
Designing 0 0 0 1
Deployment 0 0 1 0
I have tried this
SELECT COUNT(Service.Status) AS Expr1, Service.ServiceName, Status.Status
FROM Data INNER JOIN
Service ON Hcc_Service_Data.Hcc_Service_Category_Id = Service .ServiceId INNER JOIN
Status ON Data.StatusId = Status.Status_Serno
GROUP BY Service.ServiceName, Status.Status
Select S.ServiceName,
(CASE WHEN D.Status=1 then '1' ELSE '0' END) as S1,
(CASE WHEN D.Status=2 then '1' ELSE '0' END) as S2,
(CASE WHEN D.Status=3 then '1' ELSE '0' END) as S3,
(CASE WHEN D.Status=4 then '1' ELSE '0' END) as S4
FROM Data D, Service S where
D.serviceId = S.serviceId;

Full Join on two queries

I'm trying to do a full join on two SQL queries, below:
1st Query:
SELECT
ID
,SUM(CASE WHEN reason = 4 THEN 0 ELSE quantity*price END) AS TValue
,COUNT(*) AS CountAll
FROM table1
WHERE Date>=#StartDate AND Date<=#EndDate
GROUP BY ID
2nd Query:
SELECT
ID
,SUM(CASE WHEN reason = 1 THEN 1 ELSE 0 END) AS New
,SUM(CASE WHEN reason = 6 THEN 1 ELSE 0 END) AS Amend
,SUM(CASE WHEN reason = 5 THEN 1 ELSE 0 END) AS Cancel
FROM Table2
WHERE Date2 >=#StartDate AND Date2<= #EndDate
GROUP BY ID
Result from query1
ID CountAll TValue
-------------------------
id1 24 1020
id2 13 2030
id3 4 120
Result from query 2:
ID New Amend Cancel
--------------------------------
id1 12 4 6
id2 7 6 1
id4 2 1 2
Needed output:
ID TValue CountAll New Amend Cancel Total(countall+new+amend+cancel)
----------------------------------------------------------------------------------------
Id1 1020 24 12 4 6 46
Id2 2030 13 7 6 1 27
id3 120 4 0 0 0 4
Id4 0 0 2 1 2 5
I'll post my current solution if requested, but it is pretty far from working.
I've been doing a bit of research and I think I need to either make a union to join the ID'S, or just do a Full Join. (Second day ever doing sql)
Try this,
SELECT *
FROM
(
SELECT ID ,
SUM(CASE WHEN reason = 4 THEN 0 ELSE quantity*price END) AS TValue,
COUNT(*) AS CountAll
FROM table1
WHERE Date>=#StartDate AND Date<=#EndDate
GROUP BY ID
) a FULL JOIN
(
SELECT ID ,
SUM(CASE WHEN reason = 1 THEN 1 ELSE 0 END) AS New ,
SUM(CASE WHEN reason = 6 THEN 1 ELSE 0 END) AS Amend ,
SUM(CASE WHEN reason = 5 THEN 1 ELSE 0 END) AS Cancel
FROM Table2
WHERE Date2 >=#StartDate AND Date2<= #EndDate
GROUP BY ID
) b ON a.ID = b.ID
I would write something like below:
select decode (a.id, null, b.id, a.id) as ID, a.TValue, CountAll, b.new, b.Amend, b.cancel
from (SELECT ID ,SUM(CASE WHEN reason = 4 THEN 0 ELSE quantity*price END)
AS TValue ,COUNT(*) AS CountAll
FROM table1
WHERE Date>=#StartDate AND Date<=#EndDate GROUP BY ID
) a FULL OUTER JOIN
(SELECT ID , SUM(CASE WHEN reason = 1 THEN 1 ELSE 0 END)
AS New ,SUM(CASE WHEN reason = 6
THEN 1 ELSE 0 END) AS Amend ,
SUM(CASE WHEN reason = 5 THEN 1 ELSE 0 END) AS Cancel
FROM Table2 WHERE Date2 >=#StartDate AND Date2<= #EndDate GROUP BY ID
) b
on a.id = b.id
have you tried this...
select isnull (a.id,b.id) as ID, a.TValue, CountAll, b.new, b.Amend, b.cancel
from (SELECT ID ,SUM(CASE WHEN reason = 4 THEN 0 ELSE quantity*price END) AS TValue ,COUNT(*) AS CountAll
FROM table1
WHERE Date>=#StartDate AND Date<=#EndDate GROUP BY ID ) a
FULL OUTER JOIN (SELECT ID , SUM(CASE WHEN reason = 1 THEN 1 ELSE 0 END) AS New ,SUM(CASE WHEN reason = 6 THEN 1 ELSE 0 END) AS Amend , SUM(CASE WHEN reason = 5 THEN 1 ELSE 0 END) AS Cancel
FROM Table2 WHERE Date2 >=#StartDate AND Date2<= #EndDate GROUP BY ID ) b on a.id = b.id