Stuck on beginner SQL practice. Multiple table where columns use same id - sql

I'm very sorry to bother with minor problem, but I tried to search old answers for this one and since my skills in SQL are complete 0, I didn't even understand the answers :/! Neither is my English terminology great enough for properly searching.
I have these 2 tables: Cities and Flights.
Cities
+----+-------------+
|id | name |
+----+-------------+
|1 | Oslo |
|2 | New York |
|3 | Hong Kong |
+----+-------------+
Flights
+----+--------------------+-------------------+
|id | wherefrom_id | whereto_id |
+----+--------------------+-------------------+
|1 | 3 | 2 |
|2 | 3 | 1 |
|3 | 1 | 3 |
+----+--------------------+-------------------+
Now I have to write code where I need to make city ID's merge to wherefrom_id and whereto_id, in that manner that the answer shows table where you can see list of Flights (FROM/TO).
Example:
ANSWER:
+-----------+----------------+
|HONG KONG | NEW YORK |
+-----------+----------------+
|HONG KONG | OSLO |
+-----------+----------------+
|OSLO | HONG KONG |
+-----------+----------------+
This is what I wrote:
SELECT C.name, C.name
FROM Cities C, Flights F
WHERE C.id = F.wherefrom_id AND C.id = F.whereto_id;
For some reason this doesnt seem to work and I get nothing showing on my practice program. There is no error or anything it just doesnt show anything on the test answer. I really hope you get what I mean, English is not my first language and I truly tried my best to make it clear as possible :S

First things first - it's a lot easier to code in standard SQL join syntax. Converting your above to that is
SELECT C.name, C.name
FROM Cities C
INNER JOIN Flights F ON C.id = F.wherefrom_id AND C.id = F.whereto_id;
The question you've been asked requires logic people don't usually use at first so it can be confusing the first time you encounter it.
I will run through the logic jump in a moment.
Imagine your Flights table has the City names in it (not IDs).
It would have columns, say, FlightID, From_City_Name, To_City_Name.
An example row would be 1, 'Oslo', 'Prague'.
Getting the data for this would be easy e.g., SELECT Flight_ID, From_City_Name, To_City_name FROM Flights.
However, this has many problems. As your question has done, you decide to pull out the cities into their own reference tables.
For this first example, however, you decided to have two extra tables as reference tables: From_City and To_City. These would both have an ID and city name. You then change your Flights to refer to these.
Your code would look like
SELECT F.ID, FC.Name AS From_City, TC.Name AS To_City
FROM Flights
INNER JOIN From_City AS FC ON Flights.From_City_ID = FC.ID
INNER JOIN To_City AS TC ON Flights.To_City_ID = TC.ID
Notice how there are two joins there - one to From_City and one to To_City? That is because the From and To cities are referring to different things in the data.
So, then the final part of the issue: why have two city tables (from and to). Why not have one? Well, you can. If you create just one table, and modify the above, you get something like this:
SELECT F.ID, FC.Name AS From_City, TC.Name AS To_City
FROM Flights
INNER JOIN City AS FC ON Flights.From_City_ID = FC.ID
INNER JOIN City AS TC ON Flights.To_City_ID = TC.ID
Note that all that has changed is that the From_City and To_City references have been pointed to a different table City. However, the rest is the same.
And that, actually, would be your answer. The complex part that most people don't get to straight away, is having two joins to the same table.
As an aside, your original code is technically valid.
SELECT C.name, C.name
FROM Cities C
INNER JOIN Flights F ON C.id = F.wherefrom_id AND C.id = F.whereto_id;
However, what it's effectively saying is to get the city names where the From_City is the same as the To_City - which is obviously not what you want (unless you're looking for turnbacks).

What you're doing is an old SQL way of expressing joins. The standard now has better ways to declare the relationships within the from clause and I take it that your material has postponed that slightly:
There are people who will yell at you for using this ancient syntax but the answer is easy enough:
SELECT C1.name, C2.name
FROM Cities C1, Cities C2, Flights F
WHERE C1.id = F.wherefrom_id AND C2.id = F.whereto_id
You can think of this as creating a "cross product" of all city-pair combinations and matching up the ones that match actual flights. The key is to references Cities twice by using different aliases (or correlation names.)

