Why does my SQLite code not work? (CS50 2020 Pset 7 - Movies) - sqlite

I've made this SQLite code in the Movies exercise of Pset 7 from CS50 2020. It's the query number 12. The objective is to find all the movies in which both Helena Carter and Johnny Depp acted:
SELECT movies.title FROM people
JOIN stars ON stars.person_id = people.id
JOIN movies ON movies.id = stars.movie_id
WHERE movies.id IN (SELECT movies.id FROM movies WHERE name == "Helena Bonham Carter") AND movies.id IN (SELECT movies.id FROM movies WHERE name == "Johnny Depp");
But this code doesn't output anything. So I got some hints in Internet and wrote another code, that works correctly:
SELECT movies.title FROM people
JOIN stars ON stars.person_id = people.id
JOIN movies ON movies.id = stars.movie_id
WHERE movies.id IN (SELECT movies.id FROM movies WHERE name == "Helena Bonham Carter") AND movies.id IN (
SELECT movies.id FROM people
JOIN stars ON stars.person_id = people.id
JOIN movies ON movies.id = stars.movie_id
WHERE name == "Johnny Depp");
I'm still confused. If you take off the second condition of the first code, it works fine, and finds all the movies in which Helena Carter acted. Why did I have to JOIN tables again in order to make a second condition for Johnny Depp?

