SQL returns only some of the results for a complex one to many query - sql

My table structure is as follows
Endoscopy:
PK:Endoscopy_Id
FK:PatientId
This is many to one with...
Patient
PK:Patient_Id
This is one to many with Endoscopy and with Histology
Histology
PK:Histology_Id
FK:PatientId
This is one to many with
HistologyDetails
PK:HistologyDet_Id
FK:Histology_Id
I am trying to perform a query which will extract all the 'astroscopies' and some fields from histology and histologydetails for all the patients. At the moment I am not getting all the results and I am not sure why. I am using:
SELECT
Endoscopy.*,
Histology.Diagnosis, Histology.NatureOfSpec,
Histology.Histology,
HistolDetails.MeasurementLargest, HistolDeatils.NumberBx
FROM
Endoscopy
JOIN
PatientData ON Endoscopy.HospNum_Id = PatientData.HospNum_Id
JOIN
Histology ON Histology.HospNum_Id = PatientData.HospNum_Id
JOIN
HistolDetails ON Histology.Histology_Id = HistolDetails.Histology_Id
WHERE
histology.VisitDate = endoscopy.VisitDate
AND (ERFINDINGSSTR LIKE '%Barret%'OR ERDIAGNOSISSTR LIKE '%Barret%')
AND Endoscopy.ERPROCEDUREPERFORMED LIKE '%astroscopy%'

Since you only use Join it will only return your Dataset if correlating rows exists in those tables, PatientData, Histology and Histoldetails.
You will want to use Left Joins if the data in the joined tables may or may not have a relationship.
FYI just using Join like you have correlates to an Inner join in the below image

Related

SQL query wrong index when where on join

I have a query with joins that is not using the index that would be the best match and I am looking for help to correct this.
I have the following query:
select
equipment.name,purchaselines.description,contacts.name,vendors.accountNumber
from purchaselines
left join vendors on vendors.id = purchaselines.vendorId
left join contacts on contacts.id = vendors.contactId
left join equipment on equipment.id = purchaselines.equipmentId
where contacts.id = 12345
The table purchaselines has an index on the column vendorId, which is the proper index to use. When the query is run, I know the value of contacts.id which is joined to vendors.contactId which is joined to purchaselines.vendorId.
What is the proper way to run this query? Currently, no index is used on the table purchaselines.
If you are intending to query a specific contact, I would put THAT first since that is the primary basis. Additionally, you had left-joins to the other tables (vendors, contacts, equipment). So by having a WHERE clause to the CONTACTS table forces the equation to become an INNER JOIN, thus REQUIRING.
That said, I would try to rewrite the query as (also using aliases for simplified readability of longer table names)
select
equipment.name,
purchaselines.description,
contacts.name,
vendors.accountNumber
from
contacts c
join vendors v
on c.id = v.contactid
join purchaselines pl
on v.id = pl.vendorid
join equipment e
on pl.equipmentid = e.id
where
c.id = 12345
Also notice the indentation of the JOINs helps readability (IMO) to see how/where each table gets to the next in a more hierarchical manner. They are all regular inner JOIN context.
So, the customer ID will be the first / fastest, then to vendors by that contact ID which should optimize the join to that. Then, I would expect the purchase lines to have an index on vendorid optimizing that. And finally, the equipment table on ITs PK.
FEEDBACK Basic JOIN clarification.
JOIN is just the explicit statement of how two tables are related. By listing them left-side and right-side and the join condition showing on what relationship is between them is all.
Now, in your data example, each table is subsequently nested under the one prior. It is quite common though that one table may link to multiple other tables. For example an employee. A customer could have an ethnicity ID linking to an ethnicity lookup table, but also, a job position id also linking to a job position lookup table. That might look something like
select
e.name,
eth.ethnicity,
jp.jobPosition
from
employee e
join ethnicitiy eth
on e.ethnicityid = eth.id
join jobPosition jp
on e.jobPositionID = jp.id
Notice here that both ethnicity and jobPosition are at the same hierarchical level to the employee table scenario. If, for example, you wanted to further apply conditions that you only wanted certain types of employees, you can just add your logical additional conditions directly at the location of the join such as
join jobPosition jp
on e.jobPositionID = jp.id
AND jp.jobPosition = 'Manager'
This would get you a list of only those employees who are managers. You do not need to explictily add a WHERE condition if you already include it directly at the JOIN/ON criteria. This helps keeping the table-specific criteria at the join if you ever find yourself needing LEFT JOINs.

SQL for many to one to many table

