Cross records between tables - sql

I have this query:
select pa.id,pa.name
from patients pa
where (select count(distinct co.doctorID)
from consults co
where co.patientID=pa.id) = (select count(do.id)
from doctors do);
In which I select the patients who had consults with all the doctors.
I am counting the number of distinct doctors and see if is equal to the total of doctors.
But how can I do this with an exists and/or in without using count

You can use a nested NOT EXISTS:
select pa.id,pa.name
from patients pa
where NOT EXISTS
(
select 1 FROM doctors do
where NOT EXISTS
(
SELECT 1 FROM consults co
WHERE co.doctorID=do.id
AND pa.id=co.patientID
)
)
In other words, select all patients without a doctor that was not consulted. At least it should give you the idea.

I think you can try even better solution with NOT EXISTS that will not require counts at all:
SELCT pa.id,pa.name
FROM patients pa
WHERE NOT EXISTS (SELECT *
FROM doctors do
LEFT JOIN consults co
ON co.doctorID ON do.id
WHERE co.patientID=pa.id
AND co.doctorID IS NULL
)
In sub-query we get all the doctors and see whether all of them have consult for current patient. If co.doctorID IS NULL, that doctor was not visited by patient.

Related

PostgreSQL JOIN in aggregate function

Treatment table schema
id
doctor_id
location_id
patient_id
...
I need to be able to group patient treatments based on location and list all the doctors that were treating the patient for the specific location.
SELECT id,
(SELECT STRING_AGG(t.initials::text, ',') FROM (
SELECT DISTINCT treatments.doctor_id, doctors.initials FROM
treatments LEFT JOIN doctors ON doctors.id = treatments.doctor_id) t
) as treating_doctors_initials
GROUP BY treatments.patient_id, treatments.location_id
The problem is that this returns the same initials for all records -> it does perform the join I guess or maybe I am misunderstanding the aggregate function.

SQL getting the average count of diagnosis based on specialty

I have 2 tables one for appointments and one for doctors.
I want to select the average number of patients for each specialty, which is stored in the doctors table. The appointments table has the patients' id the doctors' id and the diagnosis, if a patient has had a diagnosis.
I tried this, but doesn't work.
SELECT AVG(patientAMKA)
FROM appointments
WHERE diagnosis IS NOT NULL
GROUP
BY doctor.specialty
EDIT: I just want to clarify that patientAMKA is the id of the patient.
EDIT2: I specifically mean how many patients (with a diagnosis) exist for each speciaity, then take the average of those numbers.
Sounds like it would be something like:
SELECT d.specialty, COUNT(*)
FROM doctor d
INNER JOIN appointments a ON d.id = a.doctor_id
WHERE diagnosis IS NOT NULL
GROUP BY d.specialty
SELECT doc.specialty, AVG(app.patientAMKA)
FROM doctor doc
JOIN appointments app ON doc.doctorid = app.doctorid
WHERE app.diagnosis IS NOT NULL
GROUP BY doc.specialty
Based on your clarifications, I believe what you want is:
SELECT
AVG(CountOfDiagnoses) as TheAverage
FROM
(
SELECT
d.specialty,
COUNT(1) as CountOfDiagnoses
FROM
appointments a
JOIN doctor d ON
d.doctor_id = a.doctor_id
WHERE
a.diagnosis IS NOT NULL
GROUP BY
d.specialty
) Counts

Get courses chosen by all students

Now there are two tables student(sid, name) and course(cid, name), which relation is many-to-many. So there is another table student_course(sid, cid) that stores the information about what courses have been chosen by whom.
How to write the sql that can get the courses that have been chosen by all the students?
Standard solution: use the NOT EXISTS ( ... NOT EXISTS (...)) construct:
find all courses in which all students take part
==>> there must not exist a student that does not take part in this course
SELECT * FROM course c
WHERE NOT EXISTS (
SELECT * from student s
WHERE NOT EXISTS (
SELECT * from student_course cs
WHERE cs.sid = s.sid
AND cs.cid = c.cid
)
)
;
This query is often faster (given appropiate indexes) than the count() == count() variant. The reason for this: you do not have to count all the (distinct) records; once you found one student that does not take this course you can omit this course from your list of suspects. Also: ANTI-joins often can make use of indexes [so can a count(), but that still has to count all the (distinct) keyvalues in the index]
Select c.cid, c.name
From course c where
(select count(1) from student) = (select count(1) from student_course sc where sc.cid = c.cid);
See SQL Fiddle
It finds all courses where the count of entries for that course in the student_course table matches the number of students
CID NAME
1 Test Course1
4 Test Course4

sql query to select matching rows for all or nothing criteria

I have a table of cars where each car belongs to a company. In another table I have a list of company locations by city.
I want to select all cars from the cars table whose company has locations on all cities passed into the stored procedure, otherwise exclude those cars all together even if it falls short of one city.
So, I've tried something like:
select id, cartype from cars where companyid in
(
select id from locations where cityid in
(
select id from cities
)
)
This doesn't work as it obviously satisfies the condition if ANY of the cities are in the list, not all of them.
It sounds like a group by count, but can't make it work with what I tried.
I"m using MS SQL 2005
One example:
select id, cartype from cars c
where ( select count(1) from cities where id in (...))
= ( select count(distinct cityid)
from locations
where c.companyid = locations.id and cityid in (...) )
Maybe try counting all the cities, and then select the car if the company has the same number of distinct location cities are there are total cities.
SELECT id, cartype FROM cars
WHERE
--Subquery to find the number of locations belonging to car's company
(SELECT count(distinct cities.id) FROM cities
INNER JOIN locations on locations.cityid = cities.id
WHERE locations.companyId = cars.companyId)
=
--Subquery to find the total number of locations
(SELECT count(distinct cities.id) FROM cities)
I haven't tested this, and it may not be the most efficient query, but I think this might work.
Try this
SELECT e.*
FROM cars e
WHERE NOT EXISTS (
SELECT 1
FROM Cities p
WHERE p.location = e.Location
)

Help construct a query given a schema

Here is the schema for the database: http://i.stack.imgur.com/omX60.png
Question is: How many people have at least five еntitlements?
I've got this, please tell me how wrong it is and fix it.
select count(personId)
from serialNumber_tbl natural join entitlement_tbl
group by personId
having sum(entitlementID) > 5
Thank you.
The condition for at least 5 is >= 5, not > 5
You need to count the distinct ids in the entitlement table, not person
This gives you the persons, next you need to subquery it to find the count of persons.
select count(personId)
FROM
(
select personId
from serialNumber_tbl natural join entitlement_tbl
group by personId
having count(distinct entitlement_id) >= 5
) X
Your request isn't exactly clear. Are you asking for the count of people with more than five entitlement rows whether they exist on multiple serial numbers or not? If so, you could do something like:
Select Count(*) As CountOfPeople
From Person_tbl As P
Where Exists (
Select 1
From serialNumbers As S1
Join entitlement_tbl As E1
On E1.serialNumberId = S.serialNumberId
Where S1.personId = P.personId
Having Count(*) >= 5
)
Or is it that you are asking to find the number of people that have a serialNumber with more than five entitlements? If that is the case, then you could do something like:
Select Count(*) As CountOfPeople
From Person_tbl As P
Where Exists (
Select 1
From serialNumbers As S1
Join entitlement_tbl As E1
On E1.serialNumberId = S.serialNumberId
Where S1.personId = P.personId
Having Count( Distinct S1.serialNumberId ) >= 5
)