how to show zero when IN does not return any value sql - sql

I want to return 0 when no rows found for this following condition.
SELECT P.externalid,
Count(DISTINCT Pp.patientuid) patcount
FROM PatientProcedure Pp (nolock )
INNER JOIN visit Temp (nolock )
ON Temp.patientuid = Pp.patientuid
AND (Pp.effectivedate = Temp.VisitDate)
AND Temp.RenderringProviderUid = Pp.serviceprovideruid
AND Temp.VisitDate >= '07-01-2013'
AND Temp.VisitDate <= '06-30-2014'
INNER JOIN serviceprovider Sp (nolock )
ON Pp.serviceprovideruid = Sp.serviceprovideruid
AND Sp.inactive = 0
INNER JOIN MasterCode Mc (nolock )
ON Mc.codeuid = Pp.procedurecodeuid
INNER JOIN MasterCodeSet Mcs (nolock )
ON Mcs.Value = Mc.code
INNER JOIN practice P
ON P.PracticeUid = Temp.practiceuid
WHERE NAME = 'Visual_Field_Exam'
AND Temp.mastervisittypeuid IS NOT NULL
AND P.ExternalID IN ('26900', '26902', '26903', '26906',
'26907', '26908', '26946', '26963',
'27128', '27131', '27133', '27134',
'27135', '27137', '27166', '27167',
'27497', '27498', '27499', '27501',
'27502', '27504', '27505', '27509',
'27510', '27511', '27518')
GROUP BY P.ExternalID
I have found patient count for many practices but no patient found for one practice. So, I want to return count(patientuid) = 0 for that practice.
Here is the sample data.
PracticeId Patcount
26900 2583
26902 826
26903 4085
26906 241
26907 3205
26908 4592
26946 344
26963 398
27128 238
27131 2467
27133 975
27135 815
27137 1252
27166 1038
27167 211
27497 1053
27498 934
27499 3467
27501 617
27502 3511
27504 7222
27505 683
27509 210
27510 1145
27511 181
27518 500
In above output I got data for 26 practices, but "27134" practice does not having patient count, so i want it show 0.
I want above output. How can I achieve it?
Thanks in advance..!