I have three tables in an Access database that I am using in java via ucanaccess.
Patients (PK Pt_ID)
Endoscopy (PK Endo_ID, FK Pt_ID)
Histology (PK Histol_ID, FK Pt_ID)
1 patient can have many endoscopies
1 patient can have many histologies
Endoscopy and histology are not related
I want to retrieve all the Endoscopies and histologies for a single patients in a single SQL query. Although I can write select statements for two tables I don't know how to do this across the three tables. Is it something like this
Select *.Endoscopy,*.Histology from Patients INNER JOIN Endoscopy, Histology ON Patient.Pt_Id=Endoscopy.Pt_ID, Patient.Pt_Id=Histology.Pt_ID
I'm sure that's a mess though...
What kind of SQL DB are you using?
I believe this works on most.
SELECT * FROM Patients, Endoscopy, Histology
WHERE Patient.Pt_Id=Endoscopy.Pt_ID
AND Patient.Pt_Id=Histology.Pt_ID
Also, I belive you have these switched around *.Endoscopy,*.Histology If you need to use that it should be Endoscopy.*, Histology.*
You can use the following query to select both endoscopies and histologies :
SELECT p.Pt_ID
, e.Endo_ID
, h.Histol_ID
FROM Patients p
INNER JOIN Endoscopy e ON p.Pt_Id = e.Pt_ID
INNER JOIN Histology h ON p.Pt_Id = h.Pt_ID
But I'm not sure this is really what you want. You might need to map the tables Patients, Endoscopy and Histology into Java classes ? In this case, you can consider the Java Persistence API (JPA). It helps you to handle these tables in your java code. Here is a JPA Quick Guide.
First idea is to use inner join (with correct syntax) but this is wrong. inner join returns patients who have both procedure. Pure left join returns additionally patients who have none. So this is the solution:
SELECT Patients.Pt_PK, Endoscopy.*, Histology.*
FROM Patients
LEFT JOIN Endoscopy ON Patients.Pt_Id = Endoscopy.Pt_ID
LEFT JOIN Histology ON Patients.Pt_Id = Histology.Pt_ID
--exclude patients who don't have any
where coalesce(Endoscopy.Endo_ID, Histology.Histol_ID) is not null
If you have multiple Endoscopy records or multiple Histology records for the same Patient then you will receive duplicate/repeated records in your SELECT. I do no think there is a way around that unless you use 2 SELECT statements instead of 1.
SELECT Endoscopy.*, Histology.*
FROM Patients
INNER JOIN Endoscopy ON Patients.Pt_Id = Endoscopy.Pt_ID
INNER JOIN Histology ON Patients.Pt_Id = Histology.Pt_ID
To select all records on a table in the select its table name/table alias .*
INNER JOIN will only select records where there is a relationship, once one of these tables does not contain a Pt_ID where it is contained in any one of the other tables then no record will be displayed with that Pt_ID
To add additional tables continue to add additional join statements
You used Patients (with S) in one location and Patient (no S) in another, make sure you use the correct naming. I am guessing its Patients but maybe its Patient.
This statement does almost the same as the above but uses LEFT JOIN syntax so that you will always get records for both tables even if one of the two tables does not have a record for a patient.
SELECT Endoscopy.*, Histology.*
FROM Patients
LEFT JOIN Endoscopy ON Patients.Pt_Id = Endoscopy.Pt_ID
LEFT JOIN Histology ON Patients.Pt_Id = Histology.Pt_ID
WHERE Histology.Histol_ID IS NOT NULL OR Endoscopy.Endo_ID IS NOT NULL
The added WHERE clause ensures that you do not get a record with all NULL values where there is a patient but no records in either of those tables.

Viewing Data from two tables while linking multiple tables in SQL

