Sub Queries, Group By's, and Joins - sql

I am new to sql and am trying to complete an assignment for a class where were practicing using subqueries and joins.
The question I'm struggling with is: Provide a list of the airport city names and the travelers (last name) who have traveled to each airport via a flight.
Here are the tables in the database:Database Tables
Here is what I have so far:
SELECT Airport.CityName
FROM AIRPORT
GROUP BY AirportID
INNER JOIN FLIGHT
ON FLIGHT.AirportID = AIRPORT.AirportID
INNER JOIN TRAVELER
ON TRAVELER.TravelerID = FLIGHT.TravelerID
SELECT TravLastName
FROM TRAVELER
but I'm getting an error on the first "Inner" and I know I'm probably nowhere close to being right. Any help would be appreciated.

The joins look fine but there should only be one select at the beginning and the group by should come at the end
SELECT
Airport.CityName,
TRAVELER.TravLastName
FROM AIRPORT
INNER JOIN FLIGHT
ON FLIGHT.AirportID = AIRPORT.AirportID
INNER JOIN TRAVELER
ON TRAVELER.TravelerID = FLIGHT.TravelerID
GROUP BY
Airport.CityName,
TRAVELER.TravLastName;
Or, as we are not using any aggregate functions we can use DISTINCT. It is simpler and can run quicker.
SELECT DISTINCT
Airport.CityName,
TRAVELER.TravLastName
FROM AIRPORT
INNER JOIN FLIGHT
ON FLIGHT.AirportID = AIRPORT.AirportID
INNER JOIN TRAVELER
ON TRAVELER.TravelerID = FLIGHT.TravelerID;

You don't want GROUP BY, you want DISTINCT:
SELECT DISTINCT
AIRPORT.CityName,
TRAVELER.TravLastName
FROM AIRPORT
JOIN FLIGHT ON FLIGHT.AirportID = AIRPORT.AirportID
JOIN TRAVELER ON TRAVELER.TravelerID = FLIGHT.TravelerID
Some tidy ups:
INNER is the default join type, so you can leave it out
DISTINCT means remove duplicates
FROM starts the list of tables to be joined. You can't add tables to the query in other places
A further tidy up would be to use table aliases, which rename the table in the context of the query - often using just the first letter of the table, to make the query smaller overall:
SELECT DISTINCT
a.CityName,
t.TravLastName
FROM AIRPORT a
JOIN FLIGHT f ON f.AirportID = a.AirportID
JOIN TRAVELER t ON t.TravelerID = f.TravelerID
The keyword AS may optionally be put between a table and its alias, eg FROM AIRPORT AS a.

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.

Trying to figure out a sql query to fetch the right data from multiple tables

So I have a database and I have 4 tables (admin, appointment, doctor, patient).
I only care about appointment, doctor and patient.
Diagram of these 3 tables:
What I want is based on a patients amka(int) to find all his appointments and show his name, surname and doctors name, surname.
I made this query:
(SELECT doctor.name, doctor.surname
FROM public.doctor
INNER JOIN public.appointment ON doctor.username = 'alouisot2')
union
(SELECT patient.name, patient.surname
FROM public.patient
INNER JOIN public.appointment ON patient.amka = '713783001');
The problem with this query is that it will only show appointments between this specific doctor and I just want to get all the doctors names and surnames.
Also I use postgresql.
I was trying to figure it out but I couldn't. I need to restructure the query but I cannot think how I would solve this problem.
If you have any idea on how to do achieve this I would really appreciate it.
Try this - properly join the three tables together (doctor to appointment based on the common username column, appointment to patient on the common amka column), and define the WHERE clause with your desired values:
SELECT
doctor.name, doctor.surname,
patient.name, patient.surname
FROM
public.doctor
INNER JOIN
public.appointment ON doctor.username = appointment.username
INNER JOIN
public.patient ON appointment.amka = patient.amka
WHERE
doctor.username = 'alouisot2'
AND patient.amka = '713783001';
this query just needs simple joins; you seem to have overcomplicated it a lot for some reason...
SELECT
patient.name,
patient.surname,
doctor.name,
doctor.surname
FROM public.patient
INNER JOIN public.appointment
ON patient.amka = appointment.amka
INNER JOIN public.doctor
ON appointment.username = doctor.username
WHERE patient.amka = '713783001'

On an SQL Select, how do i avoid getting 0 results if I want to query for optional data in another table?

