How to Select Max Value From a View PosgreSQL - sql

I am new at SQL and I am trying to select a max value from a view. The database is of movies and actors, and the nested query part works. I am trying to find the actor that has the most co-actors, so the first thing I did was calculate the number of co-actors for each actor. Now I would like to select the value with the highest co-actors and return the number and the name of the actor. Please find below the attempted code:
CREATE VIEW actorview AS
SELECT COUNT(DISTINCT A2.name) AS Count, A.name AS Name
FROM actors A, actors A2
WHERE A2.mid =A.mid
GROUP BY A.name;
SELECT Name, MAX(Count) FROM actorview;
actors table
CREATE TABLE actors (mid integer NOT NULL, name varchar, cast_position integer, PRIMARY KEY (mid, name),
FOREIGN KEY (mid) REFERENCES movies(mid) ON DELETE CASCADE ON UPDATE CASCADE);
Edit:
In the above table mid (movie ID) represents the Movies an actor has been in, any actor that shares the same mid as another was in a movie with that actor. The view works for finding the number of co-actors every actor has, now I just need to select from that list the actor that has the most co-actors.

You can use analytical function rank as follows:
Select * from
(Select a.name, count(distinct a2.name) as co_actors,
Rank() over (order by count(distinct a2.name) desc ) as rn
From actors a join actors c
On a.mid = a2.mid and a.name <> a2.name
Group by a.name) t
Where rn = 1

Related

How to create a good request with "max(count(*))"?

