SQL query return name and count of specific attributes across multiple tables - sql

Given multiple tables I'm trying to write a query that returns the names that satisfies a specific count clause.
I have the tables:
genre(genre, movieid)
moviedirectors(movieid, directorid)
directors(directorid, firstname, lastname)
I want to write a query that returns the first and last name of directors that directed at least 50 movies of the genre comedy, and return that number as well.
This is what I have
select d.fname, d.lname, count(*)
from genre g, directors d, moviedirectors md
where g.genre='Comedy' and g.movieid=md.movieid and
md.directorid=d.directorid
group by d.id
having count(*) >= 50
I believe this should be correct but when I run this query on the command line it never finishes. I waited 30 minutes and got no results.

you need inner joins:
SELECT d.fname
d.lname
FROM genre g
INNER JOIN moviedirectors md
ON g.movieid = md.movieid
INNER JOIN directors d
ON md.directorid = d.directorid
WHERE g.genre = 'Comedy'
GROUP BY d.fname, -- group by columns in select
d.lname
HAVING COUNT(*) >= 50

select c.firstname,c.lastname,count(e.movieid) from (select a.* from directors a,movie b where b.genre = 'Comedy' and b.movieid=a.movieid)d,directors c where c.directorid=d.directorid group by e.movieid having count(e.movieid)>50;

Related

How to select the highest value after a count() | Sql Oracle

