Is there an easier way to figure the query out - sql

I have a movie table which has year and movie details like title , movie id( mid) and a table m_cast where i have all the actors in that movie.
I would like to get all the actors who have never been unemployed for more than 3 years. ( Assuming actors are unemployed between two consecutive movies)
i code i came up with is
select a.yr1 y1 , b.yr2 y2 , a.yr1 - b.yr2 diff from
(select substr(substr(trim(year),-5),0,5) yr1 , * from movie m inner join m_cast p on m.mid = p.mid order by pid , yr1) a ,
(select substr(substr(trim(year),-5),0,5) yr2 , * from movie m inner join m_cast p on m.mid = p.mid order by pid, yr2) b on a.yr1 > b.yr2
where not exists
(select count(*) from movie m inner join m_cast p on m.mid = p.mid
and cast(substr(substr(trim(year),-5),0,5) as integer) < a.yr1 and cast(substr(substr(trim(year),-5),0,5) as integer) > b.yr2)
Self join itself takes a lot of time. And lag and lead functions do not work in SQLite version i am using.

I'm assuming the movie table has a column called year, and a column to identify the actor's name. Something like : year int, actorId int
The fastest way to run your query is to filter the last 3 years from your movie table and then to group by your actors the distinct count of years.
Example after filtering
ActorId Year
1. 2018
1. 2018
1. 2017
2. 2016
2. 2017
2. 2018
Then group by and select distinct :
Select actorId from movieTable group by actorId having count (distinct (Year)) =3
And that will only return the actors who have worked in the last 3 years. Once you have your actors id's filtered out in that column do a join to the table that holds their names.
Sorry about the format of my writing - did it from my cellphone.
Regards,
Jorge D. Lopez

Related

How to use count() without the output changing when I use join -Postgresql

I need to find the sum of all the rental days of a car dealership (including
period_begin and period_end, not unique) for all cars of that department.Divided by the total number of different (unique) employees that department ever had.
I have 5 tables
Department(PK departmentnr, name, FK postcode, FK place_name)
Employee(PK employeenr, FK email)
Contract(PK periode_begin, PK periode_end, FK departmentnr, FK employeenr)
registerform (,PK periode_end,PK,periode_end,FKemail,FK,
Fk numberplate,periode_end,periode_begin)
car(PK numberplate,FK departmentnr,FK Brand,FK model)
when I go step by step
part 1
The total employees per department
select departmentnr,Count(employeenr)FROM contract
group by departmentnr
part 2
the amount of days the cars were hired
SELECT DISTINCT departmentnr,
Sum((( Date(periode_end) - Date(periode_begin) + 1 ))) AS
average
FROM registerform r
INNER JOIN car w using(numberplate)
GROUP BY departmentnr
I get the correct ouput but when I try to get these 2 together
SELECT distinct departmentnr,
(
sum(((date(r.periode_end) - date(r.periode_begin) + 1))) / (
select
count(employeenr))
)
as average
from
registerform r
inner join
car w using(numberplate)
inner join
contract using(departmentnr)
inner join
employee using(employeenr)
group by
departmentnr
then my output gets absurd.
How can I fix this and is there a way to make the code more efficient.
Aggregated before you JOIN. So, one method is:
SELECT c.departmentnr, co.num_employees,
Sum( Date(r.periode_end) - Date(r.periode_begin) + 1 ) AS average
FROM registerform r JOIN
car c
USING (numberplate) LEFT JOIN
(SELECT co.departmentnr, Count(*) as num_employees
FROM contract co
GROUP BY co.departmentnr
) co
ON co.departmentnr = c.departmentnr
GROUP BY c.departmentnr, co.num_employees;

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.

How to list movie actors who acted in every single film released in any given year

I have two databases heading into this: a table acting_gigs with columns actor_name and movie_title and table movies with columns movie_title and release_year. I would like to make a SQL query that lists the names of all the actors that have participated in every single movie in a given release_year, and display two columns: the actors' names (actor_names) and the year in which they participated in every movie (release_year).
For example:
movie_title | release_year
------------------------------------------
'The Green Mile' | 2000
'Titanic' | 1997
'Cast Aways' | 2000
'Independence Day' | 1997
actor_name | movie_title
-------------------------------------------------
'Leonardo DiCaprio' | 'Titanic'
'Tom Hanks' | 'The Green Mile'
'Will Smith' | 'Independence Day'
'Tom Hanks' | 'Cast Aways'
Which means that the table I would like to return is
actor_name | release_year
---------------------------
'Tom Hanks' | 2000
I have been trying to use subqueries and outer joining, but I have not been able to quite arrive at a solution. I know that I have to use count, but I'm unsure how to apply it multiple times in a manner such as this.
Here's one way:
SELECT y.actor_name, y.release_year
FROM (SELECT release_year, COUNT(*) AS cnt
FROM movies
GROUP BY release_year) AS x
INNER JOIN (SELECT actor_name, release_year, COUNT(*) AS cnt
FROM acting_gigs AS t1
INNER JOIN movies AS t2 ON t1.movie_title = t2.movie_title
GROUP BY actor_name, release_year) AS y
ON x.release_year = y.release_year AND x.cnt = y.cnt
Derived table x contains the count of movies per year, whereas derived table y contains the count of movies per year / per actor.
The JOIN predicates:
x.release_year = y.release_year and
x.cnt = y.cnt
guarantee that, for a specific year, only actors that participated in all movies of that year are returned.
Demo here
Here's another, probably more efficient, way using window functions:
SELECT DISTINCT actor_name, release_year
FROM (
SELECT actor_name, release_year,
COUNT(*) OVER (PARTITION BY actor_name, release_year) AS cntPerActorYear,
COUNT(*) OVER (PARTITION BY release_year) AS cntPerYear
FROM acting_gigs AS t1
INNER JOIN movies AS t2 ON t1.movie_title = t2.movie_title ) AS t
WHERE cntPerActorYear = cntPerYear
Demo here
This should do the trick:
select m.release_year
, a.actor_name
, count(1) total_movies
from movies m
join actors a on a.movie_title = m.movie_title
group by m.release_year, a.actor_name
order by m.release_year, a.actor_name -- or however you want to order it
Here's how you do it in MS SQL - http://sqlfiddle.com/#!6/492ac/3
SELECT
A.ActorName,
M.ReleaseYear
FROM
Movies AS M
INNER JOIN
ActorsMovies AS A
ON
M.MovieTitle = A.MovieTitle

