sql query counting something across three tables - sql

I have three tables with some columns shown below:
Table 1 - airports: NAME IATA, COUNTRY
Table 2 - airlines: NAME, IATA
Table 3 - routes: AIRLINE, SOURCE AIRPORT, DESTINATION AIRPORT
I want to write an SQLite query which finds out how many countries certain airlines fly to.
Here's what I've got so far:
SELECT al.NAME AS 'Airline Name',
COUNT(r.AIRLINE) AS 'No. of Destinations'
FROM airlines as al
INNER JOIN routes r ON r.AIRLINE = al.IATA
INNER JOIN airports ap ON r.DESTINATION_AIRPORT = ap.IATA
GROUP BY ap.COUNTRY
ORDER BY [No. of Destinations] DESC;
I'm getting an incredibly wrong result. Help would be appreciated please and thank you. (those joins are the links between the tables for those who want to try and write a query for me)

There are two major problems with your query:
The SELECT columns are inconsistent with the GROUP BY.
You are not counting the countries, which seems to be what you want.
Tweaking your query fixes this problem:
SELECT al.NAME AS Airline_Name,
COUNT(DISTINCT cp.country) AS num_countries
FROM airlines al INNER JOIN
routes r
ON r.AIRLINE = al.IATA INNER JOIN
airports ap
ON r.DESTINATION_AIRPORT = ap.IATA
GROUP BY al.name;
Note: I strongly discourage you from using single quotes for column names. The simplest approach is to use column names that don't require escaping. Then only use single quotes for string and date constants.

Related

Sub Queries, Group By's, and Joins

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.

What letters are written before column titles

I'm learning SQL language using online resources, but mostly using queries that my predecessors have written at my company. I'm editing fields correspondingly to produce the correct results. But I want to understand more.
I have a few questions about this section of code.
1. Why is there a "p" before TrackingNumber, and oh/cc/im in front of others?
It seems to matter which I choose, so I just use trial and error until it runs.
2. Why do I need to have tracking number - when I delete this line, the code won't run!
select
p.TrackingNumber
,im.Sku
,oh.BusinessUnitCode
,cc.Qty
,oh.ShipCode
,oh.OrigShipCode
,oh.Store
,convert(date,oh.ShipTime) as 'OrderDate'
,oh.ShipToName
,oh.OrderNumber
from dmhost.tblOrderHeader oh
join dmhost.tblContainer c on oh.OrderHeaderID = c.OrderHeaderID
join dmhost.tblPackage p on c.ContainerID = p.ContainerID
join dmhost.tblContainerContents cc on c.ContainerID = cc.ContainerID
join dmhost.tblItemMaster im on im.ItemMasterID = cc.ItemMasterID
where (oh.ShipTime between '04/07/2019' and '05/05/2019')
The letters you talk about are referring to table names (or aliases).
Example using aliases would be:
SELECT c.customerName, o.orderNumber from Customers c
INNER JOIN Orders o on c.id=o.customerid
Same query without aliases:
SELECT Customers.customerName, Orders.orderNumber from Customers
INNER JOIN Orders on Customers.id=Orders.customerid
or omitting table names
SELECT customerName, orderNumber from Customers
INNER JOIN Orders on Customers.id=Orders.customerid
The table denomination is specially important when you retrieve columns with the same name from different table. For example id from Customers and id from Orders
SELECT c.id as CustomerId, o.id as OrderId from Customers c
INNER JOIN Orders o on c.id=o.customerid
The bits before the dot (.) in your field names are table aliases. If you look in the FROM clause of this query you should see these abbreviations in front of the various tables listed in there. They're used to
a) make it less tedious to type table names and
b) make it unambiguous which table you are selecting the column from (this both increases readability of the code and also deals with any cases where two of the tables have columns with the same name)
Here's a simple example of table alias usage:
SELECT emp.ID, emp.Name, dep.ID, dep.Name
FROM employees emp
INNER JOIN departments dep ON dep.ID = emp.DepartmentID
Here you can see that the employees and departments tables have each got aliases to shorten their name. In the query we refer to each field using it's alias. This is especially useful since both tables have fields called "ID" and "Name".
As for why it crashes when you remove p.TrackingNumber, it's likely because you did not also remove the comma (,) from the next line. The comma is used to mark where the name of the next field begins - it could be at the end of the previous line, rather than the start of the next one. Clearly you can't start the list of fields with a comma, because there is no field name preceding it - hence you get a syntax error.
The same query could have been written
select
p.TrackingNumber,
im.Sku,
oh.BusinessUnitCode,
-- etc
which might make it easier to see the usage of the comma.

Append data from table one to another - Syntax error?

