Structure of SQL query using NOT IN / NOT EXISTS(?) with three tables - sql

I need help to figure out the right structure of query to
find the names of those clients, who stopped in exactly the same
hotels as John Doe.
I have tables :
Clients(clientID, clientName)
ClientTour(clientID, tourID)
Tours(tourID, country, hotel)
It all should be written down as one query.
I'm quite new to SQL and can't write down right subqueries for it, as it uses three tables... I somewhat understand how to write similar queries for two tables, but when I tried to write this one... well, I got lost. =/
As far as I understand, I have to find the names of those clients,
who stopped in
[All Hotels where John Doe stopped] - [Hotels where only John Doe stopped]
but who never stopped in those
[Hotels where John Doe never stopped].
Is it logically right? If it is, then I get the part that is
SELECT c1.clientName
FROM Clients c1
WHERE c1.clientID NOT IN (clientIDs of clients in [Hotels where John
Doe never stopped])
AND c1.clientID IN (clientIDs of clients in [[All Hotels where John
Doe stopped] - [Hotels where only John Doe stopped]])
but can't figure out the parts marked in Italics... how do I find clients who stopped in hotels where John Doe did/didn't stop, or the hotels where only John Doe stopped?
Also, is there any easier way to write this query down?

EDIT:
1. Added to the outer SELECT a GROUP BY clause, replacing the need for DISTINCT, and adding the option of countnig hotels for each client in the result set. Note the result set has only hotels that 'John Doe' has attended.
2. Added a SELECT statement to count number of hotels 'John Doe' has visited at.
3. Added a HAVING clause to eliminate from the result set clients with different numbers of hotels than 'John Doe'.
SELECT C.clientName
FROM (Clients AS C INNER JOIN ClientTour AS CT ON C.clientID = CT.clientID) INNER JOIN Tours AS T ON CT.tourID = T.tourID
WHERE C.clientName <> 'John Doe'
AND T.hotel IN
(
SELECT DISTINCT T1.hotel
FROM (Clients AS C1 INNER JOIN ClientTour AS CT1 ON C1.clientID = CT1.clientID) INNER JOIN Tours AS T1 ON CT1.tourID = T1.tourID
WHERE C1.clientName = 'John Doe'
)
GROUP BY C.clientName
HAVING COUNT(DISTINKT T.hotel) = SELECT COUNT(DISTINCT T2.hotel)
FROM (Clients AS C2 INNER JOIN ClientTour AS CT2 ON C2.clientID = CT2.clientID) INNER JOIN Tours AS T2 ON CT2.tourID = T2.tourID
WHERE C2.clientName = 'John Doe'
First atempt:
SELECT C.clientName
FROM (Clients AS C INNER JOIN ClientTour AS CT ON C.clientID = CT.clientID) INNER JOIN Tours AS T ON CT.tourID = T.tourID
WHERE Trim(C.clientName) <> 'John Doe'
AND T.hotel IN
(
SELECT DISTINCT T1.hotel
FROM (Clients AS C1 INNER JOIN ClientTour AS CT1 ON C1.clientID = CT1.clientID) INNER JOIN Tours AS T1 ON CT1.tourID = T1.tourID
WHERE Trim(C1.clientName) = 'John Doe'
)

Related

SQL Union of 4 tables

I have 4 different tables:
Address: address_id, postcode.
Patient: address_id, name.
FocusArea: geom.
Postcode: geom, postcode.
I need to find the name of the patients that live within the focus area.
I have managed to get the postcodes where the patients live, and the postcodes within the focus area, but I don't know how to join both queries:
SELECT
air.address.postcode, air.patient.name
FROM
air.address
INNER JOIN
air.patient ON patient.address_id = address.address_id;
SELECT
postcode as postcode
FROM
air.postcode, air.focusarea
WHERE
air.focusarea.objectid = 1
AND ST_Intersects(air.postcode.geom, air.focusarea.geom);
air.focusarea.objectid = 1 as there are different boundaries of the focus area.
Any ideas?
Thank you
Tania
select
a.postcode, p.name
from air.address as a
inner join air.patient as p on
p.address_id = a.address_id
inner join air.postcode as pc on
pc.postcode = a.postcode
inner join air.focusarea as fa on
fa.objectid = 1 and
ST_Intersects(pc.geom, fa.geom);

How to use 2 instances of the same table