I am trying to set up a view in a database I want to see all the data in the PERSON table and three columns from the NON_PERSONNEL table for a program from the PROGRAM table. This is what I am trying now, the query runs without errors but doesnt give me any results. All 4 of the tables listed below are imperative to derive the answer
SELECT
person.*,
non_personnel.description,
non_personnel.amount
FROM
person,
non_personnel,
personnel_role,
programs
WHERE
person.person_id = personnel_role.person_id
AND personnel_role.program_id = programs.program_id
AND programs.program_id = non_personnel.program_id
AND programs.program_name = 'Fake Program'
you need to use left join, so you get all persons but description and amount can be NULL if that person doesn't have records in other tables
Also use explicit join syntax.
SELECT person.*, non_personnel.description, non_personnel.amount
FROM person
left join personnel_role
ON person.person_id = personnel_role.person_id
left join programs
ON personnel_role.program_id = programs.program_id
AND programs.program_name = 'Fake Program'
left join non_personnel
ON programs.program_id = non_personnel.program_id
This really depends on your schema and the data in the tables. The way you have it written now means that only records that match in every table (according to your WHERE conditions) are passed into the result set.
This means that you have to have all program_id's in your programs table that you want returned in the results ALSO in your non_personnel table. They must also ALL be in your personnel_role table. And all person_ids in your personnel_role table must be in the person table. You get no results back, so this is probably not what you meant to write.
My guess is that you want to use a LEFT OUTER JOIN here. LEFT OUTER JOIN says "Take all records from one table and ONLY records from the joined table that meet the criteria in your ON statement".
Because you are wanting information based on a particular Program, chances are you want to start with that table:
SELECT person.*,
non_personnel.description,
non_personnel.amount
FROM
programs
LEFT OUTER JOIN personnel_role ON
programs.program_id = personnel_role.program_id
LEFT OUTER JOIN person ON
personnel_role.person_id = person.person_id
LEFT OUTER JOIN non_personnel
programs.program_id = non_personnel.program_id
WHERE
programs.program_name = 'Fake Program'
This is a bit of an assumption since I have no idea what your schema is or how your data is built, but I'm betting it's what you are after.
What this FROM clause says is:
1. Take all records from Program (where program_name = 'fake program') and only reocrds from personnel_role that share the same program_id
2. Take only the records from person where the person_id matches the records we just got from the personnel_role table
3. Take only the records from non_personnel where it shares a program_id with the results from the program table.

Making Report From Four Tables

I am working in Oracle APEX.I am Making report from following four tables Patient History Junction and Disease but unable to make it.
I want to SELECT
Pat_Name,Pat_Age` from Patient Table
.
Treated_By,Sys_Date from History Table
and
Dis_Name from Disease Table
.There is a Junction Table between History and Disease. Below is the diagram of the above scenario.
You need to JOIN each of the tables, similar to this:
select p.pat_name,
p.pat_age,
h.treated_by,
h.sys_date,
d.dis_name
from patient p
inner join history h
on p.pat_id = h.pat_id
and p.app_id = h.app_id
left join junction j
on p.pat_id = j.pat_id
left join disease d
on j.dis_id = d.dis_id
If you need help learning join syntax, check this helpful visual explanation of joins.
Notice that I used an INNER JOIN between patient and history and joined the tables on both keys in patient. This type of join will return all matching records in both tables.
I used a LEFT JOIN on the other two tables which will return all of the patient/history data even if there is not a matching record in the other two tables. Depending on your need, you might be able to use an INNER JOIN on those tables.

Inner join sql statement

I have two tables, Invoices and members, connected by PK/FK relationship through the field InvoiceNum. I have created the following sql and it works fine, and pulls 44 records as expected.
SELECT
INVOICES.InvoiceNum,
INVOICES.GroupNum,
INVOICES.DivisionNum,
INVOICES.DateBillFrom,
INVOICES.DateBillTo
FROM INVOICES
INNER JOIN MEMBERS ON INVOICES.InvoiceNum = MEMBERS.InvoiceNum
WHERE MEMBERS.MemberNum = '20032526000'
Now, I want to replace INVOICES.GroupNum and INVOICES.DivisionNum in the above query with GroupName and DivisionName. These values are present in the Groups and Divisions tables which also have the corresponding Group_num and Division_num fields. I have created the following sql. The problem is that it now pulls 528 records instead of 44!
SELECT
INVOICES.InvoiceNum,
INVOICES.DateBillFrom,
INVOICES.DateBillTo,
DIVISIONS.DIVISION_NAME,
GROUPS.GROUP_NAME
FROM INVOICES
INNER JOIN MEMBERS ON INVOICES.InvoiceNum = MEMBERS.InvoiceNum
INNER JOIN GROUPS ON INVOICES.GroupNum = GROUPS.Group_Num
INNER JOIN DIVISIONS ON INVOICES.DivisionNum = DIVISIONS.Division_Num
WHERE MEMBERS.MemberNum = '20032526000'
Any help is greatly appreciated.
You have at least one relation between your tables which is missing in your query. It gives you extra records. Find all common fields. Say, are divisions related to groups?
The statement is fine, as far as the SQL syntax goes.
But the question you have to ask yourself (and answer it):
How many rows in Groups do you get for that given GroupNum?
Ditto for Divisions - how many rows exist for that DivisionNum?
It would appear that those numbers aren't unique - multiple rows exist for each number - therefore you get multiple rows returned