SQL Joins Clarification - sql

I wish to display the hospitalid,hosp name and hosp type for the hospital which have/has the highest no of doctors associated with them.
I have two tables:
Doctor: doctorid, hospitalid
Hospital: hospitalid, hname, htype
SELECT d.hospitalid,h.hname,h.htype
FROM doctor d
INNER JOIN hospital h ON d.hospitalid = h.hospitalid
GROUP BY d.hospitalid,h.hname,h.htype
HAVING MAX(count(d.doctorid));
I tried the above code, but i get an error "group func is nested too deeply". How should i modify d code?

This is a common error when learning SQL, thinking that having Max(col) says "keep only the row with the max". It simply means having <some function on the column> without any condition. For instance, you could say having count(d.doctorid) = 1 to get hospitals with only one doctor.
The way to do this is to order the columns and then take the first row. However, the syntax for "take the first row" varies by database. The following works in many SQL dialects:
SELECT d.hospitalid,h.hname,h.htype
FROM doctor d INNER JOIN
hospital h
ON d.hospitalid = h.hospitalid
GROUP BY d.hospitalid,h.hname,h.htype
order by count(d.doctorid) desc
limit 1;
In SQL Server and Sybase, the syntax is:
SELECT top 1 d.hospitalid,h.hname,h.htype
FROM doctor d INNER JOIN
hospital h
ON d.hospitalid = h.hospitalid
GROUP BY d.hospitalid,h.hname,h.htype
order by count(d.doctorid) desc;
In Oracle:
select t.*
from (SELECT d.hospitalid,h.hname,h.htype
FROM doctor d INNER JOIN
hospital h
ON d.hospitalid = h.hospitalid
GROUP BY d.hospitalid,h.hname,h.htype
order by count(d.doctorid) desc
) t
where rownum = 1;
EDIT (based on comment):
To get all rows with the maximum, then you can do something similar to your original query. It is just more complicated. You can calculate the maximum number using a subquery and do the comparison in the having clause:
SELECT d.hospitalid, h.hname, h.htype
FROM doctor d INNER JOIN
hospital h
ON d.hospitalid = h.hospitalid join
GROUP BY d.hospitalid,h.hname,h.htype
having count(d.doctorid) = (select max(NumDoctors)
from (select hospitalid, count(*) as NumDoctors
from hospitalId
group by hospitalid
) hd
)
As a note, there are easier mechanisms in other databases.

This is how I would write it for SQL Server. THe specific details might vary depending teh database backend you are using.
SELECT TOP 1 a.hospitalid,a.hname,a.htype
FROM
(SELECT d.hospitalid,h.hname,h.htype, count(d.doctorid) as doctorcount FROM doctor d INNER JOIN hospital h ON d.hospitalid = h.hospitalid
GROUP BY d.hospitalid,h.hname,h.htype) a
ORDER BY doctorcount DESC;

Related

How do I select how many pantients every doctor has?

I have a table of Visits on the Hospital, included DoctorID, PacientID, Date....
I did like this but I recieved an error.
SELECT
d.DoctorID, COUNT(v.DoctorID)
FROM Visits v
left join Doctors d on v.DoctorID=d.DoctorID
You care missing the group by:
SELECT d.DoctorID, COUNT(v.DoctorID)
FROM Doctors d LEFT JOIN
Visits v
ON v.DoctorID = d.DoctorID
GROUP BY d.DoctorID;
In addition, you presumably want the LEFT JOIN starting with Doctors so you keep all doctors.
Your query is missing a GROUP BY clause. Also I do not see the need for a JOIN. If you want the count of (distinct) patientsIDs per doctorID, you can get that information directly from the visit table, like so:
select doctorID, count(distinct patientID) from visits group by doctorID
Or, if you also want to see doctors that have no patients:
select d.doctorID, count(distinct v.patientID)
from doctors d
left join visits v on v.doctorID = d.doctorID
group by d.doctorID
Live demo on db<>fiddle
You are missing GroupBy clause.
So you can fix it in this way
SELECT d.Name, COUNT(v.DoctorID) AS PantientCount
FROM Doctors d
LEFT JOIN Visits v ON d.DoctorId = v.DoctorId
GROUP BY d.Name
You can also using Conditional aggregation query with a group by like below.
SELECT d.Name, SUM(CASE WHEN v.CustomerId IS NOT NULL THEN 1 ELSE 0 END) AS PantientCount
FROM Doctors d
LEFT JOIN Visits v ON d.DoctorId = v.DoctorId
GROUP BY d.Name
Output

How can I show the maximum score of each department with their names

