Use the count result from another query in where condition - sql

I wonder how to combine two of my queries.
I have these 3 tables:
movies
movie_id PK
room_id FK
rooms
room_id PK
seats INTEGER
tickets
ticket_id PK
movie_id FK
In this simplified example, a movie only plays in a room and many tickets are sold for each movie.
I want to query which movies still have seats available.
For that I need to check
(room.seats - all tickets sold for that movie) > 0
If I do this, I get the total of tickets for each movie
SELECT movie_id, COUNT(*)
FROM tickets
GROUP BY movie_id;
And I would like to use that results in this query are condition
SELECT movie_id
FROM movies
JOIN rooms ON movies.room_id = rooms.room_id
WHERE (rooms.seats - [THE COUNT OF THE OTHER QUERY]) > 0
Does anyone if it is possible to achieve that?
Thank you in advance
I don't know how to combine two queries, it would be nice to understand how to achieve it

Lets assume, if data looks something like this
select
a.movie_id,
a.seats as "TOTAL_SEATS",
count(1) as "SEATS_SOLD",
a.seats - count(1) as "AVAILABLE_SEATS",
CASE when a.seats - count(1) > 0 then 'Y' else 'N' End as "SEATS_AVAILABLE"
from
(
select
m.id as movie_id,
r.seats,
t.id
from
movie m,
rooms r,
tickets t
where
m.room_id = r.id
and m.id = t.movie_id
) a
group by
a.movie_id,
a.seats
order by
movie_id asc;
Output of the Query:
If you want movie_id's specifically, for those seats that are available, then query will be as below:
select
b.movie_id
from
(
select
a.movie_id,
a.seats as "TOTAL_SEATS",
count(1) as "SEATS_SOLD",
a.seats - count(1) as "AVAILABLE_SEATS",
CASE when a.seats - count(1) > 0 then 'Y' else 'N' End as "SEATS_AVAILABLE"
from
(
select
m.id as movie_id,
r.seats,
t.id
from
movie m,
rooms r,
tickets t
where
m.room_id = r.id
and m.id = t.movie_id
) a
group by
a.movie_id,
a.seats
) b
where
b.SEATS_AVAILABLE = 'Y';
Output of the query will be:
Queries were tested on Oracle database with the above mentioned data.

SELECT movie_id
FROM movies
JOIN rooms ON movies.room_id = rooms.room_id
LEFT JOIN (SELECT movie_id, COUNT(*) AS C_M
FROM tickets
GROUP BY movie_id) t
ON movies.movie_id = t.movie_id and (rooms.seats - t.C_M) > 0
or : use a quantifier ALL,ANY
SELECT movie_id
FROM movies
JOIN rooms ON movies.room_id = rooms.room_id
WHERE (rooms.seats -
ANY (SELECT COUNT(*) OVER(PARTITION BY movie_id)
FROM tickets
WHERE tickets.movie_id =movies.movie_id)) > 0

Related

SQL Sum and Count JOIN Multiple tables

Please help, I want to merge this two query
I have 3 Tables (places,ratings,places_image)
places
id
name
description
ratings
id
rating
place_id
user_id
places_image
id
place_id
image
These are the 2 queries:
SELECT places.*, SUM(rating) AS total_rating,COUNT(ratings.user_id) AS total_user FROM ratings, places WHERE ratings.place_id = places.id GROUP BY places.id
SELECT places.*, places_images.image FROM places, places_images WHERE places.id = places_images.place_id GROUP BY places.id
Query 1
Query 2
I tried to do this query but it give duplicate data for the aggregate function
SELECT places.id, places.name, places.description, places_images.image ,SUM(rating) AS total_rating,COUNT(ratings.user_id) AS total_user FROM places_images JOIN places ON places_images.place_id = places.id JOIN ratings ON ratings.place_id = places.id GROUP BY places.id
Query 3
How can i combine it ?
You need to use 2 Inner Join on the place_id and user_id
It'll be something like
SELECT SUM(r.rating) AS total_ratings, COUNT(r.user_id) AS total_users
FROM ratings r
INNER JOIN places p ON p.id = r.user_id
INNER JOIN places_image pi ON pi.place_id = r.place_id
GROUP BY r.id
Seems the only thing i need is to subquery it
select a.*, d.image, b.totalRating, c.totalUser from places a, ( select place_id, sum(rating) AS totalRating from ratings group by place_id ) b, ( select place_id, count(id) AS totalUser from ratings group by place_id ) c, places_images d where c.place_id = a.id and b.place_id = a.id and d.place_id = a.id GROUP BY a.id
Result
Thank you very much, GBU