This is my query:
SELECT f.name, COUNT(*) as num_books
from author f
JOIN book b on b.tittle = f.book
Group by f.name
Which gives me this table:
NAME NUM_BOOKS
-------------------------------------------------- ----------
Dyremann 2
Nam mann 1
Thomas 1
Asgeir 1
Tullemann 5
Plantemann 1
Beste forfatter 1
Fagmann 5
Lars 1
Hans 1
Svein Arne 1
How could I easly alter the query to only display the author with the highest amount of released books? (While keeping in mind I'm rather new to sql)
Oracle, and as far as I know - only Oracle, allows you to nest two aggregate functions.
SELECT max (f.name) keep (dense_rank last order by count (*)) as name
from author f
JOIN book b on b.tittle = f.book
Group by f.name
In order to get ALL top authors:
select name
from (SELECT f.name,rank () over (order by count(*) desc) as rnk
from author f
JOIN book b on b.tittle = f.book
Group by f.name
)
where rnk = 1
Since Oracle 12c:
SELECT f.name
from author f
JOIN book b on b.tittle = f.book
Group by f.name
order by count (*) desc
fetch first row /* with ties (optional, in order to get all top authors) */
The best way to do is to use:
SELECT f.name, COUNT(*) as num_books
from author f
JOIN book b on b.tittle = f.book
Group by f.name
Order by num_books DESC
FETCH FIRST ROW ONLY
This will order the results from biggest to smallest and return the first result.
1) Oracle Specific : ( Using ROWNUM, For Postgres/MySql use limit )
select * from
(SELECT f.name, COUNT(*) as num_books
from author f
JOIN book b on b.tittle = f.book
Group by f.name order by num_books desc )
where ROWNUM = 1
2) General Query for all databases :
select f.name,count(*) as max_num_books from author f
JOIN book b on b.tittle = f.book
Group by f.name
having count(*) =
(select max(num_books)
from
(SELECT f.name, COUNT(*) as num_books
from author f
JOIN book b on b.tittle = f.book
Group by f.name)
);
I am not sure why you need a join in the first place. It appears that the author table has a column book - why is it not enough to count(book) from that table, grouping by name? This arrangement is very strange - the author table should only have author properties, the author name should be in the title table, but you do join on author.book = book.title which seems to suggest that you do, in fact, have that strange arrangement (and therefore you don't need a join). Also, having a table and a column (in another table) share the same name, book, is a practice best to be avoided.
The most elementary solution (not the most efficient though), in this case, is
select name, count(book) as max_num_books
from author
group by name
having count(book) = (select max(count(book) from author group by name);
The subquery groups by name, and then it selects the max over all group counts. The outer query selects the names that have a book count equal to this maximum. The subquery returns a single row in a single column - a single value. Such a query is called a "scalar" subquery and can be used wherever a single value is needed, such as the HAVING clause of the outer query. (It's in the HAVING clause and not a WHERE clause, since it refers to group properties - count(book) - and not to individual row properties).
The more efficient solution is as Dudu showed:
select name, ct as max_num_books
from ( select name, count(*) as ct, rank() over (order by count(*) desc) rnk
from author
group by name
)
where rnk = 1;

Oracle sql - referencing tables

My school task was to get names from my movie database actors which play in movies with highest ratings
I made it this way and it works :
select name,surname
from actor
where ACTORID in(
select actorid
from actor_movie
where MOVIEID in (
select movieid
from movie
where RATINGID in (
select ratingid
from rating
where PERCENT_CSFD = (
select max(percent_csfd)
from rating
)
)
)
);
the output is :
Gary Oldman
Sigourney Weaver
...but I'd like to also add to this select mentioned movie and its rating. It accessible in inner selects but I don't know how to join it with outer select in which i can work just with rows found in Actor Table.
Thank you for your answers.
You just need to join the tables properly. Afterwards you can simply add the columns you´d like to select. The final select could be looking like this.
select ac.name, ac.surname, -- go on selecting from the different tables
from actor ac
inner join actor_movie amo
on amo.actorid = ac.actorid
inner join movie mo
on amo.movieid = mo.movieid
inner join rating ra
on ra.ratingid = mo.ratingid
where ra.PERCENT_CSFD =
(select max(percent_csfd)
from rating)
A way to get your result with a slightly different method could be something like:
select *
from
(
select name, surname, percent_csfd, row_number() over ( order by percent_csfd desc) as rank
from actor
inner join actor_movie
using (actorId)
inner join movie
using (movieId)
inner join rating
using(ratingId)
(
where rank = 1
This uses row_number to evaluate the "rank" of the movie(s) and then filter for the movie(s) with the highest rating.

Query with aggregating data from 2 tables

I have three tables in my Oracle db:
Peoples:
IdPerson PK
Name
Surname
Earnings:
IdEarning
IdPerson
EarningValue
Awards:
IdAward PK
IdPerson FK
AwardDescription
A person can have many earnings.
An earning can have many or no any earnings.
A person can have many awards, one award, or no any award.
I want to make a query that will return 3 columns:
Surname
SUM of all EarningValue of person with this surname
COUNT of all Awards for this person
An important thing is that i also need to display: 0 value if person don't have any award or earning. There is a possibility that person have an award but don't have any earning.
Is it possible to make such query?
SELECT p.IdPerson,
p.Surname,
NVL(SUM(e.EarningValue), 0) as SumEarnings,
COUNT(a.IdAward) as CntAwards
FROM Peoples p
LEFT JOIN Earnings e ON p.IdPerson = e.IdPerson
LEFT JOIN Awards a ON p.IdPerson = a.IdPerson
GROUP BY p.IdPerson,
p.Surname;
What returns this:
SELECT p.Surname,
(SELECT NVL(SUM(e.EarningValue), 0)
FROM Earnings e WHERE e.IdPerson = p.IdPerson) as SumEarnings,
(SELECT COUNT(aIdAward)
FROM Awards a WHERE a.IdPerson = p.IdPerson) as CntAwards
FROM Peoples p
yes of-course it is possible.
you just need to join the tables using multiple join queries and then apply sum() function to get sum of earnings and Count() to count the no. of awards.
Try the below query:
SELECT P.Surname,
Sum(E.EarningValue)AS Total_Earnings,
Count(A.IdAward) AS total_awards
FROM Peoples P
LEFT JOIN Earnings E
ON P.IdPerson = E.IdPerson
LEFT JOIN Awards A
ON A.IdPerson = P.IdPerson
GROUP BY P.IdPerson;
Yes it is possible, you just have to join the tables
SELECT Peoples.Surname, SUM(Earnings.EarningValue) as Earnings, COUNT(Awards. IdPerson) as Awards
FROM Peoples
INNER JOIN Earnings
ON Peoples.IdPerson = Earnings.IdPerson
INNER JOIN Awards
ON Peoples.IdPerson = Awards.IdPerson
GROUP BY Peoples.IdPerson;

SQL looping through a table to count and group

I have a table like this
ID|list
1|:A:B:C:
2|:B:D:A:
3|:C:A:D:
I want to count the number of each of A,B,C,D and group them as
A|3
B|2
C|2
D|2
I was thinking about iterating through the table and inserting values in to a temporary table from count statements like the following
select count(*) from table where list like %:A:%
It does look ugly.Is there a better way to do this?
(I am using sqlite from qt)
Never, never, never store multiple values in one column! A better DB design would be
movies table
----------
id
name
release_date
...
actors table
------------
id
first_name
last_name
birth_date
...
movie_cast table
----------------
movie_id
actor_id
You could then select all actors of a specific movie like this
select a.lastName, a.first_name
from actors a
join movie_cast c on c.actor_id = a.id
join movies m on c.movie_id = m.id
where m.name = 'Citizen Kane'
Or select all movies a specific actor stars in like this
select m.name
from actors a
join movie_cast c on c.actor_id = a.id
join movies m on c.movie_id = m.id
where a.lastName ='Wayne'
and a.first_name 'John'

sql get a unique ID then count the number of tuples relating to that ID

Database Structure
MovieInfo (mvID, title, rating, year, length, studio)
DirectorInfo(directorID, firstname, lastname)
MemberInfo(username, email, password)
ActorInfo(actorID, firstname, lastname, gender, birthplace)
CastInfo(mvID*, actorID*)
DirectInfo(mvID*, directorID*)
GenreInfo(mvID*, genre)
RankingInfo(username*, mvID*, score, voteDate)
Query
I need to get the director with the largest number of comedy movies. (I'm also required to use the ALL operator). My understanding is getting the list of mvid where genre = 'Comedy" and directorid:
select mvid
from genreinfo
where genre = 'Comedy'
union all
select directorid
from directorinfo
;
But then how do I count the number of movies a specific director has? And how do I get that single one with the highest count of "comedy" movies?
You're on the right track. I'd recommend looking at JOINs.
I've provided a step-by-step answer on how to obtain the desired results. If you just want the final query, go down to step 5 and pick the one appropriate for your DBMS.
1: Selecting all comedy movie IDs:
SELECT mvid
FROM GenreInfo
WHERE genre = 'Comedy';
2: Selecting the directorIDs of those movies
SELECT directorID
FROM DirectInfo
JOIN GenreInfo
ON DirectInfo.mvID = GenreInfo.mvID
WHERE genre = 'Comedy';
3: Selecting the director name of those directors.
SELECT firstname
FROM DirectorInfo
JOIN DirectInfo
ON DirectorInfo.directorID = DirectInfo.directorID
JOIN GenreInfo
ON DirectInfo.mvID = GenreInfo.mvID
WHERE genre = 'Comedy';
4: Grouping that query by director to get number of movies:
SELECT firstname, COUNT(*) AS NumberOfMovies
FROM DirectorInfo
JOIN DirectInfo
ON DirectorInfo.directorID = DirectInfo.directorID
JOIN GenreInfo
ON DirectInfo.mvID = GenreInfo.mvID
WHERE genre = 'Comedy'
GROUP BY DirectorInfo.directorID;
5: Sort the results and get only the first one:
SELECT firstname, COUNT(*) AS NumberOfMovies
FROM DirectorInfo
JOIN DirectInfo
ON DirectorInfo.directorID = DirectInfo.directorID
JOIN GenreInfo
ON DirectInfo.mvID = GenreInfo.mvID
WHERE genre = 'Comedy'
GROUP BY DirectorInfo.directorID
ORDER BY NumberOfMovies
LIMIT 1;
If you're using SQL server, use TOP instead:
SELECT TOP 1 firstname, COUNT(*) AS NumberOfMovies
FROM DirectorInfo
JOIN DirectInfo
ON DirectorInfo.directorID = DirectInfo.directorID
JOIN GenreInfo
ON DirectInfo.mvID = GenreInfo.mvID
WHERE genre = 'Comedy'
GROUP BY DirectorInfo.directorID
ORDER BY NumberOfMovies;
You can use a join and group by to get the result.
select DirectorID,COUNT(mvid)
from DirectInfo d
inner join genreinfo g
ON d.mvid=g.mvid
where genre ='Comedy'
GROUP BY DirectorID
ORDER BY COUNT(mvid)
This is homework? Well, right now you are selecting a list of IDs, some of them representing directors, others representing movies. You notice that this is not at all what you are supposed to do, right?
What you want is a list of directors. So you select from the DirectorInfo table. You also want information about his movies (excatly: the number of movies of a certain kind). So you must join that information from MovieInfo. Now think about what else you need to glue together to get from director to their movies. Then think about how to glue in that genre criterium.
Once you have joined it all together, then you group your results. You want one record per director (instead of ane record per director and movie), so you make a group and count within that group.
I hope this helps you solve your task. Good luck!
select di.directorid, count(1) as 'no_of_comedy_movies'
from DirectorInfo di inner join join DirectInfo dri
on di.directorid = dri.directorid
inner join genreinfo gi
on gi.mvid = dri.mvid
where gi.genre = 'Comedy'
group by dri.directorID
order by no_of_comedy_movies