Suppose you have a table Practicer which holds the practicers (i do not know if that is the correct word, but doesn't matter for SQL). Then you need to left join that table with Patient in order to get a list of all Practicers filter by you where clause. So this will get the ones you want.
Select PR.PracticeName,
count(distinct patientid)
from Practicer PR
LEFT JOIN Patient P
ON PR.Practiceid = P.Practiceid
where PR.Practiceid in
('12345','65478','78541')
GROUP BY PR.PracticeName
In general, you want to return 2 fields. The first is the list of Hospitals, Practiotioners or whatever else you may like. In order to get the full list (even if it does not have a count) you need to start with that table.
Lets say we have a table Hospitals that we want to filter on specific ids. The we would have the following query:
SELECT HospitalId,
HospitalName
FROM Hospitals
WHERE HospitalId IN ('26900', '26902', '26903')
Now we would like to know the no of patients in each hospital. We know that hospital with id '26902' has no patients (WOW). If we would go to the Patients table and tried the following:
SELECT HospitalId,
COUNT(*) AS NoOfPatients
FROM Patients
WHERE HospitalId IN ('26900', '26902', '26903')
GROUP BY HospitalId
It would return only ids '26900' and '26903' as the Hospital with id '26902' does not have any patient.
So, how do we figure out the third Hospital?
We join the two tables:
SELECT H.HospitalId,
COUNT(PatienId) AS NoOfPatients
FROM Hospitals H
LEFT JOIN Patients P
WHERE H.HospitalId IN ('26900', '26902', '26903')
GROUP BY H.HospitalId
Now this would return all the requested hospitals with the according number of patients.
Your case is more complex, as we need more joins from the Hospitals to the Patients tables. But i hope my explanation will get you on the right track.
The essence of it is to start from your List Table to your Count table.

Related

SQL Count with join are returning double results

I have two tables, "event" and "soundType". I am trying to count the number of event with specific soundType.
This is my request :
SELECT Count(*) AS nb
FROM event
INNER JOIN soundtype
ON event.id = soundtype.eventid
WHERE ( soundtype.NAME = 'pop'
OR soundtype.NAME = 'rock' )
AND ( event.partytype = 'wedding'
OR event.partytype = 'Corporate evening'
OR event.partytype = 'birthday' )
Example of tables below:
event Table
id userId partyType
----------------------------
249 30 birthday
250 30 wedding
SoundType Table
id evenId name
-----------------------
1 249 pop
2 249 rock
3 250 pop
The result
nb
---
3
The result i expect
nb
---
2
Thank you for your help
You might find that exists is more efficient than count(distinct):
SELECT COUNT(*) AS nb
FROM event e
WHERE e.partytype IN ('wedding', 'Corporate evening' , 'birthday') AND
EXISTS (SELECT 1
FROM soundtype st
WHERE st.eventid = e.id AND
st.NAME IN ('pop', 'rock')
) ;
Your problem is (presumably) arising because some events have multiple sound types. You just need to match one of them. Multiplying out all the rows just to use COUNT(DISTINCT) is inefficient, when EXISTS (or IN) prevents the duplicates in the first place.
You count all the resulting records. But you need to count different events. So use distinct
SELECT COUNT(distinct event.id) AS nb
FROM event
INNER JOIN soundType ON event.id = soundType.eventId
WHERE soundType.name in('pop', 'rock')
AND event.partyType in('wedding', 'Corporate evening', 'birthday')

SQL - Return 0 value for a grouping

I have 3 tables, Member_School, which holds links between members and schools, Schools which contains school information, and Category which is a simply lookup table for full category names (categories of members).
Im trying to get a count of all member categories for each school, but can't get a 0 count for the categories which have no members in for particular schools. Here's my query so far:
WITH MemberNums AS
(
SELECT School_No, Category, COUNT(Member_No) AS NumberOfMembs
FROM Member_School
WHERE Finish_Date > GETDATE()
AND Category IS NOT NULL
GROUP BY School_No, Category
)
SELECT School.School_No, Category.Category, MemberNums.NumberOfMembs AS NumberOfMembs, School.School_Name, School.ATH_No, School.AWA_No, School.CLE_No, School.CYC_No, School.CYF_No, School.OBS_No, School.PEN_No, School.RHI_No,
School.STA_No, School.SYL_No
FROM School
INNER JOIN MemberNums
ON School.School_No = MemberNums.School_No
LEFT JOIN Category
ON Category.Category_No = MemberNums.Category
ORDER BY School_No
So for example this query might return 8 rows for the school with ID 123, but there are 10 categories in the Category table. What I'm trying to get would be 10 rows per school, with a count of how many members in each category (including zero counts for schools which have no records for a certain category in the Member_School table)
Here is an example row from each table:
Category:
Category_No: Cat1
Category: Category1
School:
School_No: 123
School_Name: Highschool
Memb_No: 10
Secondary: True
Job_No: XJI314
ATH_No: 1
AWA_No: 1
CLE_No: 1
CYC_No: 1
CYF_No: 1
OBS_No: 1
PEN_No: 1
RHI_No: 1
STA_No: 1
SYL_No: 1
Member_School:
Rec_No: 1
Member_No: 2
School_No: 123
Start_Date: 2018-03-19 00:00:00.000
End_Date: 2020-03-19 00:00:00.000
Category: Cat1
If you want zeros, then use a cross join to generate the rows then bring in the existing data:
select s.School_No, c.Category, count(ms.School_No) as NumberOfMembs as NumberOfMembs,
s.School_Name, s.ATH_No, s.AWA_No, School.CLE_No,
s.CYC_No, s.CYF_No, s.OBS_No, s.PEN_No, s.RHI_No,
s.STA_No, s.SYL_No
from school s cross join
categories c left join
Member_School ms
on ms.School_No = s.School_No and
ms.Category = c.Category_No and
ms.Finish_Date > getdate()
group by s.School_No, c.Category,
s.School_Name, s.ATH_No, s.AWA_No, School.CLE_No,
s.CYC_No, s.CYF_No, s.OBS_No, s.PEN_No, s.RHI_No,
s.STA_No, s.SYL_No;
use left join
WITH MemberNums AS
(
SELECT School_No, Category, COUNT(Member_No) AS NumberOfMembs
FROM Member_School
WHERE Finish_Date > GETDATE()
AND Category IS NOT NULL
GROUP BY School_No, Category
)
SELECT School.School_No, Category.Category, MemberNums.NumberOfMembs AS NumberOfMembs, School.School_Name, School.ATH_No, School.AWA_No, School.CLE_No, School.CYC_No, School.CYF_No, School.OBS_No, School.PEN_No, School.RHI_No,
School.STA_No, School.SYL_No
FROM School
left JOIN MemberNums
ON School.School_No = MemberNums.School_No
LEFT JOIN Category
ON Category.Category_No = MemberNums.Category
ORDER BY School_No
Have you tried using a right outer join? Use the right outer join and coalesce function to ensure 0 shows up instead of null.
I've posted a sample query below
WITH MemberNums AS
(
SELECT School_No, Category, COUNT(Member_No) AS NumberOfMembs
FROM Member_School
WHERE Finish_Date > GETDATE()
AND Category IS NOT NULL
GROUP BY School_No, Category
)
SELECT School.School_No, Category.Category, COALESCE(MemberNums.NumberOfMembs, 0) AS NumberOfMembs, School.School_Name, School.ATH_No, School.AWA_No, School.CLE_No, School.CYC_No, School.CYF_No, School.OBS_No, School.PEN_No, School.RHI_No,
School.STA_No, School.SYL_No
FROM School
INNER JOIN MemberNums
ON School.School_No = MemberNums.School_No
RIGHT OUTER JOIN Category
ON Category.Category_No = MemberNums.Category
ORDER BY School_No
If you want all results from school and member_school that may not have a category, then you might want to consider FULL OUTER JOIN instead

Merge multiple rows in data to show only a single row in the result

I have a stored procedure which takes 1 parameter, an ID number (systudentid).
The procedure returns 3 rows: a student’s academic counselor (AC), financial counselor (FC), and admissions counselor (EC) along with relevant contact information; 3 different people.
Certain students have ACs and FCs who are the same person, but the query will still return 3 rows.
AdvisorType|AdvisorLastName|AdvisorFirstName|(other data)|systaffID
AC DOE JOHN ..... 12345
AC DOE JOHN ..... 12345
EC SMITH JANE ..... 45678
Where in my code can I plug in the logic (and how, I'm a newbie with sql) so that when the systudentid passed to the procedure identifies a student having the same person for both AC and FC, it will display the results this way.
The advisor type is changed to "SSA" and only one of the records for the double-duty counselor is returned.
AdvisorType|AdvisorLastName|AdvisorFirstName|(other data)|SystaffID
SSA DOE JOHN ...... 12345
EC SMITH JANE ...... 45678
Here is my select statement:
SELECT
SyStaffGroup.Descrip AS AdvisorType
,SyStaff.LastName AS AdvisorLastName
,SyStaff.FirstName AS AdvisorFirstName
,SyStaff.Phone AS AdvisorPhone
,SyStaff.Ext AS AdvisorExtention
,SyStaff.eMail AS AdvisorEMail
,SyStaff.SyStaffID AS SyStaffID
FROM SyStaff (NOLOCK)
JOIN SyAdvisorByEnroll (NOLOCK)
ON SyAdvisorByEnroll.SyStaffID = SyStaff.SyStaffID
JOIN SyStaffGroup (NOLOCK)
ON SyStaffGroup.SyStaffGroupID = SyAdvisorByEnroll.SyStaffGroupID
JOIN AdEnroll (NOLOCK)
ON AdEnroll.AdEnrollID = SyAdvisorByEnroll.AdEnrollID
JOIN SyStudent (NOLOCK)
ON AdEnroll.SyStudentID = SyStudent.SyStudentId
WHERE
SyStaff.Active = 1
--AND
--syadvisorbyenroll.adenrollid = (
--SELECT adenrollid from dbo.fn_student_enrollment_activeenrollmentlist (#systudentid)
--)
AND adEnroll.adEnrollID IN (
SELECT adEnrollID FROM dbo.fn_Student_Enrollment_ActiveEnrollmentList(#SyStudentID)
)
AND SyAdvisorByEnroll.AdvisorModule IN ('AD','FA')
AND SyStaffGroup.Descrip IN ('AC - Academic Counselor', 'FC - Finance Counselors', 'EC - Adm. Counselor With Reg')
UNION
SELECT DISTINCT
'Admissions Counselor' AS AdvisorType
,SyStaff.LastName AS AdvisorLastName
,SyStaff.FirstName AS AdvisorFirstName
,SyStaff.Phone AS AdvisorPhone
,SyStaff.Ext AS AdvisorExtention
,SyStaff.eMail AS AdvisorEMail
,SyStaff.SyStaffID AS SyStaffID
FROM systudent
INNER JOIN AmRep ON SyStudent.AMREpID = AmREp.AMREpid
INNER JOIN SyStaff ON SyStaff.SyStaffID = AmRep.AmRepID
WHERE Systudent.SYStudentid = #systudentid
Any hints or suggested methods that I can either try or Google (I've tried searching but results are a lot more useful if I knew what to look for) would be greatly appreciated.
You can add a nested subquery to indicate which students have the same advisor filling multiple positions, and adjust the type selection accordingly. Here are the changed portions of your above query:
SELECT
CASE WHEN (mutiples.SyStaffID IS NOT NULL) THEN 'SSA'
ELSE SyStaffGroup.Descrip END AS AdvisorType
-- other columns omitted
FROM SyStaff (NOLOCK)
JOIN SyAdvisorByEnroll (NOLOCK)
ON SyAdvisorByEnroll.SyStaffID = SyStaff.SyStaffID
LEFT JOIN (
SELECT SyStaffID,AdEnrollID
FROM SyAdvisorByEnroll
GROUP BY SyStaffID,AdEnrollID
HAVING COUNT(DISTINCT SyStaffGroupID) > 1
) multiples
ON multiples.SyStaffID = SyAdvisorByEnroll.SyStaffID
AND multiples.AdEnrollID = SyAdvisorByEnroll.AdEnrollID
-- rest of query omitted
This might have a mistake or two, since you didn't include your table schema. The nested subquery, "multiples", contains all advisor / enrollee pairs where the advisor is in multiple groups. You left join against this and adjust the final type selection to "SSA" if there's a matching entry in the nested subquery.
An important note: as written, this will include two SSA rows for an eligible advisor / enrollee pair. However, the final results will not, because you are using a UNION in this query, which filters out duplicates, even if they're only present in one half of the union. If you change this to UNION ALL or eliminate the UNION entirely, you will need to add DISTINCT to the top of the query, like so:
SELECT DISTINCT CASE WHEN (mutiples.SyStaffID IS NOT NULL) ...

Outer Join Returns Multiple Rows

I am joining APPS.PER_JOBS on PER_ALL_ASSIGNMENTS_F, looking to get current row - but I get multiple rows unless I add REQUEST_ID to the join. However, when I do this - though it supresses the mulitple rows - it also causes the substring I use to pull Title to return a Null value for all records. How can I join these tables to pull only the current row without causing my Title substring to null out?
Here's the outer join I have right now:
APPS.PER_ALL_ASSIGNMENTS_F B
LEFT OUTER JOIN (SELECT *
FROM APPS.PER_JOBS F_ED
WHERE F_ED.DATE_FROM = (SELECT MAX(F_ED1.DATE_FROM)
FROM APPS.PER_JOBS F_ED1
WHERE F_ED1.JOB_ID = F_ED.JOB_ID
AND F_ED1.DATE_FROM <= SYSDATE)) F
ON F.JOB_ID = B.JOB_ID
AND F.REQUEST_ID = B.REQUEST_ID
And here's the title substring:
SUBSTR(F.NAME,INSTR(F.NAME,'.')+1) AS "Occupation"
Thanks,
Steve
Edit: Here's an example:
Without ‘F.REQUEST_ID = B.REQUEST_ID’:
Employee Number Occupation
597 Manager, Special Events
632 Web Developer
632 Software Developer
8392 Development Intern
8392 Software Developer
With ‘F.REQUEST_ID = B.REQUEST_ID’:
Employee Number Occupation
597
632
8392

Get percentages of larger group

The query below is kind of an ugly one so I hope I've got it spaced well enough to make it readable. The query finds the percentage of people that visit a given hospital if they are from a certain area. For instance, if 100 people live in county X and 20 go to hospital A and 80 go to hospital B the query outputs. How the heck is this sort of thing done? Let me know if I need to document the query or whatever I can do to make it clearer.
hospital A 20
hospital B 80
The query below works exactly like I want it to, but it give me thinking: how could this be done for every county in my table?
select hospitalname, round(cast(counts as float)/cast(fayettestrokepop as float)*100,2)as percentSeen
from
(
SELECT tblHospitals.hospitalname, COUNT(tblHospitals.hospitalname) AS counts, tblStateCounties_1.countyName,
(SELECT COUNT(*) AS Expr1
FROM Patient INNER JOIN
tblStateCounties ON Patient.stateCode = tblStateCounties.stateCode AND Patient.countyCode = tblStateCounties.countyCode
WHERE (tblStateCounties.stateCode = '21') AND (tblStateCounties.countyName = 'fayette')) AS fayetteStrokePop
FROM Patient AS Patient_1 INNER JOIN
tblHospitals ON Patient_1.hospitalnpi = tblHospitals.hospitalnpi INNER JOIN
tblStateCounties AS tblStateCounties_1 ON Patient_1.stateCode = tblStateCounties_1.stateCode AND Patient_1.countyCode = tblStateCounties_1.countyCode
WHERE (tblStateCounties_1.stateCode = '21') AND (tblStateCounties_1.countyName = 'fayette')
GROUP BY tblHospitals.hospitalname, tblStateCounties_1.countyName
) as t
order by percentSeen desc
EDIT: sample data
The sample data below is without the outermost query (the as t order by part).
The countsInTheCounty column is the (select count(*)..) part after 'tblStateCounties_1.countyName'
hospitalName hospitalCounts countyName countsInTheCounty
st. james 23 X 300
st. jude 40 X 300
Now with the outer query we would get
st james 0.076 (23/300)
st. jude 0.1333 (40/300)
Here is my guess. You'll have to test against your data or provide proper DDL + sample data.
;WITH totalCounts AS
(
SELECT StateCode, countyCode, COUNT(*) AS totalcount
FROM dbo.Patient GROUP BY StateCode, countyCode
)
SELECT
h.hospitalName,
hospitalCounts = COUNT(p.hospitalnpi),
c.countyName,
countsInTheCounty = tc.totalCount,
percentseen = CONVERT(DECIMAL(5,2), COUNT(p.hospitalnpi)*100.0/tc.totalCount)
FROM
dbo.Patient AS p
INNER JOIN
dbo.tblHospitals AS h
ON p.hospitalnpi = h.hospitalnpi
INNER JOIN
totalCounts AS tc
ON p.StateCode = tc.StateCode
AND p.countyCode = tc.countyCode
INNER JOIN
dbo.tblStateCounties AS c
ON tc.StateCode = c.stateCode
AND tc.countyCode = c.countyCode
GROUP BY
h.hospitalname,
c.countyName,
tc.totalcount
ORDER BY
c.countyName,
percentseen DESC;