sql: get a single-column table, order by column from another table

I have interconnected tables.
movies (main, parent) : id | title | year
people (child) : people_id | name | birthyear
ratings (child) : movie_id | rating | votes
stars (child) : movie_id | person_id
I need to make a query ang get a sinle column output from tables "movies-people-stars" and order that by column from the table "rating" without joining column "rating" to my output.
My code:
SELECT title from movies
where id in (select movie_id from stars
where person_id in(select id from people where name = "Chadwick Boseman"))LIMIT 5;
It returns all titles of movies where Chadwick Boseman plays. I need to order them by rating. How to do it?
Although this would never be done without a join, since it is homework, you can use a correlated subquery for the table ratings in the ORDER BY clause:
select m.title
from movies m
inner join stars s on s.movie_id = m.id
inner join people p on p.people_id = s.person_id
where p.name = 'Chadwick Boseman'
order by (select r.rating from ratings r where r.movie_id = m.id) desc
limit 5
You could also use your query and add the ORDER BY clause:
select m.title
from movies m
where m.id in (
select movie_id
from stars
where person_id in(
select id
from people
where name = 'Chadwick Boseman'
)
)
order by (select r.rating from ratings r where r.movie_id = m.id) desc
limit 5;
You need to include the column in the select list to order by that column. Order by sorts your output in the order of the column you specify. Also, why can't you use JOINs for your query like below.
SELECT m.title,d.rating
FROM movies m
JOIN stars s ON s.movie_id = m.id
JOIN people p ON p.id = s.person_id
JOIN tbl d ON d.xx = z.yy ----- JOIN the table d here and use it in select . replace z,xx and yy with actual table name and columns.
WHERE p.name = "Chadwick Boseman"
ORDER BY d.rating
LIMIT 5
updated* - It might work but not able to test as I don't have access to actual data and tables.
SELECT m.title
FROM movies m
JOIN stars s ON s.movie_id = m.id
JOIN people p ON p.id = s.person_id
WHERE p.name = 'Chadwick Boseman'
AND m.id in (SELECT top 5 movie_id
FROM ratings r
WHERE r.movie_id = m.id
ORDER BY ratings desc)

Shorten a query

I have to write a query that would calculate number of tickets purchased consisting only of movie genre of that type. At the end, I have to return movie genre and number of tickets bought for that genre. I have written a query but I was wondering if it can be made shorter and more compact?
Following is the database scheme:
movies(movieId, movieGenre, moviePrice)
tickets(ticketId, ticketDate, customerId)
details(ticketId, movieId, numOfTickets)
Here is my query:
select m.genre, count(*)
from(select t.ticketId, m.genre
from(select d.ticketId
from(select m.genre, t.ticketId
from tickets t join details d on t.ticketId =
d.ticketId join movies m on d.movieId = m.movieId
group by m.genre, t.ticketId) d
group by d.ticketId
having count(*) = 1) as t join details d on t.ticketId =
d.ticketId join movies m on d.movieId = m.movieId
group by t.ticketId, m.genre) m
group by m.genre;
This runs on a database so I am only able to post sample output:
comedy 29821
action 27857
rom-com 19663
I see no reason to use the table tickets, because the results do not filter or aggregate by ticketDate or customerID. Thus, a shorter sql is
SELECT m.moviegenre,
Sum(d.numoftickets) as SumNum
FROM details d
LEFT JOIN movies m
ON d.movieid = m.movieid
GROUP BY m.moviegenre
HAVING SumNum > 0
ORDER BY m.moviegenre
added 3/28 am
I am not sure what is meant by Duplicates?? In table = details(ticketId, movieId, numOfTickets) ??
I would expect that ticketId is unique, so what would explain duplicates?
Is the same ticketId being printed twice, repeatedly??
Determine what number of ticketId are duplicates--
SELECT ticketId, count(*) as cnt
FROM details d
GROUP By ticketId
HAVING count(*) > 1
Determine what number of "details" rows are duplicates--
SELECT ticketId, movieId, numOfTickets, count(*) as cnt
FROM details d
GROUP By ticketId, movieId, numOfTickets
HAVING count(*) > 1
Then again, it may be that table = movies(movieId, movieGenre, moviePrice) is the one with duplicates??
Determine what number of movieId are duplicates--
SELECT movieId, count(*) as cnt
FROM movies m
GROUP BY movieId
HAVING count(*) > 1
Remove duplicates from details--
SELECT m.moviegenre,
Sum(d.numoftickets) as SumNum
FROM
(Select Distinct * From details) d
LEFT JOIN movies m
ON d.movieid = m.movieid
GROUP BY m.moviegenre
ORDER BY m.moviegenre