I have to say who is the scientist who have been the most in mission. I tried this code but it wasn't successful:
select name
from scientist, mission
where mission.nums = chercheur.nums
having count(*) = (select max(count(numis)) from mission, scientist where
mission.nums = chercheur.nums
group by name)
I have done several modifications for this request but I only obtain errors (ora-0095 and ora-0096 if I remember correctly).
Also, I create my tables with:
CREATE TABLE Scientist
(NUMS NUMBER(8),
NAME VARCHAR2 (15),
CONSTRAINT CP_CHER PRIMARY KEY (NUMS));
CREATE TABLE MISSION
(NUMIS NUMBER(8),
Country VARCHAR2 (15),
NUMS NUMBER(8),
CONSTRAINT CP_MIS PRIMARY KEY (NUMIS),
CONSTRAINT CE_MIS FOREIGN KEY (NUMS) REFERENCES SCIENTIST (NUMC));
You could count the missions each scientist participated in, and wrap that query in a query with a window function that will rank them according to their participation:
SELECT name
FROM (SELECT name, RANK() OVER (PARTITION BY name ORDER BY cnt DESC) AS rk
FROM (SELECT name, COUNT(*) AS cnt
FROM scientist s
JOIN mission m ON s.nums = m.nums
GROUP BY name) t
) q
WHERE rk = 1
Step 0 : Format your code :-) It would make it much easier to visualize
Step 1 : Get the count of Numis by Nums in the Mission table. This will tell you how many missions were done by each Nums
This is done in the cte block cnt_by_nums
Next to get the name of the scientist by joining cnt_by_nums with scientist table.
After that you want to get only those scientists who have the cnt_by_missions as the max available value from cnt_by_num
with cnt_by_nums
as (select Nums,count(Numis) as cnt_missions
from mission
group by Nums
)
select a.Nums,max(b.Name) as name
from cnt_by_nums a
join scientist b
on a.Nums=b.Nums
group by a.Nums
having count(a.cnt_missions)=(select max(a1.cnt_missions) from cnt_by_nums a1)
I'd write a query like this:
SELECT NAME, COUNTER
FROM
(SELECT NAME, COUNT(*) AS COUNTER
FROM SCIENTIST S
LEFT JOIN MISSION M
ON S.NUMS=M.NUMS
GROUP BY NAME) NUM
INNER JOIN
(SELECT MAX(COUNTER) AS MAX_COUNTER FROM
(SELECT NAME, COUNT(*) AS COUNTER
FROM SCIENTIST S
LEFT JOIN MISSION M
ON S.NUMS=M.NUMS
GROUP BY NAME) C) MAX
ON NUM.COUNTER=MAX.MAX_COUNTER;
(it works on MYSQL, I hope it's the same in Oracle)
As you don't select the name of your scientist (only count their missions) you don't need to join those tables within the subquery. Grouping over the foreign key would be sufficient:
select count(numis) from mission group by nums
You column names are a bit weird but that's your choice ;-)
Selecting only the scientist with the most mission references could be achieved in two ways. One way would be your approach where you may get multiple scientists if they have the same max missions.
The first problem you have in your query is that you are checking an aggregation (HAVING COUNT(*) = ) without grouping. You are only grouping your subselect.
Second, you could not aggregate an aggregation (MAX(COUNT)) but you may select only the first row of that subselect ordered by it's size or select the max of it by subquerying the subquery.
Approach with only one line:
select s.name from scientist s, mission m
where m.nums = s.nums
group by name
having count(*) =
(select count(numis) from mission
group by nums
order by 1 desc
fetch first 1 row only)
Approach with double subquery:
select s.name from scientist s, mission m
where m.nums = s.nums
group by name having count(*) =
(select max(numis) from
(select count(numis) numis from mission group by nums)
)
Second approach would be doing the FETCH FIRST on yur final result but this would give you exactly 1 scientist even if there are multiple with the same max missions:
select s.name from scientist s, mission m
where m.nums = s.nums
group by name
order by count(*) desc
fetch first 1 row only
Doing a cartisian product is not state of the art but the optimizer would make it a "good join" with the given reference in the where clause.
Made these with IBM Db2 but should also work on Oracle.
If you want one row, then in Oracle 12+, you can do:
SELECT name, COUNT(*) AS cnt
FROM scientist s JOIN
mission m
ON s.nums = m.nums
GROUP BY name
ORDER BY COUNT(*) DESC
FETCH FIRST 1 ROW ONLY;
In earlier versions, you would generally use a subquery:
SELECT s.*
FROM (SELECT name, COUNT(*) AS cnt
FROM scientist s JOIN
mission m
ON s.nums = m.nums
GROUP BY name
ORDER BY COUNT(*) DESC
) sm
WHERE rownum = 1;
If you want ties, then generally window functions would be a simple solution:
SELECT s.*
FROM (SELECT name, COUNT(*) AS cnt,
RANK() OVER (ORDER BY COUNT(*) DESC) as seqnum
FROM scientist s JOIN
mission m
ON s.nums = m.nums
GROUP BY name
ORDER BY COUNT(*) DESC
) sm
WHERE seqnum = 1;

How to count number of items in a specific type in a table in sql?

I have a table named Movie, with actors attribute. actors_type is specific and looks like this:
GEORGE.ACTOR_TYPE('Clint Eastwood', 'Christopher Carley', 'Bee Vang', 'Ahney Her')
ACTOR_TYPE is implemented as a varray(5) of varchar(20)
the query I tried to count the number of movies for each actor is :
select m.title, a.column_value, count(m.title)
from movie m, table(m.actors) a
group by m.title, a.column_value
order by a.COLUMN_VALUE
which gives me a count of each row(?) Not the count of movies for each actor. the output is as below:
what I am trying to get is to List actors that acted in multiple movies and show movie title and the actor.
but when I add m.title in the select statement, it will count each row.
This is the other query I wrote:
select a.column_value, count(m.title)
from movie m, table(m.actors) a
having count(m.title) > 1
group by a.column_value
order by a.COLUMN_VALUE
and the result is:
I need to add the title to the output too, but when I add it, all the counts will be one, as the first table.
Movie Table:
There is no table for Actors, we create table for it via table(m.actors) a to access its items
You have to remove m.title from group by and selection cause you are going to count that according to actor.I assume column_value is a common column between two tables , so i used that on join
You need to try like below
select a.column_value, count(m.title)
from movie m
join m.actors a on m.column_value=a.column_value
group by a.column_value
order by a.COLUMN_VALUE

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.

sql select actors play two more distinct role in the same movie

the question is to select actors that played 2 or more distinct roles in the same movie.
And I got 3 table, actor (id,name) movie (id,name) and casts(aid,mid,role) (aid is the actor id and mid is the movie id)
I wrote a query like this
select a.name
from actor a, movie m, casts c
where a.id = c.aid and m.id = casts.mid
group by (m.name)
having count(distinct role) > 2;
this didnt print the right result and I didnt see the problem with it.
Thanks for the help!
How about this? Was there any error trying to execute your query?
select actor.name from actor, casts, movie
where casts.aid =actor.id
and casts.mid = movie.id
group by movie.name, actor.name
having count(distinct role) >= 2
As the casts table appears to contain one row per actor, movie, and role (unless you are leaving out other columns), any time a single pair of unique values of aid and mid appears on more than one row, it means the actor played more than one role in that movie. Thus, there is no reason to use distinct. Also because your desired result doesn't contain the movie names, your query doesn't need and shouldn't use the movie table.
If it is true that the cast table has only one row for each unique combination of (aid, mid, and role) then the following should work:
select name
from actor
where id in ( select aid
from casts
group by aid, mid
having count(*) > 1)

SQL query to find rows with at least one of the specified values

Suppose you had two tables. One called MOVIES:
MovieId
MovieName
Then another called ACTORS that contains people who appear in those movies:
MovieId
ActorName
Now, I want to write a query that returns any movie that contains ONE OR MORE of the following actors: "Tom Hanks", "Russell Crowe" or "Arnold Schwarzenegger".
One way to do it would be something like:
SELECT DISTINCT A.MovieId, M.MovieName FROM ACTORS A
INNER JOIN MOVIES M USING (MovieId)
WHERE A.ActorName IN ('Tom Hanks', 'Russell Crowe', 'Arnold Schwarzenegger');
Which is perfectly fine, however in my case I might have several more of these conditions on the WHERE clause so I want to find a way to make the MOVIES table the primary table I select from.
What's the best way to query for this? I'm using Oracle 11g if that matters, but I'm hoping for a standard SQL method.
You can use EXISTS or IN subqueries:
SELECT *
FROM MOVIES m
WHERE EXISTS
(
SELECT *
FROM ACTORS a
WHERE a.MovieId = m.MovieId
AND a.ActorName IN ('Tom Hanks', 'Russell Crowe', 'Arnold Schwarzenegger')
)
or
SELECT *
FROM MOVIES m
WHERE m.MovieId IN
(
SELECT a.MovieId
FROM ACTORS a
WHERE a.ActorName IN ('Tom Hanks', 'Russell Crowe', 'Arnold Schwarzenegger')
)
First you should have a 3rd table implementing the n:m relationship:
CREATE TABLE movie (
movie_id int primary key
,moviename text
-- more fields
);
CREATE TABLE actor (
actor_id int primary key
,actorname text
-- more fields
);
CREATE TABLE movieactor (
movie_id int references movie(movie_id)
,actor_id int references actor(actor_id)
,CONSTRAINT movieactor_pkey PRIMARY KEY (movie_id, actor_id)
);
Then you select like this:
SELECT DISTINCT m.movie_id, m.moviename
FROM movie m
JOIN movieactor ma USING (movie_id)
JOIN actor a USING (actor_id)
WHERE a.actorname IN ('Tom Hanks', 'Russell Crowe', 'Arnold Schwarzenegger');
Note, that text literals are enclose in single quotes!