I think this is what you are looking for..
SELECT wf.name "wherefrom", wt.name "whereto"
FROM Flights f
JOIN Cities wf
ON f.wherefrom_id = wf.id
JOIN Cities wt
ON f.whereto_id = wt.id
order by f.id

Related

SQL query without join

I have the following tables
Table food Table Race Table animal
+------------+--------------+ +------------+--------------+ +------------+--------------+
| Quantity | animal_id | | race_code | race_name | | animal_id | race_code |
+------------+--------------+ +------------+--------------+ +------------+--------------+
I was asked to calculate the average food quantity for every race (race_name). The challenge here is that I should not use JOIN because we have not studied it yet.
I have written the following query:
select AVG(f.quantity),r.race_name from food f, race r
group by r.race_name;
but it doesn't work as I want it to be since it returns the same average food quantity for all races. I know I have to use the animal table to link the other 2 but I didn't know how. I should be using subqueries
That question is exactly the same as your previous, where you had to use SUM (instead of AVG). No difference at all.
Ah, sorry - it wasn't you, but your school colleague, here
Saying that you "didn't learn joins", well - what do you call what you posted here, then? That's a cross join and will produce Cartesian product, once you fix the error you got by not including non-aggregated column into the group by clause and include additional joins required to return desired result.
The "old" syntax is
select r.name,
avg(f.quantity) avg_quantity
from race r, animal a, food f
where a.race_code = r.race_code
and f.animal_id = a.animal_id
group by r.name;
What you "didn't learn yet" does the same, but looks differently:
from race r join animal a on a.race_code = r.race_code
join food f on f.animal_id = a.animal_id
The rest of the query remains the same.
Nowadays, you should use JOINs to join tables, and put conditions into the WHERE clause. For example, condition would be that you want to calculate averages for donkeys only. As you don't have it, you don't need it.
You still have to do some matching of related rows. If not explicitly with JOIN you can do it in the WHERE clause. Ie something like
select AVG(f.quantity),r.race_name
from food f, race r, animal a
where f.animal_id = a.animal_id and a.race_code = r.race_code
group by r.race_name;
select race_name ,(select avg(quantity) from food where animal_id in (select animal_id from animal a where r.race_code = a.race_code))
from race r

Joining/Querying Self-Referential "Grandchildren" Tables

Say I have a table as following:
countries
| ID | Name | Population | Continent |
Countries is self-referential, and has an association table:
alliances
| country_id | ally_id |
I understand that I need to use the 'AS' keyword to join the table, say, as c1, c2, etc. But I can't quite wrap my head around how to go about this for grandchildren, great grandchildren, etc.
How would I write SQL, for example, to get the countries where they have an ally' who's ally's population is greater than 50 000 000?
I'm generating this SQL based off of models defined in code, so need to be able to support this kind of behaviour up to a user-defined depth.
Thanks!
The query for your example is
SELECT c1.name
FROM countries AS c1
JOIN alliances AS a ON c1.id = a.country_id
JOIN countries AS c2 ON a.ally_id = c2.id
WHERE c2.population > 50000000;
If this was a homework question (and it looks like one) you have found the sucker who does it for you.

Aliases for 2 joins on one table in Microsoft Access