How should I join these 3 SQL queries in Oracle?

I have these 3 queries:
SELECT
title, year, MovieGenres(m.mid) genres,
MovieDirectors(m.mid) directors, MovieWriters(m.mid) writers,
synopsis, poster_url
FROM movies m
WHERE m.mid = 1;
SELECT AVG(rating) FROM movie_ratings WHERE mid = 1;
SELECT COUNT(rating) FROM movie_ratings WHERE mid = 1;
And I need to join them into a single query. I was able to do it like this:
SELECT
title, year, MovieGenres(m.mid) genres,
MovieDirectors(m.mid) directors, MovieWriters(m.mid) writers,
synopsis, poster_url, AVG(rating) average, COUNT(rating) count
FROM movies m INNER JOIN movie_ratings mr
ON m.mid = mr.mid
WHERE m.mid = 1
GROUP BY
title, year, MovieGenres(m.mid), MovieDirectors(m.mid),
MovieWriters(m.mid), synopsis, poster_url;
But I don't really like that "huge" GROUP BY, is there a simpler way to do it?
You could do something like this:
SELECT title
,year
,MovieGenres(m.mid) genres
,MovieDirectors(m.mid) directors
,MovieWriters(m.mid) writers
,synopsis
,poster_url
,(select avg(mr.rating)
from movie_ratings mr
where mr.mid = m.mid) as avg_rating
,(select count(rating)
from movie_ratings mr
where mr.mid = m.mid) as num_ratings
FROM movies m
WHERE m.mid = 1;
or even
with grouped as(
select avg(rating) as avg_rating
,count(rating) as num_ratings
from movie_ratings
where mid = 1
)
select title
,year
,MovieGenres(m.mid) genres
,MovieDirectors(m.mid) directors
,MovieWriters(m.mid) writers
,synopsis
,poster_url
,avg_rating
,num_ratings
from movies m cross join grouped
where m.mid = 1;
I guess I don't see the problem with having several GroupBy columns. That's a very common pattern in SQL. Of course, code clarity is often in the eye of the beholder.
Check the explain plans for the two approaches; my guess is you'll get better performance with your original version since it only needs to process the movie_ratings table once. But I haven't checked, and that will be somewhat data and installation dependent.
how about
SELECT
title, year, MovieGenres(m.mid) genres,
MovieDirectors(m.mid) directors, MovieWriters(m.mid) writers,
synopsis, poster_url,
(SELECT AVG(rating) FROM movie_ratings WHERE mid = 1) av,
(SELECT COUNT(rating) FROM movie_ratings WHERE mid = 1) cnt
FROM movies m
WHERE m.mid = 1;
or
SELECT
title, year, MovieGenres(m.mid) genres,
MovieDirectors(m.mid) directors, MovieWriters(m.mid) writers,
synopsis, poster_url,
av.av,
cnt.cnt
FROM movies m,
(SELECT AVG(rating) av FROM movie_ratings WHERE mid = 1) av,
(SELECT COUNT(rating) cnt FROM movie_ratings WHERE mid = 1) cnt
WHERE m.mid = 1;

How to count number of different items in SQL

