If I have a database(sakila) with multiple tables, and I want to query multiple columns that relate to each other do I need to use keywords like
SELECT city.city, actor.first_name, actor.last_name
FROM city, actor, staff, address, inventory, film_actor, store
WHERE city.city_id = address.city_id AND
address.address_id = staff.address_id AND
staff.staff_id = store.store_id AND
store.store_id = inventory.store_id AND
inventory.film_id = film_actor.film_id AND
film_actor.actor_id = actor.actor_id
or can I just select them without linking the keys together like this:
SELECT city.city, actor.first_name, actor.last_name
FROM city, actor
EDIT:
So, since I want to see which cities the actors are from, I should use an inner join because a cross join will just match every city to every actor regardless if they actually relate?
What do you mean by multiple columns that relate to each other? can you explain further, the normal way of making a select query is like this
$sql= "Select column name FROM tablename ";
or be specific like
$sql="Select column name FROM tablename Where column name LIKE '%%' ";
you can make query with related fields by making another query for example $sql, $sql2 and so on.
Yes. You will have to use a JOIN command.
Ex.
SELECT c.city, a.first_name, a.last_name
FROM city c
INNER JOIN address ad ON c.city_id = ad.city_id
INNER JOIN staff s ON ad.address_id = s.address_id
INNER JOIN store st ON s.store_id = st.store_id
INNER JOIN inventory i ON st.store_id = i.store_id
INNER JOIN film_actor fa ON i.flim_id = fa.film_id
INNER JOIN actor a ON fa.actor_id = a.actor_id
Related
"Determine if there are actors with the same first name who appeared in the same movie."
This is my task and I'm supposed to do that with subqueries and I just dont really know what else to do. I tried everything with group by, order by and having count but I just don't get to the point where I get the same first name actors with the same movie.
Maybe someone can help me? I am using Sakila Database
SELECT
a.first_name
,(a.last_name)
,a.actor_id
, f.title
FROM actor a
JOIN film_actor fa ON fa.actor_id = a.actor_id
JOIN film f ON f.film_id = fa.film_id
JOIN(SELECT b.first_name, COUNT(*)
FROM actor B
GROUP BY b.first_name
HAVING COUNT(*) > 1 ) b
ON a.first_name = b.first_name
GROUP BY a.last_name
HAVING COUNT(f.title) > 1
ORDER BY a.first_name
You can do this with joins only:
select f.title, a1.first_name, a1.last_name as last_name_1, a2.last_name as last_name_2
from film f
inner join film_actor fa1 on fa1.film_id = f.film_id
inner join film_actor fa2 on fa2.film_id = f.film_id
inner join actor a1 on a1.actor_id = fa.actor_id
inner join actor a2 on a2.actor_id = fa.actor_id
where a1.first_name = a2.first_name and a1.actor_id < a2.actor_id
Starting from the film table, this follows the relationships to actor through film_actor twice, and then filters on diffrerent actors that have the same first name.
As a result, you get tuples of actors that have the same last name and played in the same film. The inequality condition ensures that there are no "mirror" records (that is, each tuple appears only once per film).
I would simply use aggregation:
SELECT fa.film_id, a.first_name,
GROUP_CONCAT(a.last_name) as last_names,
GROUP_CONCAT(a.actor_id) as actor_ids
FROM actor a JOIN
film_actor fa
ON fa.actor_id = a.actor_id
GROUP BY fa.film_id, a.first_name
HAVING COUNT(*) > 1;
Your question doesn't specify what the result set should look like. This returns one row per actors with the same first name in a film. The last names are concatenated into a string as are the actor ids.
How about this:
SELECT f.title, f.film_id, a.first_name, a.last_name, a.actor_id
FROM actor a
JOIN film_actor fa ON fa.actor_id = a.actor_id
JOIN film f ON f.film_id = fa.film_id
WHERE a.first_name IN (
SELECT a2.first_name
FROM actor a2
JOIN film_actor fa2 ON fa2.actor_id = a2.actor_id
JOIN film f2 ON f2.film_id = fa2.film_id
WHERE a2.actor_id <> a.actor_id AND f2.film_id = f.film_id
)
ORDER BY f.title ASC, a.last_name ASC, a.first_name ASC
Explaining query step-by-step
SELECT the needed fields from the joined tables
JOIN the necessary tables
WHERE (here is the subquery) a.first_name is in the set of:
firstnames of actors, different than the current actor (a2.actor_id <> a.actor_id) and the film is the same (f2.film_id = f.film_id)
The subquery in where is a similar select with joins query as the parent query.
PS:
One can do variations on this basic query template:
Eg film_id can be given as parameter, so one can find all actors with same name for specific film.
Also one can group and count how many actors appeared in same film wih same name eg by grouping on film_id and counting.
One can even optimise a bit the query by removing unnecessary joins (eg film.title may not be needed at all) and so on..
The advantage of having single results returned (instead of tuples or aggregates) is that number of actors with same name in same film is not fixed and manipulating the results, eg by grouping and counting or getting further info for each actor, is easier.
The price is a slightly more complex and potentialy slower query.
I have 4 different tables:
student: sid, name, gender
grades: sid, grade
address: postcode, sid, distance_to_school
health_condition: sid, health_code
Now I want to make a new table that contains all of the columns that I mentioned above. I need to join them based on the sid. I tried to add columns separately but the code is really long. So is there another way to do this task?
If you want to show the data from the fifth table that you want to create, I think with the query is better, so you don't need the fifth table. Just
"SELECT * FROM student s
INNER JOIN grades g
on s.sid = g.sid
INNER JOIN address a
on s.sid = a.sid
INNER JOIN health_condition h
on s.sid = h.sid
ORDER BY sid ASC";
Please use below query will give you the exact result set.
SELECT t1.sid, t1.name, t1.gender , t2.grade,t3.postcode, t3.distance_to_school,
t4.health_code AS
student as t1 inner join grades t2 on t1.sid=t2.sid
inner join address as t3 on t3.sid= t1.sid
inner join health_condition as t4 on t4.sid=t1.sid
Try this query
FROM student s
LEFT JOIN grades g on g.sid = s.sid
LEFT JOIN address a on a.sid = s.sid
LEFT JOIN health_condition h on h.sid = s.sid
WHERE s.sid = ;
I have a question, I am trying to list all of the actors for each movie in my database. Right now I have this SQL
SELECT title, count(1) COUNT, actor.first_name, actor.last_name
FROM film
INNER JOIN film_actor ON film.film_id = film.actor.film_id
INNER JOIN actor ON film_actor.actor_id = actor.actor_id
GROUP BY title.
What I am trying to do is format my out put into a text file like this:
Title:Number of Actors in each film: Actor first name(1): Actor last name(2) : Actor first name(2): Actor last name(2) ... ect
But for example, in the first film there are ten actots for this film. How would I print out the first and last name of each actor for each movie.
The problem is you want aggregate and non-aggregate data.
One way to achieve this in MySQL you would have to get the count of actors in each film as a sub query and join back to the base set.
SELECT title, b.cnt COUNT, actor.first_name, actor.last_name
FROM film
INNER JOIN film_actor
ON film.film_id = film_actor.film_id
INNER JOIN actor
ON film_actor.actor_id = actor.actor_id
INNER JOIN (SELECT film_ID, count(1) as cnt
FROM film_actor
GROUP BY film_ID) B
on film.film_ID = B.Film_ID
in databases which support window functions you could use a count(1) over (parttition by film.film_ID) as cnt
It may also be able to be achieved by using a group by with rollup without using an added join; but this creates an additional row with the total.
SELECT title, count(1) COUNT, actor.first_name, actor.last_name
FROM film
INNER JOIN film_actor
ON film.film_id = film_actor.film_id
INNER JOIN actor
ON film_actor.actor_id = actor.actor_id
on film.film_ID = B.Film_ID
GROUP BY title with rollup
I'm working with the Sakila sample database, and trying to get the most viewed film per country. So far I've managed to get the most viewed film of a certain country given its id with the following query:
SELECT
F.title, CO.country, count(F.film_id) as times
FROM
customer C
INNER JOIN
address A ON C.address_id = A.address_id
INNER JOIN
city CI ON A.city_id = CI.city_id
INNER JOIN
country CO ON CI.country_id = CO.country_id
INNER JOIN
rental R ON C.customer_id = R.customer_id
INNER JOIN
inventory I ON R.inventory_id = I.inventory_id
INNER JOIN
film F ON I.film_id = F.film_id
WHERE
CO.country_id = 1
GROUP BY
F.film_id
ORDER BY
times DESC
LIMIT 1;
I supose that I'll have to use this query or something similar in the FORM of another query, but I've tried it all I could think and am completely unable to figure out how to do so.
Thanks in advance!
I admit, this is a hell of a query. But well, as long as it works.
Explanation:
Subquery: almost the same as you already has. Without the WHERE and LIMIT. Resulting in a list of movie-count per country
Result of that, grouped per country
GROUP_CONCAT(title ORDER BY times DESC SEPARATOR '|||'), will give ALL titles in that 'row', with the most-viewed title first. The separator doesn't matter, as long as you are sure it will never occurs in a title.
SUBSTRING_INDEX('...', '|||', 1) results in the first part of the string until it finds |||, in this case the first (and thus most-viewed) title
Full query:
SELECT
country_name,
SUBSTRING_INDEX(
GROUP_CONCAT(title ORDER BY times DESC SEPARATOR '|||'),
'|||', 1
) as title,
MAX(times)
FROM (
SELECT
F.title AS title,
CO.country_id AS country_id,
CO.country AS country_name,
count(F.film_id) as times
FROM customer C INNER JOIN address A ON C.address_id = A.address_id
INNER JOIN city CI ON A.city_id = CI.city_id
INNER JOIN country CO ON CI.country_id = CO.country_id
INNER JOIN rental R ON C.customer_id = R.customer_id
INNER JOIN inventory I ON R.inventory_id = I.inventory_id
INNER JOIN film F ON I.film_id = F.film_id
GROUP BY F.film_id, CO.country_id
) AS count_per_movie_per_country
GROUP BY country_id
Proof of concept (as long as the subquery is correct): SQLFiddle
I have a database with the tables:
Student(SID,Name,Surname,Age)
Registration(StudentID,CourseID)
Course(CID,Name,Cost)
I would like to extract only the name of the courses with students younger than 20. Will the query below do just that?
SELECT C.NAME
FROM Course C
INNER JOIN Registration
INNER JOIN Student S
WHERE CID = CourseID
AND SID = StudentID
AND Age < 20
GROUP BY C.NAME
I would also like to extract the number of students in each course having students younger than 20. Is it correct to do it as below?
SELECT count(S.NAME)
,C.NAME
FROM Student S
INNER JOIN Course C
INNER JOIN Registration
WHERE Age < 20
AND CID = CourseID
AND SID = StudentID
GROUP BY C.NAME
You are missing the ON part for the join otherwise it would just be a CROSS JOIN.
Your first query should look like this if you want just a distinct list of student names:
SELECT DISTINCT C.NAME
FROM Course C
INNER JOIN Registration R ON C.CID = R.CourseID
INNER JOIN Student S ON R.StudentID = S.SID
WHERE Age < 20
Your second query shouldn't really have the C.Name in the select if you want to get just a count unless you want a count of how many students have that name.
SELECT count(*)
FROM Student S
INNER JOIN Registration R ON s.SID = R.StudentID
INNER JOIN Course C ON c.CID = R.CourseID
WHERE Age < 20
GROUP BY C.NAME
First join these tables, then group by Course's PK(CID), Add the HAVING condition to filter the course which has students younger than 20.
Then use Course table to join the result to get the course name and count of students in the course.
SELECT
T1.Name,
T2.StudentCount
FROM
Course T1
INNER JOIN (
SELECT
c.CID,
COUNT(s.SID) AS StudentCount
FROM
Course c
LEFT JOIN Registration r ON c.CID = r.CourseID
LEFT JOIN Student s ON s.SID = r.StudentID
GROUP BY c.CID
HAVING COUNT(IF(s.Age < 20, 1, NULL)) > 0
) T2 ON T1.CID = T2.CID
More correctly, you should move the conditions of the join, to the join statements themselves by including them in the on clause instead of the where. While the results may not change in this instance, if you were to start including outer joins you would encounter difficulties.
SELECT count(S.NAME)
,C.NAME
FROM Student S
INNER JOIN Registration R
ON s.SID = R.StudentID
INNER JOIN Course C
ON c.CID = R.CourseID
WHERE Age < 20
GROUP BY C.NAME
There's a fiddle here showing it in action: http://sqlfiddle.com/#!9/c3b8f/1
Your first query will also produce the results you want, but again, you should move the join predicates to the join itself. Also, you don't need to perform the grouping just to get distinct values, mysql has an expression for that called distinct. So rewritten, the first query would look like:
SELECT DISTINCT C.NAME
FROM Student S
INNER JOIN Registration R
ON s.SID = R.StudentID
INNER JOIN Course C
ON c.CID = R.CourseID
WHERE Age < 20.
Again, the results are the same as what you have already but it is easier to 'read' and will put you in good stead when you move on to other queries. As it stands you have mixed implicit and explicit join syntax.
This fiddle demonstrates both queries: http://sqlfiddle.com/#!9/c3b8f/4
edit
I may have misinterpreted your original question - if you want the total number of students enrolled in a course with at least one student under 19, you can use a query like this:
select name, count(*)
from course c
inner join registration r
on c.cid = r.courseid
where exists (
select 1
from course cc
inner join registration r
on cc.cid = r.courseid
inner join student s
on s.sid = r.studentid
where cc.cid = c.cid
group by cc.cid
having min(s.age) < 20
)
group by name;
Again with the updated fiddle here: http://sqlfiddle.com/#!9/c3b8f/17