I have a table with Customers which includes their contact person in the helpdesk. I have another table that lists all vacancies of the helpdesk employees - if they are currently sick or on vacation etc.
I need to get the helpdesk contact and the start/end time of their vacation IF there is an entry.
I currently have this (simplified):
SELECT *
FROM dbo.Customers, dbo.Projects, dbo.Vacations
WHERE ($Phone = dbo.Customers.Phone)
AND dbo.Customers.CustomerID = dbo.Projects.CustomerID
AND dbo.Projects.HDContactID = dbo.Vacations.HDContactID
So if there is a vacation listed in the Vacations table, it works fine, but if there is no vacation at all, this will not return anything - what i want is that if there is no vacation, it simply returns the other data, and ignores the missing data (returns NULL, doesn't return anything, not important)
In any case, I need to get the Customers and Project data, even if the query can't find an entry in the Vacations table. How would I do this? I pretty new to SQL and couldn't find a similar question on this site
EDIT: I'm using SQL Server, currently using HeidiSQL
Try below query:
SELECT * FROM dbo.Customers, dbo.Projects
left join dbo.Vacations on dbo.Projects.HDContactID = dbo.Vacations.HDContactID
WHERE ($Phone = dbo.Customers.Phone)
AND dbo.Customers.CustomerID = dbo.Projects.CustomerID
Use left join as mentioned by #Flying Thunder,
Example of the left join:
SELECT country.country_name_eng, city.city_name, customer.customer_name
FROM customer
LEFT JOIN city ON customer.city_id = city.id
LEFT JOIN country ON city.country_id = country.id;
You can find a nice guide for the joins and SQL here:
https://www.sqlshack.com/learn-sql-join-multiple-tables/
You should be using LEFT JOIN. In fact, you should never be using commas in the FROM clause. That is just archaic syntax and closes the powerful world of JOINs from your queries.
I also recommend using table aliases that are abbreviations of table names. The best are abbreviations for the table names:
SELECT *
FROM dbo.Customers c LEFT JOIN
dbo.Projects p
ON c.CustomerID = p.CustomerID LEFT JOIN
dbo.Vacations v
ON p.HDContactID = v.HDContactID
WHERE c.Phone = $Phone;
Have you try this to skip vacation record if not present like this:
SELECT * FROM dbo.Customers, dbo.Projects, dbo.Vacations
WHERE ($Phone = dbo.Customers.Phone)
AND dbo.Customers.CustomerID = dbo.Projects.CustomerID
AND (dbo.Vacations.HDContactID IS NULL OR dbo.Projects.HDContactID = dbo.Vacations.HDContactID)

Getting repeats of output, possibly doing a join wrong?

I'm having an issue where I'm getting some incorrect values in my output. I am binding the below highlighted table column with the circled column down the bottom. The service_id on the highlighted column is what is unique, but I need to bind the booking_id to retrieve the info (if that makes sense. What I end up getting is the top table where I get repeats, or the price is wrong. I should be getting only 5 lines in the top table.
Here's my code. I suspect I might be doing the join wrong?
SELECT bad.agent as Agents,
dog.SUPPLIER as SUPPLIER,
bad.status as TheStatus,
country.analysis_master1 as Country,
ftb.booking_actual_retail as BookingActualRetail,
ftb.Booking_Actual_Cost as BookingCost,
ftb.Booking_Actual_Retail_inc as BookingRetailINC,
fts.Service_Id,
fts.Service_Actual_Retail_inc as ServiceActualCostInc,
Product.SERVICE,
Product.SL_STATUS as SLSTATUS,
cat.name as Product2,
bad.LAST_SERVICE_DATE as Servicedate,
bad.LW_DATE as LWDATE,
ftb.Entered_Date as DATEENTERED,
ftb.Booking_Pax as PEOPLE,
ftb.Booking_Children as KIDS,
bad.TRAVELDATE as TRAVELDATE,
bad.FULL_REFERENCE
from BHD bad
inner join FTB on bad.FULL_REFERENCE = ftb.booking_reference
inner join FTS on FTB.Booking_Id = fts.Booking_Id
inner join DRM Country on bad.agent = country.code
inner join BSL Product on bad.BHD_ID = Product.BHD_ID
inner join SRV cat on Product.SERVICE = cat.CODE
inner join OPT dog on Product.OPT_ID = dog.OPT_ID
where bad.STATUS = 'IV' AND bad.FULL_REFERENCE = 'LTIT129488'
UPDATE:
Ok, so it looks like this join here causes the multiple outputs:
inner join FTS on FTB.Booking_Id = fts.Booking_Id
I have included the two tables, their headers, and sample data
You have somewhere put the join for the service or supplier in the wrong way.. Please check this line again.
inner join SRV cat on Product.SERVICE = cat.CODE
UPDATED SOLUTION :
As per your updated screenshots, I found the issue...
you have joined like this.
inner join FTB on bad.FULL_REFERENCE = ftb.booking_reference
In this join, your one table has single record against booking reference while another have multiple records against booking refrence. Thats why you are getting the multiple records in the output.
Remove this join and your problem will be solved. If you really want the data from this table then you can select in other way like using outer apply etc.

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.