I have a table that shows relationships between items and another table with the items themselves:
articles_to_articles
-------------------------
|articleID_1|articleID_2|
-------------------------
|12345 |67890 |
|23442 |343243 |
-------------------------
articles
-----------------------------------------------------
|article_id | article_name|lots | of | other | stuff|
-----------------------------------------------------
I am attempting to generate a file with that consists of the relationships from articles_to_articles but with the names in addition to the ids.
What I have so far is:
SELECT
a2a.articleID_1,
key_articles.article_name,
a2a.articleID_2,
val_articles.article_name
FROM
articles_to_articles a2a
INNER JOIN
articles key_articles
ON key_articles.articleID = articles_to_articles.articleID_1
INNER JOIN
articles val_articles
ON val_articles.articleID = articles_to_articles.articleID_2;
Access gives me a "missing operator" error but I can't seem to find the missing operator. What basic thing am I missing?
When joining more than two tables in MS Access, you must enclose each join within separate groups of parentheses, for example:
SELECT
a2a.articleID_1,
key_articles.article_name,
a2a.articleID_2,
val_articles.article_name
FROM
(
articles_to_articles a2a
INNER JOIN
articles key_articles
ON
key_articles.articleID = a2a.articleID_1
)
INNER JOIN
articles val_articles
ON
val_articles.articleID = a2a.articleID_2

Selecting more after group-by while using join

At the moment I am busy with two tables, Students and Classes. These two both contain a column project_group, a way to categorize multiple students from one class into smaller groups.
In the Students table there is a column City that states in which town/city students live, from the rows that have been filled there are already several cities occurring multiple times. The code I used to check how many times a city is being showed is this:
SELECT City, count(*)
FROM Students
GROUP BY City
Now the next thing I want to do is show per class in which cities the students live and how many live there, so for example a result like:
A | - | 2
A | New York | 3
A | Los Angeles | 1
B | - | 1
B | Miami | 2
B | Seattle | 1
Students and Classes can join each other on the column project_group but what I'm mostly interested in his using both the GROUP BY mentioned earlier, using the JOIN and also showing the results per class.
Thanks in advance,
KRAD
I'm not sure what the column name is for A and B in your example. I'm assuming Classes.Class in the following:
SELECT
C.Class
, S.City
, COUNT(S.*) AS Count
FROM
Classes AS C INNER JOIN
Students AS S ON C.Project_Group = S.Project_Group
GROUP BY
C.Class
, S.City
I managed to get it working. While doing some tests to see which exact error message it was that I got, I used this and managed to get it working. I now get an overview per class that shows how many people live in which city. This is the code used.
SELECT class_id, city, count(*) AS amount
FROM students, classes
WHERE students.project_group = classes.project_group
GROUP BY class_id, city
ORDER BY class_id

Triple joins with SQL?

My database represents a library. Each book is tagged with multiple things, so that one title might be tagged 'science fiction', 'short stories', and 'Russian'.
There are three tables: books, tags, and books_tag_link. They look like this:
Books
ID | TITLE
-----------------------------
1 | Rendezvous With Rama
2 | Howl and Other Poems
3 | A Short History of Nearly Everything
Tags
ID | TAGNAME
-----------------------------
1 | science fiction
2 | fiction
3 | poetry
Books_Tag_Link
BOOK | TAG
-----------------------------------
1 | 1
1 | 2
2 | 3
Hopefully you can see how that would work. The books_tag_link table has two foreign keys, and links books to tags; each book has many tags, each tag is associated with many books. I don't know if this is the best way to do it but it's what the OSS library program Calibre does, and that's what I'm kind of using as a reference as I study.
Now what I want to do is say "select all fiction books". But I can't quite work out the proper way to express that thought in SQL. Select books.title where books.id = tags.id = books_tag_link.tag... or something. I'm not sure.
Can someone help me out with a tip or explanation of what I should be doing?
I'm using SQLite at the moment but MySQL-specific advice would be fine too.
SELECT b.title
FROM Books AS b
JOIN Books_Tag_Link AS bt ON b.id = bt.book
JOIN Tags AS t ON t.id = bt.tag
WHERE t.tagname = 'fiction'
Something like that?
select b.title
from Books b join Books_Tag_Link btl on btl.BOOK=b.ID
join Tags t on t.ID=btl.TAG
where t.TAGNAME='fiction';
Caveat: if all tables are large, you have to make sure that the fields mentioned in JOIN are keys (indexes).
You need to use two joins:
select
books.title
from
books
inner join
books_tag_link on
books_tag_link.book = book.id
inner join
tags on
tags.id = books_tag_link.tag
where
tag.tagname = 'fiction'