I can select maximum the score of each department but I can't show the name of each person associated with the max score.
I tried to select the name and the maximum grade (with maximum function) but it doesn't work:
select max(stgrade)as highscore,StName,DepName --department
from TBL_DEPARTMANTS d
inner join TBL_LESSONS l on d.DepID=l.LessonID
inner join TBL_GRADES g on g.lessonid=l.LessonID
inner join TBL_STUDENT s on s.STID=g.stid
group by DepName,StName
order by DepName,highscore desc
You may try this...
select * from ( select rank() over (partition by DepName order by stgrade desc) as Slno, stgrade, stname, DepName
from TBL_DEPARTMANTS d
inner join TBL_LESSONS l on d.DepID=l.LessonID
inner join TBL_GRADES g on g.lessonid=l.LessonID
inner join TBL_STUDENT s on s.STID=g.stid ) as dep where dep.slno=1
First create rank() in decreasing order of grade for individual department. then select top record for same.
Note: Use RANK() or DENSE_RANK(), both will work fine for top 1 record, while if you want to select n highest grade then use DENSE_RANK(), at the last for slno pass n'th record you want to select.
Always hard to do theory selects, but while DarkRob's solution is good, it will remove students if for instance two people are best. This is why I love using cross apply:
select
d.Depname
, s.StName
, g.stgrade
from TBL_DEPARTMANTS d
inner join TBL_LESSONS l on
d.DepID=l.LessonID
inner join TBL_GRADES g on
g.lessonid=l.LessonID
inner join TBL_STUDENT s on
s.STID=g.stid
cross apply (
select
sub_d.DepID
, max(sub_g.stgrade) as maxgrade
from TBL_DEPARTMANTS sub_d
inner join TBL_LESSONS sub_l on
sub_d.DepID=sub_l.LessonID
inner join TBL_GRADES sub_g on
sub_g.lessonid=sub_l.LessonID
where sub_d.Dep_ID = d.Dep_ID
group by sub_d.DepID
) as sub
where g.stgrade = sub.maxgrade
This way you will get all the people with max grade per department and not just one.

Count and group the number of times each town is listed in the table

SELECT PEOPLE.TOWNKEY, TOWN_LOOKUP.TOWN FROM PEOPLE
INNER JOIN TOWN_LOOKUP
ON PEOPLE.TOWNKEY = TOWN_LOOKUP.PK
ORDER BY TOWN
Current Table Output:
You are missing the group by clause entirely:
SELECT tl.town, COUNT(*)
FROM people p
INNER JOIN town_lookup ON p.townkey = tl.pk
GROUP BY tl.town
ORDER BY tl.town

Query extensibility with WHERE EXISTS with a large table

The following query is designed to find the number of people who went to a hospital, the total number of people who went to a hospital and the divide those two to find a percentage. The table Claims is two million plus rows and does have the correct non-clustered index of patientid, admissiondate, and dischargdate. The query runs quickly enough but I'm interested in how I could make it more usable. I would like to be able to add another code in the line where (hcpcs.hcpcs ='97001') and have the change in percentRehabNotHomeHealth be relfected in another column. Is there possible without writing a big, fat join statement where I join the results of the two queries together? I know that by adding the extra column the math won't look right, but I'm not worried about that at the moment. desired sample output: http://imgur.com/BCLrd
database schema
select h.hospitalname
,count(*) as visitCounts
,hospitalcounts
,round(count(*)/cast(hospitalcounts as float) *100,2) as percentRehabNotHomeHealth
from Patient p
inner join statecounties as sc on sc.countycode = p.countycode
and sc.statecode = p.statecode
inner join hospitals as h on h.npi=p.hospitalnpi
inner join
--this join adds the hospitalCounts column
(
select h.hospitalname, count(*) as hospitalCounts
from hospitals as h
inner join patient as p on p.hospitalnpi=h.npi
where p.statecode='21' and h.statecode='21'
group by h.hospitalname
) as t on t.hospitalname=h.hospitalname
--this where exists clause gives the visitCounts column
where h.stateCode='21' and p.statecode='21'
and exists
(
select distinct p2.patientid
from Patient as p2
inner join Claims as c on c.patientid = p2.patientid
and c.admissiondate = p2.admissiondate
and c.dischargedate = p2.dischargedate
inner join hcpcs on hcpcs.hcpcs=c.hcpcs
inner join hospitals as h on h.npi=p2.hospitalnpi
where (hcpcs.hcpcs ='97001' or hcpcs.hcpcs='9339' or hcpcs.hcpcs='97002')
and p2.patientid=p.patientid
)
and hospitalcounts > 10
group by h.hospitalname, t.hospitalcounts
having count(*)>10
You might look into CTE (Common Table Expressions) to get what you need. It would allow you to get summarized data and join that back to the detail on a common key. As an example I modified your join on the subquery to be a CTE.
;with hospitalCounts as (
select h.hospitalname, count(*) as hospitalCounts
from hospitals as h
inner join patient as p on p.hospitalnpi=h.npi
where p.statecode='21' and h.statecode='21'
group by h.hospitalname
)
select h.hospitalname
,count(*) as visitCounts
,hospitalcounts
,round(count(*)/cast(hospitalcounts as float) *100,2) as percentRehabNotHomeHealth
from Patient p
inner join statecounties as sc on sc.countycode = p.countycode
and sc.statecode = p.statecode
inner join hospitals as h on h.npi=p.hospitalnpi
inner join hospitalCounts on t.hospitalname=h.hospitalname
--this where exists clause gives the visitCounts column
where h.stateCode='21' and p.statecode='21'
and exists
(
select p2.patientid
from Patient as p2
inner join Claims as c on c.patientid = p2.patientid
and c.admissiondate = p2.admissiondate
and c.dischargedate = p2.dischargedate
inner join hcpcs on hcpcs.hcpcs=c.hcpcs
inner join hospitals as h on h.npi=p2.hospitalnpi
where (hcpcs.hcpcs ='97001' or hcpcs.hcpcs='9339' or hcpcs.hcpcs='97002')
and p2.patientid=p.patientid
)
and hospitalcounts > 10
group by h.hospitalname, t.hospitalcounts
having count(*)>10