Because each people row has exactly one name. Therefore this phrase
SELECT movies.id FROM movies WHERE name == "Helena Bonham Carter")
AND movies.id IN (SELECT movies.id FROM movies WHERE name == "Johnny Depp"
will never return a result. (As will anything that tries to select name="a" AND name="b")

Related

Referencing a past query in a loop SQLite

I want to intersect two or more results from a query from a for loop
My code takes in strings from the command line and checks the database for movies with matching actors
movie database
id | name
actor database
id | name
acting
movie_id | actor_id
i.e.
inp = sys.argv[1]
query =
'''
SELECT m.name
FROM movie m
JOIN acting ag on m.id = ag.movie_id
JOIN actor a on a.id = ag.actor_id
WHERE a.name = ?
'''
cur.execute(query, (inp,))
for tup in cur.fetchall:
print(tup)
This program would get all the movies with actor called David
./program "David'
The Secret Life of Pets
The Big Short
Concussion
A Most Violent Year
Baggage Claim
Skyfall
Drive
Albert Nobbs
The Book of Eli
Shine a Light
The Bourne Ultimatum
The Simpsons Movie
I want to extend my program to take in multiple arguments, taking in multiple names of actors, where the actors act in the same movie.
Possible code?
for index in range(len(sys.argv - 1)):
# insert code here
I think I should use an intersection of the outputs of the queries, but I don't know how to do that.
This is the output of movies that both Albert and David
./program "Albert" "David"
A Most Violent Year
Baggage Claim
The Simpsons Movie
You can use aggregation to construct a query that returns the common movies of all actors that you pass as arguments:
query = """
SELECT m.name
FROM movie m
JOIN acting ag on m.id = ag.movie_id
JOIN actor a on a.id = ag.actor_id
WHERE ',' || ? || ',' LIKE '%,' || a.name || ',%'
GROUP BY m.id, m.name
HAVING COUNT(*) = ?
"""
cur = con.cursor()
cur.execute(query, (','.join(sys.argv[1:]), len(sys.argv)-1))
for tup in cur.fetchall():
print(tup[0])

Homework Question :: Tried very hard :: Need Direction

I have been stuck on this progressive question for more than 10 days now
Questions is :: Find all the actors that made more movies with Yash Chopra than any other director
Heres my attempt
SELECT pidsWhoDidMoviesWithYashChopra.pid,
pidsWhoDidMoviesWithYashChopra.moviesWithYashChopra,
pidsOfThoseWhoDidMoviesWithDirectors.moviesByAPID,
pidsWhoDidMoviesWithYashChopra.countOfMoviesWithYashChopraByAPID,
pidsOfThoseWhoDidMoviesWithDirectors.totalNumberOfMoviesByAPID
FROM
(
SELECT TRIM(M_Cast.PID) AS pid, moviesByYashChopra.mDirectorMID AS moviesWithYashChopra, COUNT(moviesByYashChopra.mDirectorMID) AS countOfMoviesWithYashChopraByAPID
FROM M_Cast
JOIN
(
SELECT TRIM(M_Director.MID) AS mDirectorMID
FROM
M_Director
WHERE TRIM(M_Director.PID) IN
(
SELECT TRIM(Person.PID) AS personPID
FROM
Person
WHERE Person.Name LIKE '%Yash Chopra%'
)
) AS moviesByYashChopra
ON TRIM(M_Cast.MID) == moviesByYashChopra.mDirectorMID
GROUP BY pid
) AS pidsWhoDidMoviesWithYashChopra
JOIN
(
SELECT TRIM(M_Cast.PID) AS pid, TRIM(M_Cast.MID) AS moviesByAPID, COUNT(TRIM(M_Cast.MID)) AS totalNumberOfMoviesByAPID
FROM M_Cast
GROUP BY pid
) AS pidsOfThoseWhoDidMoviesWithDirectors
ON pidsWhoDidMoviesWithYashChopra.pid == pidsOfThoseWhoDidMoviesWithDirectors.pid
GROUP BY pidsWhoDidMoviesWithYashChopra.pid
And here's the output it produces
And here's the schema
Now where I require help is :: Ability to go ahead from here :: As in how do i dissect this part of the question "than any other director." :: I think that's the tricky part
Any direction/hints will be helpful, Thanks...
You need to join person (for the actor's details) with m_cast, movie, m_director and person again (for the director's details) and group by actor and director to count the number of movies each actor made with each director.
Then use window functions first_value() to get the name of the director with whom the actor made the most movies and lag() to get the 2nd max number of movies (this is needed to filter out ties).
Enclose this query inside a CTE and then filter:
with cte as (
select pa.pid, pa.name, count(*) counter,
first_value(pd.name) over (partition by pa.pid, pa.name order by count(*) desc) max_dir_name,
lag(count(*)) over (partition by pa.pid, pa.name order by count(*) desc) prev_counter
from person pa
inner join m_cast c on c.pid = pa.pid
inner join movie m on m.mid = c.mid
inner join m_director d on d.mid = m.mid
inner join person pd on pd.pid = d.pid
group by pa.pid, pa.name, pd.name
)
select pid, name, counter
from cte
where max_dir_name = 'Yash Chopra' and coalesce(prev_counter, 0) < counter

SQL Query: Select the sum(quantity) and count(number) SQLite

I am trying to query the chinnok database to select sum of quantity tracks purchase and count the number of times the track appears in a playlist in one query.
Here is what I have:
/* Query 3 : which artist has the most songs on the top 100 songs across playlist and the most sold
songs*/
SELECT ar.Name Artist_Name, tr.Name Track_Name, count(pl.Name) Play_List, pt.TrackId Track_ID,
SUM(il.Quantity) Qty,
CASE WHEN count(pl.Name)=5 THEN "Five Stars"
ELSE "Four Star" END AS Ranking
FROM Track tr
JOIN PlaylistTrack pt
ON pt.TrackId = tr.TrackId
JOIN Playlist pl
ON pl.PlaylistId=pt.PlaylistId
JOIN Album ab
ON ab.AlbumId = tr.AlbumId
JOIN Artist ar
ON ar.ArtistId = ab.ArtistId
JOIN InvoiceLine il
ON il.TrackId = tr.TrackId
GROUP BY tr.TrackId
ORDER BY Play_List DESC
LIMIT 100;
Here is the results:
Fist 6 results
First 16 results
The Qty is correct but the Play_list number is not.
Can anyone help?

Getting a min(date) AND max(date) AND their respective titles

I have three tables that I would like to select from
Table 1 has a bunch of static information about a user like their idnumber, name, registration date
Table 2 has the idnumber of the user, course number, and the date they registered for the course
Table 3 has the course number, and the title of the course
I am trying to use one query that will select the columns mentioned in table 1, with the most recent course they registered (name and date registered) as well as their first course registered (name and date registered)
Here is what I came up with
SELECT u.idst, u.userid, u.firstname, u.lastname, u.email, u.register_date,
MIN(l.date_inscr) as mindate, MAX(l.date_inscr) as maxdate, lc.coursename
FROM table1 u,table3 lc
LEFT JOIN table2 l
ON l.idCourse = lc.idCourse
WHERE u.idst = 12787
AND u.idst = l.idUser
And this gives me everything i need, and the dates are correct but I have no idea how to display BOTH of the names of courses. The most recent and the first.
And help would be great.
Thanks!!!
You can get your desired results by generating the min/max date_inscr for each user in a derived table and then joining that twice to table2 and table3, once to get each course name:
SELECT u.idst, u.userid, u.firstname, u.lastname, u.email, u.register_date,
l.mindate, lc1.coursename as first_course,
l.maxdate, lc2.coursename as latest_course
FROM table1 u
LEFT JOIN (SELECT idUser, MIN(date_inscr) AS mindate, MAX(date_inscr) AS maxdate
FROM table2
WHERE idUser = 12787
) l ON l.idUser = u.idst
LEFT JOIN table2 l1 ON l1.idUser = l.idUser AND l1.date_inscr = l.mindate
LEFT JOIN table3 lc1 ON lc1.idCourse = l1.idCourse
LEFT JOIN table2 l2 ON l2.idUser = l.idUser AND l2.date_inscr = l.maxdate
LEFT JOIN table3 lc2 ON lc2.idCourse = l2.idCourse
As #BillKarwin pointed out, this is more easily done using two separate queries.

SQL Query not showing results. The query is to list the names of all the actors who played in the movie 'Anand'

Please see the tables:
Sub-Query Method:
select p.Name from Person p where p.PID in
(select mc.PID from M_Cast mc where mc.MID in
(select m.MID from Movie m where lower(title)='anand' ))
Even join is not working:
select p.Name
from Movie m
join M_Cast mc on m.MID = mc.MID
join Person p on mc.PID = p.PID
group by m.MID
having lower(m.title)='anand'
Your first query works without errors and if you make adjustments like in my few next steps the second one will work also.
Your second query: you can not select only p.Name and group by only m.MID. If it is in select clause and is not a part of aggregate function then you have to use it in group by clause. For example like this:
select p.Name
from Movie m
join M_Cast mc on m.MID = mc.MID
join Person p on mc.PID = p.PID
group by p.Name;
Your second query also has a HAVING clause having lower(m.title)='anand' but it should be in where clause like this:
select p.Name
from Movie m
join M_Cast mc on m.MID = mc.MID
join Person p on mc.PID = p.PID
where lower(m.title)='anand'
group by p.Name;
Now that both query are working you need to check if you have a movie with title 'ANAND' in your data. Also you need to check if you have a corresponding MID and PID data in other tables.
I have tested this in MySQL but it will maybe help you even if you use other database to guide you through some mistakes... Here is a small DEMO in MySQL where you will see that data will not be returned if there is no data in one table.
Here is a small DEMO for SQLite where you can see that your first query is working:
http://sqlfiddle.com/#!7/3ec44/1
and here is a small DEMO where you can see that my code is working:
http://sqlfiddle.com/#!7/3ec44/2
Please check the data!
After I have exchanged few comments with OP I have noticed that maybe it is a blank space in data making a problem. So I suggested this:
select p.Name
from Person p
where trim(p.PID, ' ') in (select trim(mc.PID, ' ')
from M_Cast mc
where mc.MID in (select m.MID
from Movie m
where lower(title)='anand'))
This also can be implemented in the second query:
select p.Name
from Movie m
join M_Cast mc on m.MID = mc.MID
join Person p on trim(mc.PID, ' ') = trim(p.PID, ' ')
where lower(m.title)='anand'
group by p.Name;
The problem was that in the query two tables were joined with mc.PID = p.PID and one column had data with blank spaces. So the query was trying to join this data : ' 1' = '1'. TRIM function will remove all the blank spaces in the value and join will then be possible.
SELECT MOVIE.title, person.name
FROM (MOVIE INNER JOIN mcast ON MOVIE.MID = mcast.MID) INNER JOIN person ON mcast.PID = person.PID
WHERE (((MOVIE.title)="ANAND"));
select distinct p.name [Actors in Anand]
Movie m
join M_Cast mc on mc.MID=m.MID
join Person p on p.PID=mc.PID
where m.title="Anand"
order by p.name

Resources