I have two tables of zip code information, one without city and state fields (2016_Zips), the other is just a list (USZips_V1) of zip codes, the “Zip_Code” field is common in both tables.
I'd like to match the more complete data (with fields for city, state, latitude, longitude, etc) to the basic list of zip codes.
This way I can see which zip codes are common for which city (big cities have multiple zip codes).
This is the code I have, but returns:
Syntax error in expression 2016_Zips.Zip_Code
SELECT USZips_V1.Zip_Code, 2016_Zips.Zip_Code, USZips_V1.city, USZips_V1.state_id, 2016_Zips.lat, 2016_Zips.long, USZips_V1.imprecise, USZips_V1.military
FROM USZips_V1
INNER JOIN 2016_Zips ON USZips_V1.Zip_Code, 2016_Zips.Zip_Code;
and based it off of this:
SELECT Orders.OrderID, Customers.CustomerName, Orders.OrderDate
FROM Orders
INNER JOIN Customers ON Orders.CustomerID=Customers.CustomerID;
Here are my table headings:
This is where I want the information added:
This will be where I draw the data from to add to the table above:
Is there a better way to do what I want to do? Is there a glaring error in my statement?
My goal in doing this is to save time matching data between the tables.
This is my expected output:
Always use Explicit JOIN
SELECT u.Zip_Code, z.Zip_Code, u.city, z.state_id, z.lat, z.long, u.imprecise, z.military
FROM USZips_V1 AS u
INNER JOIN 2016_Zips AS z ON u.Zip_Code=z.Zip_Code;
you have to use = in your join instead ,
SELECT t1.*,t2.*
FROM USZips_V1 t1
INNER JOIN 2016_Zips t2 ON t1.Zip_Code= t2.Zip_Code;

Inner join (or intersect) over three tables

I have a database with three tables named: NameAddressPhone, NameAddressAge, and AgeSex.
Table NameAddressPhone has columns name, address, and phone.
Table NameAddressAge has columns name, address, and age.
Table AgeSex has columns age and sex.
I'm trying to write a (SQLite) query to find the names, addresses, and ages such that the names and addresses appear in both NameAddressPhone and NameAddressAge, and such that the ages appear in both NameAddressAgeand AgeSex. I'm able to get halfway there (i.e., with two tables) using inner join, but I only dabble in SQL and would appreciate some help from an expert in getting this right. I have seen solutions that appear to be similar, but don't quite follow their logic.
Thanks in advance.
Chris
I think you just want to join these together on their obvious keys:
select *
from NameAddressPhone nap join
NameAddressAge naa
on nap.name = naa.name and
nap.address = naa.address join
(select distinct age
from AgeSex asx
) asx
on asx.age = naa.age
This is selecting the distinct ages in the AgeSex to prevent the proliferation of rows. Presumably, one age could appear multiple times in that table, which would result in duplicate rows on output.
I am assuming your tables have the following layout
NameAddressPhone
================
Name
Address
Phone
NameAddressAge
==============
Name
Address
Age
AgeSex
======
Age
Sex
If I am understanding everything correctly, the solution might look kind of like this:
SELECT P.Name, P.Address, P.Phone, A.Age, S.Sex
FROM NameAddressPhone P
INNER JOIN NameAddressAge A ON P.Name = A.Name AND P.Address = A.Address
INNER JOIN AgeSex S ON A.Age = S.Age
Mind you, joining AgeSex could produce duplicate rows if there are multiple rows with the same age in AgeSex. There wouldn't be a way to distinguish 21 and Male from 21 and Female, for example.
I hope I can help and this is what you are looking for.

Select based on the number of appearances of an id in another table

I have a table B with cids and cities. I also have a table C that has these cids with extra information. I want to list all the cids in table C that are associated with ALL appearances of a given city in Table B.
My current solution relies on counting the number of times the given city appears in Table B and selecting only the cids that appear that many times. I don't know all the SQL syntax yet, but is there a way to select for this kind of pattern?
My current solution:
SELECT Agents.aid
FROM Agents, Customers, Orders
WHERE (Customers.city='Duluth')
AND (Agents.aid = Orders.aid)
AND (Customers.cid = Orders.cid)
GROUP BY Agents.aid
HAVING count(Agents.aid) > 1
It only works because I know right now with the HAVING statement.
Thanks for the help. I wasn't sure how to google this problem, since it's pretty specific.
EDIT: I'm pinpointing my problem a bit. I need to know how to determine if EVERY row in a table has a certain value for a field. Declaring a variable and counting the rows in a sub-selection and filtering out my results by IDs that appear that many times works, but It's really ugly.
There HAS to be a way to do this without explicitly count()ing rows. I hope.
Not an answer to your question, but a general improvement.
I'd recommend using JOIN syntax to join your tables together.
This would change your query to be:
SELECT Agents.aid
FROM Agents
INNER JOIN Orders
ON Agents.aid = Orders.aid
INNER JOIN Customers
ON Customers.cid = Orders.cid
WHERE Customers.city='Duluth'
GROUP BY Agents.aid
HAVING count(Agents.aid) > 1
What variant of SQL are you using?
To start with, you can (and should) use JOIN instead of doing it in the WHERE clause, e.g.,
select Agents.aid
from Agents
join Orders on Agents.aid = Orders.aid
join Customers on Customers.cid = Orders.cid
where Customers.city = 'Duluth'
group by Agents.aid
having count(Agents.aid) > 1
After that, I'm afraid I might be a little lost. Using the table names in your example query, what (in English, not pseudocode) are you trying to retrieve? For example, I think your sample query is retrieving the PK for all Agents that have been involved in at least 2 Orders involving Customers in Duluth.
Also, some table definitions for Agents, Orders, and Customers might help (then again, they might be irrelevant).
I'm not sure if I understood you problem, but I think the following query is what you want:
SELECT *
FROM customers b
INNER JOIN orders c USING (cid)
WHERE b.city = 'Duluth'
AND NOT EXISTS (SELECT 1
FROM customers b2
WHERE b2.city = b.city
AND b2.cid <> cid);
Probably you will need some indexes on these columns.