I am wondering how to use 2 instances of the same table in the following example , I know how to do it but I just cant make it work for my task.
I have the following tables :
Agency(id_agency,name)
Space(id_space,address)
Offer(id_agency,id_space)
Task: Find out the address,agency name 1 ,agency name 2 for spaces offered by two different agencies(the combination of 2 agencies is unique).
What I tried:
1)
SELECT ADDRESS,A.NAME,B.NAME
FROM AGENCY A
INNER JOIN OFFER O ON A.ID_AGENCY=O.ID_AGENCY
INNER JOIN SPACE S ON O.ID_SPACE=S.ID_SPACE
WHERE S.ID_SPACE=ANY(SELECT S.ID_SPACE FROM AGENCY B
INNER JOIN OFFER O ON B.ID_AGENCY=O.ID_AGENCY
INNER JOIN SPACE S ON O.ID_SPACE=S.ID_SPACE
);
2)
SELECT ADDRESS
FROM AGENCY A
INNER JOIN OFFER O ON A.ID_AGENCY=O.ID_AGENCY
INNER JOIN SPACE S ON O.ID_SPACE=S.ID_SPACE
INTERSECT
SELECT ADDRESS
FROM AGENCY B
INNER JOIN OFFER O ON B.ID_AGENCY=O.ID_AGENCY
INNER JOIN SPACE S ON O.ID_SPACE=S.ID_SPACE
WHERE A.ID_AGENCY<>B.ID_AGENCY;
In the second example I have no idea how to make it show A.name and b.name, since intersect won't work if I try to add them...
I tried to do this for the last 3 hours , sadly, I guess I can`t do it with the skills I have so far. :(
Thanks in advance
Edit 1: I hope you understand, thats how it should look.
Agency
id_agency name
---------- -------
1 Agency1
2 Agency2
3 Agency3
Space
id_space address
--------- --------
1 address1
2 address2
3 address3
Offer
id_agency id_space
----------- --------
1 1
2 1
3 2
Expected output:
Address Name1 Name2
----------- -------- -------
address1 Agency1 Agency2
To have the results in 1 row, each pair of agencies per row, as you asked for:
select S.address as address, A1.name as agency_1, A2.name as agency_2
from offer O1
join offer O2
on O2.id_space = O1.id_space
and O2.id_agency != O1.id_agency
join space S
on S.id_space = O1.id_space
join agency A1
on A1.id_agency = O1.id_agency
join agency A2
on A2.id_agency = O2.id_agency
;
The "core functionality" here is the join of offer no.1 (O1 alias) to offer no.2 (O2 alias) on equality of id_space but difference of id_agency.
An interesting exercise: To have the results in multiple rows, one agency per row:
select S.address, A.name as agency, X.number_of_agencies_per_space
from (
select id_space, id_agency, count(1) over (partition by id_space) as number_of_agencies_per_space
from offer
) X
join space S
on S.id_space = X.id_space
join agency A
on A.id_agency = X.id_agency
where X.number_of_agencies_per_space > 1
;

SQL Query: Using AND/OR in the WHERE clause

I'm currently working on a project where I have a list of dental patients, and I'm supposed to be displaying all the patients who have two specific procedure codes attached to their profiles. These patients must have BOTH procedure codes, not one or the other. At first I thought I could accomplish this by using a basic AND statement in my WHERE clause, like so.
SELECT [stuff]
FROM [this table]
WHERE ProcedureCode = 'ABC123' AND ProcedureCode = 'DEF456';
The query of course returns nothing because the codes are entered individually and you can't have both Procedure 1 and Procedure 2 simultaneously.
I tried switching the "AND" to "OR" just out of curiosity. Of course I'm now getting results for patients who only have one code or the other, but the patients who have both are showing up too, and the codes are displayed as separate results, like so:
Patient ID Last Name First Name Code Visit Date
1111111 Doe Jane ABC123 11-21-2015
5555555 Smith John ABC123 12-08-2015
5555555 Smith John DEF456 12-08-2015
My SQL is pretty rusty these days. I'm trying to think of a way to filter out patients like Jane Doe and only include patients like John Smith who have both procedure codes. Ideas?
ADDING INFO BASED ON CHRISTIAN'S ANSWER:
This is what the updated query looks like:
SELECT PatientID, LastName, FirstName, Code, VisitDate
FROM VisitInfo
WHERE PatientID IN
(
SELECT PatientID
FROM VisitInfo
WHERE Code = 'ABC123' OR Code = 'DEF456'
GROUP BY PatientID
HAVING COUNT(*) > 1
)
AND (Code = 'ABC123' OR Code = 'DEF456');
So I'm still getting results like the following where a patient is only showing one procedure code but possibly multiple instances of it:
Patient ID Last Name First Name Code Visit Date
1111111 Doe Jane ABC123 11-02-2015
1111111 Doe Jane ABC123 11-21-2015
5555555 Smith John ABC123 12-08-2015
5555555 Smith John DEF456 12-08-2015
5555555 Smith John ABC123 12-14-2015
9999999 Jones Mike DEF456 11-22-2015
9999999 Jones Mike DEF456 12-06-2015
Even though Jane Doe and Mike Jones have 2 results, they're both the same code, so we don't want to include them. And even though John Smith still has 2 of the same code, his results also include both codes, so we want to keep him and other patients like him.
ANOTHER UPDATE:
I just learned that I now need to include a few basic demographic details for the patients in question, so I've joined my VisitInfo table with a PatientInfo table. The updated query looks like this:
SELECT v.PatientID, v.LastName, v.FirstName, v.Code, v.VisitDate, p.DateOfBirth, p.Gender, p.PrimaryPhone
FROM VisitInfo v JOIN PatientInfo p ON v.PatientID = p.PatientID
WHERE v.PatientID IN
(
SELECT PatientID
FROM VisitInfo
WHERE Code = 'ABC123' OR Code = 'DEF456'
GROUP BY PatientID
HAVING COUNT(*) > 1
)
AND (Code = 'ABC123' OR Code = 'DEF456');
I wasn't sure if the new JOIN would affect anyone's answers...
SELECT *
FROM TABLE
WHERE Patient_ID IN
(
SELECT Patient_ID
FROM TABLE
WHERE Code = 'ABC123' OR Code = 'DEF456'
GROUP BY Patient_ID
HAVING COUNT(*) = 2
)
AND (Code = 'ABC123' OR Code = 'DEF456')
UPDATE 1:
As a patient can have multiple ´procedure codes´, this way will work better:
SELECT *
FROM TABLE T1
WHERE EXISTS (SELECT 1
FROM TABLE T2
WHERE T1.Patient_ID = T2.Patient_ID
AND T2.Code = 'ABC123')
AND EXISTS (SELECT 1
FROM TABLE T2
WHERE T1.Patient_ID = T2.Patient_ID
AND T2.Code = 'DEF456')
AND T1.Code IN ('ABC123','DEF456')
There's a bunch of ways to skin this particular cat - here's another one:
WITH ABC123 AS (SELECT DISTINCT PATIENTID
FROM VISITINFO
WHERE PROCEDURECODE = 'ABC123'),
DEF456 AS (SELECT DISTINCT PATIENTID
FROM VISITINFO
WHERE PROCEDURECODE = 'DEF456')
SELECT v.PATIENTID, v.LASTNAME, v.FIRSTNAME, v.PROCEDURECODE, v.VISITDATE,
p.DateOfBirth, p.Gender, p.PrimaryPhone
FROM VISITINFO v
INNER JOIN ABC123 a
ON a.PATIENTID = v.PATIENTID
INNER JOIN DEF456 d
ON d.PATIENTID = v.PATIENTID
INNER JOIN PatientInfo p
ON v.PatientID = p.PatientID
WHERE v.PROCEDURE_CODE IN ('ABC123', 'DEF456')
ORDER BY v.PATIENTID, v.VISITDATE, v.PROCEDURECODE;
What we're doing here is using a couple of CTE's to get the PATIENTID's for each patient who has the the procedures in question performed. We then start with all records in VISITINFO and inner join those with the two CTE's. Because an INNER JOIN requires that matching information exist in the tables on both sides of the join this has the effect of retaining only the visits which match the information in both of the CTE's, based on the join criteria which in this case is the PATIENTID.
Best of luck.
Select the records with the two codes, but use COUNT OVER to count the distinct codes per patient. Only keep those records with a count of 2 codes for the patient, i.e. patients with both codes.
select patient_id, last_name, first_name, code, visit_date
from
(
select mytable.*, count(distinct code) over (partition by patient_id) as cnt
from mytable
where code in ('ABC123','DEF456')
) data
where cnt = 2;

SQL Query (or Join) for 3 tables

first time asking a question on Stack Overflow... Amazing resource, but there's just one thing that's really baffling me as a newcomer to SQL.
I have three tables and I would like to obtain the names of all the Mentors who are linked to Bob's students.
Table 1: TEACHERS
================
ID Name
================
1 Bob
Table 2: STUDENTS
===================================
STUDENT_ID Name TEACHER_ID
===================================
1 Jayne 1
2 Billy 5
3 Mark 2
Table 3: MENTOR_RELATIONSHIPS
==============================
ID STUDENT_ID MENTOR_ID
==============================
1 1 3
2 2 2
3 3 3
Table 4: MENTORS
=====================
MENTOR_ID Name
=====================
1 Sally
2 Gillian
3 Sean
I would like to run a query to find all of the mentors of Bob's students. So the mentors for all students with TEACHER_ID = 1
In this case Sean would be the result.
I know that it is something to do with Joins, or could I find this using a normal query??
Any help is much appreciated! Many thanks...
this should do the work
select distinct m.name from students s
inner join mentor_ralationships mr on mr.student_id=s.student_id
inner join mentors m on m.mentoir_id=mr.mentor_id
where s.teacher_id=1;
Without joins (not preferred)
SELECT mentors.name FROM mentors
WHERE mentors.id
IN (SELECT MENTOR_RELATIONSHIPS.mentor FROM MENTOR_RELATIONSHIPS
WHERE MENTOR_RELATIONSHIPS.student
IN (SELECT students.id FROM students WHERE students.teacher
= (SELECT teachers.id FROM teachers WHERE teachers.name = 'bob')));
It could be helpful for you as I had to retrieve data from three tables AssignedSubject, Section and SchoolClass when a teacher assigned to specific subject then I have to find out the his class and section details including subjectid which I did this way
select a.StaffID, a.SubjectID, s.ID as SectionId, s.Name as SectionName, S.Remarks as SectionRemarks, sc.ID as ClassId, sc.Name as ClassName, sc.Remarks as ClassRemarks from AssignedSubject a
inner join Section s on a.SectionId=s.ID
inner join SchoolClass sc on sc.ID=s.ClassId where a.StaffID=3068
You could try the following:
SELECT DISTINCT m.name
FROM students s INNER JOIN TEACHERS t ON t.ID = a.TEACHER_ID
INNER JOIN MENTOR_RELATIONSHIPS mr ON mr.Student_id = s.Student_id
INNER JOIN Mentors m ON mr.MENTOR_ID = s.MENTOR_ID
WHERE s.teacher_id = 1 WHERE t.Name = 'Bob';
SELECT TEACHER.NAME, STUDENTS.NAME AS STUDENT NAME, MENTORS.NAME AS MENTOR
FROM TEACHERS JOIN STUDENTS ON TEACHERS.ID = STUDENTS.TEACHER_ID
JOIN MENTOR_RELATIONSHIPS ON STUDENTS.STUDENT_ID =
MENTOR_RELATIONSHIPS.STUDENT_ID
JOIN MENTORS ON MENTOR_RELATIONSHIPS.MENTOR_ID = MENTORS.MENTOR_ID
WHERE TEACHER.NAME = 'Bob' ;

SQL query involving three tables and checking whether some result has entry in another table

I have 3 tables in my SQL database as follows
Users2
UserID,
EID,
Name,
Email,
Department,
Enabled
Sites
SiteID,
SiteCode,
SiteName
UserSites2
UsersSitesID,
UserID,
SiteID
What I need to do is, given EID and SiteID, get a full row from the Users2 table AND the SiteID, SiteCode and SiteName from the Sites table WHEN the userID of the retrieved record has an entry in the UserSites2
Example of expected result:
Users2
1, 12345, Me, me#email.com, Support, True
2, 12346, you, you#email.com, Service, True
Sites
1, 123, Regional HQ
2, 234, National HQ
UserSites2
1, 1, 1
2, 1, 2
3, 2, 2
So given EID 12345 and SiteID 2 I should get the result
1, 12345, Me, me#email.com, Support, True, 2, 234, National HQ
and for EID 12346 and SiteID 1 I should get nothing
I know how to join Users2 and Sites to get the full row I want but I don't understand how to make it depend on whether there is an entry in the lookup table for it.
select u2.UserID, u2.EID, u2.Name, u2.Email, u2.Department, u2.Enabled,
s.SiteID, s.SiteCode, s.SiteName
from users2 u2
left outer join usersites us on u2.UserID = us.UserID
left outer join sites s on s.SiteID = us.SiteID
where u2.EID = 12345 and us.SiteID = 2
I have tested this. It will give you no records if it is not mapped in UserSites. So for EID 12346 and SiteID 1 you will get nothing.
you would do something like this:
select col1,col2,.....from Users, Sites, UserSites
where Users.eid = 12345 and Sites.siteid = 2
and users.user_id = userSites.userid
and sites.siteis = usersites.siteid
I like to specifically list the joins where possible. I believe something like this would work:
select u2.UserID, u2.EID, u2.Name, u2.Email, u2.Department, u2.Enabled,
s.SiteID, s.SiteCode, s.SiteName
from Users2 u2 join UserSites2 us2 on u2.UserID=us2.UserID
join sites s on us2.SiteID=s.SideID
where u2.EID=<eid> and
us2.SiteID=<siteid>
i know how to join Users2 and Sites to get the full row I want, but I don't understand how to make it depend on wether there is an entry in the lookup table for it.
Using a LEFT JOIN means there might not be a supporting record, based on the join criteria. Where records are missing, you'll see null in appropriate column. If you want to only return rows based on the criteria, use a [INNER] JOIN. Here's a good link showing the differences between JOINs and it includes UNIONs.
Here's your query:
SELECT u.userid,
u.eid,
u.name,
u.email,
u.department,
u.enabled,
s.siteid,
s.sitecode,
s.sitename
FROM USERS2 u
JOIN USERSITES2 us ON us.userid = u.userid
JOIN SITES s ON s.siteid = us.siteid
WHERE u.eid = ?
AND us.siteid = ?