Database structure:
Clubs: ID, ClubName
Teams: ID, TeamName, ClubID
Players: ID, Name
Registrations: PlayerID, TeamID, Start_date, End_date, SeasonID
Clubs own several teams. Players may get registered into several teams (inside same club or into different club) during one year.
I have to generate a query to list all players that have been registered into DIFFERENT CLUBS during one season. So if player swapped teams that were owned by the same club then it doesn't count.
My attempts so far:
SELECT
c.short_name,
p.surname,
r.start_date,
r.end_date,
(select count(r2.id) from ejl_registration as r2
where r2.player_id=r.player_id and r2.season=r.season) as counter
FROM
ejl_registration AS r
left Join ejl_players AS p ON p.id = r.player_id
left Join ejl_teams AS t ON r.team_id = t.id
left Join ejl_clubs AS c ON t.club_id = c.id
WHERE
r.season = '2008'
having counter >1
I can't figure out how to count and show only different clubs... (It's getting too late for clear thinking). I use MySQL.
Report should be like: Player name, Club name, Start_date, End_date
This is a second try at this answer, simplifying it to merely count the distinct clubs, not report a list of club names.
SELECT p.surname, r.start_date, r.end_date, COUNT(DISTINCT c.id) AS counter
FROM ejl_players p
JOIN ejl_registration r ON (r.player_id = p.id)
JOIN ejl_teams t ON (r.team_id = t.id)
JOIN ejl_clubs c ON (t.club_id = c.id)
WHERE r.season = '2008'
GROUP BY p.id
HAVING counter > 1;
Note that since you're using MySQL, you can be pretty flexible with respect to columns in the select-list not matching columns in the GROUP BY clause. Other brands of RDBMS are more strict about the Single-Value Rule.
There's no reason to use a LEFT JOIN as in your example.
Okay, here's the first version of the query:
You have a chain of relationships like the following:
club1 <-- team1 <-- reg1 --> player <-- reg2 --> team2 --> club2
Such that club1 must not be the same as club2.
SELECT p.surname,
CONCAT_WS(',', GROUP_CONCAT(DISTINCT t1.team_name),
GROUP_CONCAT(DISTINCT t2.team_name)) AS teams,
CONCAT_WS(',', GROUP_CONCAT(DISTINCT c1.short_name),
GROUP_CONCAT(DISTINCT c2.short_name)) AS clubs
FROM ejl_players p
-- Find a club where this player is registered
JOIN ejl_registration r1 ON (r1.player_id = p.id)
JOIN ejl_teams t1 ON (r1.team_id = t1.id)
JOIN ejl_clubs c1 ON (t1.club_id = c1.id)
-- Now find another club where this player is registered in the same season
JOIN ejl_registration r2 ON (r2.player_id = p.id AND r1.season = r2.season)
JOIN ejl_teams t2 ON (r2.team_id = t2.id)
JOIN ejl_clubs c2 ON (t2.club_id = c2.id)
-- But the two clubs must not be the same (use < to prevent duplicates)
WHERE c1.id < c2.id
GROUP BY p.id;
Here's a list of players for one season.
SELECT sub.PlayerId
FROM
(
SELECT
r.PlayerId,
(SELECT t.ClubID FROM Teams t WHERE r.TeamID = t.ID) as ClubID
FROM Registrations r
WHERE r.Season = '2008'
) as sub
GROUP BY PlayerId
HAVING COUNT(DISTINCT sub.ClubID) > 1
Here's a list of players and seasons, for all seasons.
SELECT PlayerId, Season
FROM
(
SELECT
r.PlayerId,
r.Season,
(SELECT t.ClubID FROM Teams t WHERE r.TeamID = t.ID) as ClubID
FROM Registrations r
) as sub
GROUP BY PlayerId, Season
HAVING COUNT(DISTINCT sub.ClubID) > 1
By the way, this works in MS SQL.
SELECT p.Name, x.PlayerID, x.SeasonID
FROM (SELECT DISTINCT r.PlayerID, r.SeasonID, t.ClubID
FROM Registrations r
JOIN Teams t ON t.ID = r.TeamID) x
JOIN Players p ON p.ID = x.PlayerID
GROUP BY p.rName, x.PlayerID, x.SeasonID
HAVING COUNT(*) > 1