SQL select for average from another table

I spent a lot of time building this select, but Im not able to solve it. I have 2 tables. First table is called car and has PK (primary key) id_car and another columns name and so on. Second table is called rating and has colums id_rating (PK), id_car (FK) and rating_value (integer). As you suspect, one car can have more than one ranting. I want to select all cars and I want to know average rating to each car. Finally, I want to order the result by this average desc. I was trying things like this:
SELECT id_car, name, average
FROM car C, rating R
WHERE C.id_car = R.id_car
ORDER BY (average) (
SELECT AVG(rating_value) AS average
FROM rating R
WHERE C.id_car = R.id_car)
but it doesn't work.
For SQL Server; Also I suggest you to use JOIN instead of WHERE table1, table2..
SELECT C.id_car, name, AVG(rating_value) AS average
FROM car C JOIN rating R
ON C.id_car = R.id_car
GROUP By C.id_car, name
ORDER BY average DESC
This implements the aggregate function AVG() and then a GROUP BY the car id and name:
select c.id_car, c.name, avg(r.rating_value) aver
from car c
left join rating r
on c.id_car = r.id_car
group by c.id_car, c.name
order by aver desc
Using a LEFT JOIN will include all cars in the result even those which have no yet been rated.
SELECT C.name, AVG(R.rating_value)
FROM car C, rating R
WHERE C.id_car = R.id_car
GROUP BY C.name
ORDER BY AVG(R.rating_value) DESC
SELECT c.id_car, c.name,avg(r.rating_value) as rating
FROM car c
join rating r
on c.id_car = r.id_car
group by r.id_car
order by rating

SQL Oracle Select Group where all members are borrowed

I have two tables: Car and CarBorrowed.
The Car table contains all cars in the car pool with an ID and a group the car belongs to. For example:
ID 1, Car 1, Group Renault
ID 2, Car 3, Group Renault
ID 3, Car 4, Group VW
ID 4, Car 6, Group BMW
ID 5, Car 7, Group BMW
The CarBorrow table contains all cars which are borrowed on a particular day
Car 1, Borrowed on 23.08.2012
Car 3, Borrowed on 23.08.2012
Car 5, Borrowed on 23.08.2012
Now I want all groups, where no cars are left (today= 23.08.2012). So I should get "Group Renault"
First, join the tables, so we have for every car its borrows(a day).
select c.id, c.GroupName, cb.day
from car c
left join (select * from CarBorrow where day = '23 Aug 2012') cb
on (c.id = cb.id)
All cars not borrowed will have null at day.
After this, we shoud select all Groups that does not have nulls.
Bellow an trick to get it:
select GroupName
FROM(
select c.id, c.GroupName, cb.day
from car c
left join (select * from CarBorrow where day = '23 Aug 2012') cb
on (c.id = cb.id)
)
group by GroupName
having count(day) = count(*)
(Days that are null are not counted by COUNT)
SELECT distinct(D1.CARGROUP)
FROM den_car d1
MINUS
(SELECT D.CARGROUP
FROM den_car d
WHERE d.id IN (SELECT c.ID
FROM den_car c
MINUS
SELECT b.id
FROM den_car_borrow b
WHERE B.DATE_BORROW = TO_DATE (SYSDATE)))
This may be optimized but the idea is simple: Find the borrowed ones, subtract it from all cars. Then find the remaning groups.
Hope it helps. (By the way of course there are lots of other ways to do it.)
Hmmm . . . One way to approach this query is to count the cars in a group and also count the cars on a particular day, then take the groups where the borrowed equals the available:
select borrowed.BorrowedOn, available.CarGroup
from (select c.CarGroup, count(*) as cnt
from car c
group by c.CarGroup
) available left outer join
(select c.CarGroup, cb.BorrowedOn, count(*) as cnt
from CarBorrowed cb join
Car c
on cb.CarId = c.CarId
group by c.CarGroup, cb.BorrowedOn
) borrowed
on available.CarGroup = borrowed.CarGroup
where available.cnt = borrowed.cnt
By the way, "Group" is a bad name for a column, since it is a SQL reserved word. I've renamed it to CarGroup.
If the same car can be borrowed more than once on a given day, then change the count(*) in the second subquery to count(distinct cb.carId).
If you want just one day, you can add a clause to the WHERE clause.
I think I have a solution now:
select x.groupname from
(select a.groupname, count(*) as cnt from car a group by a.groupname) x
inner join
(
select b.groupname, count(*) as cnt from car b where b.carid in (select caraid from modavail where day ='23.08.2012')
group by b.groupname
) y
on x.groupname = y.groupname
where x.cnt = y.cnt and y.cnt ! = 0 ORDER BY GROUPNAME;
Thanks for your help!!!!