Select one record from two tables in Oracle

There are three tables:
A table about students: s41071030(sno, sname, ssex, sage, sdept)
A table about course: c41071030(cno, cname, cpno, credit)
A table about selecting courses: sc41071030(sno, cno, grade)
Now, I want select the details about a student whose sdept='CS' and he or she has selected the most courses in department 'CS'.
As with any modestly complex SQL statement, it is best to do 'TDQD' — Test Driven Query Design. Start off with simple parts of the question and build them into a more complex answer.
To find out how many courses each student in the CS department is taking, we write:
SELECT S.Sno, COUNT(*) NumCourses
FROM s41071030 S
JOIN sc41071030 SC ON S.Sno = SC.Sno
WHERE S.Sdept = 'CS'
GROUP BY S.Sno;
We now need to find the largest value of NumCourses:
SELECT MAX(NumCourses) MaxCourses
FROM (SELECT S.Sno, COUNT(*) NumCourses
FROM s41071030 S
JOIN sc41071030 SC ON S.Sno = SC.Sno
WHERE S.Sdept = 'CS'
GROUP BY S.Sno
)
Now we need to join that result with the sub-query, so it is time for a CTE (Common Table Expression):
WITH N AS
(SELECT S.Sno, COUNT(*) NumCourses
FROM s41071030 S
JOIN sc41071030 SC ON S.Sno = SC.Sno
WHERE S.Sdept = 'CS'
GROUP BY S.Sno
)
SELECT N.Sno
FROM N
JOIN (SELECT MAX(NumCourses) MaxCourses FROM N) M
ON M.MaxCourses = N.NumCourses;
And we need to get the student details, so we join that with the student table:
WITH N AS
(SELECT S.Sno, COUNT(*) NumCourses
FROM s41071030 S
JOIN sc41071030 SC ON S.Sno = SC.Sno
WHERE S.Sdept = 'CS'
GROUP BY S.Sno
)
SELECT S.*
FROM s41071030 S
JOIN N ON N.Sno = S.Sno
JOIN (SELECT MAX(NumCourses) MaxCourses FROM N) M
ON M.MaxCourses = N.NumCourses;
Lightly tested SQL: you were warned. To test, run the component queries, making sure you get reasonable results each time. Don't move on to the next query until the previous one is working correctly.
Note that the courses table turns out to be immaterial to the query you are solving.
Also note that this may return several rows if it turns out there are several students all taking the same number of courses and that number is the largest number that any student is taking. (So, if there are 3 students taking 7 courses each, and no student taking more than 7 courses, then you will see 3 rows in the result set.)
Aggregate sc41071030 rows to get the counts.
Join the results to s41071030 to:
filter rows on sdept;
get student details;
RANK() the joined rows on the count values.
Select rows with the ranking of 1.
WITH
aggregated AS (
SELECT
sno,
COUNT(*) AS coursecount
FROM
sc41071030
GROUP BY
sno
),
ranked AS (
SELECT
s.*,
RANK() OVER (ORDER BY agg.coursecount DESC) AS rnk
FROM
s41071030 s
INNER JOIN aggregated agg ON s.sno = agg.sno
WHERE
s.sdept = 'CS'
)
SELECT
sno,
sname,
ssex,
sage,
sdept
FROM
ranked
WHERE
rnk = 1
;