Why do we use a sub-queries in SQL? - sql

I don't understand we would need to use sub-queries in data analysis or why the order matters. Can someone explain it to me?
Example: Why do the 2 codes below produce 2 different outputs from the difference in ordering of the code? They are both trying to find the lowest rental price in 2006.
Code 1:
SELECT
film_id
,title
FROM Films
WHERE release_year = 2006
AND rental_rate =
(
SELECT
MIN(rental_rate)
FROM Films
);
Code 2:
SELECT
film_id
,title
FROM Films
WHERE rental_rate =
(
SELECT
MIN(rental_rate)
FROM Films
WHERE release_year = 2006
);

Related

Getting top 10 most popular within an array column using SQL UNNEST

I am working with a sample data set which gives the following result:
Continuing to work, I am now trying to get the top 10 Production Companies (based on "production_companies" field) that made the most number of movies in the most popular genre for a year.
The output
Rank | Production Company | Popular Genre | Movie Count
I thought breaking this down to getting the most popular genre for the year would be the 1st step with the following query:
select
genres.name AS _genre,
FROM
commons.movies m,
UNNEST(m.genres) as genres
WHERE
SUBSTR(m.release_date, 1, 4) = '2008'
GROUP BY
genres.name
ORDER BY
COUNT(genres.name) DESC
LIMIT
1
I have now go the output as 'Drama' being the most popular genre for the year 2008.
Answering the question to get the most popular prod company and their count has been a bit challenging and failing several times.
I have after several tries got to:
select
o_prd_cmp.name,
o_mov.title
from
commons.movies o_mov,
unnest(o_mov.genres) as o_gnr,
UNNEST(o_mov.production_companies) AS o_prd_cmp
where
SUBSTR(o_mov.release_date, 1, 4) = '2008'
AND o_gnr.name = (
select
genres.name AS _genre,
FROM
commons.movies m,
UNNEST(m.genres) as genres
WHERE
SUBSTR(m.release_date, 1, 4) = '2008'
GROUP BY
genres.name
ORDER BY
COUNT(genres.name) DESC
LIMIT
1
)
Any help with this is greatly appreciated.

SQL Sub Query to find data

I need to find all titles of films that have greater replacement cost than some R rating film.
film table has...
film_id, title, description, release_year, language_id, original_language_id, rental_duration, rental_rate, length, replacement_cost, rating, special_features, last_update
This is not working...
SELECT
title
FROM film
WHERE replacement_cost > (SELECT
replacement_cost
FROM film
WHERE rating = 'R');
SELECT title
FROM film
WHERE replacement_cost >
(SELECT MAX(replacement_cost)
FROM film
WHERE rating = 'R');
You need aggregation of some kind to make the subselect a scalar value. Using MAX() in your query would give you the highest replacement_cost of a rated R film.
To get the replacement_cost of a particular film then you need to do the same thing but modify your where statement to be the film you want. say you know the film_id then you could do:
SELECT title
FROM film
WHERE replacement_cost >
(SELECT replacement_cost
FROM film
WHERE film_id = 123);
Note I took off MAX() because when using the primary key or criteria that will choose a single scalar value (1 column 1 row) from the table you don't actually have to use aggregation.

SQL - Get consecutively minimum numbers

Title may not make sense so I will provide some context.
I have a table, call it Movies.
A movie tuple has the values: Name, Director, Genre, Year
I'm trying to create a query that allows me to return all Directors who have never released two consecutive Horror films with more than 4 years apart.
I'm not sure where I'd begin but I'm trying to start off by creating a query that given some specific year, returns the next minimum year, so that I can check if the difference between these two is less than 4, and keep doing that for all movies.
My attempt was:
SELECT D1.Director
FROM Movies D1
WHERE D1.Director NOT IN
(SELECT D2.Director FROM Director D2
WHERE D2.Director = D1.Director
AND D2.Genre = 'Horror'
AND D1.Genre = 'Horror' AND D2.Year - D1.Year > 4
OR D1.Year - D2.Year > 4)
which does not work for obvious reasons.
I've also had a few attempts using joins, and it works on films that follow a pattern such as 2000, 2003, 2006, but fail if more than 3 films.
You could try this:
Select all data, and use lag or lead to return the last or next year. After that look at the difference between the two.
WITH TempTable AS (
SELECT
Name,
Director,
Genre,
Year,
LAG(Year) OVER (PARTITION BY Name, Director, Genre ORDER BY Year ASC) AS 'PriorYear'
FROM
Movies
WHERE
Genre = 'Horror'
)
SELECT
Name,
Director
FROM
TempTable
GROUP BY
Name,
Director
HAVING
MAX(Year-PriorYear) < 2
Try this:
SELECT * FROM (
SELECT director, min(diff) as diff FROM (
SELECT m1.director, m1.year as year1, m2.year as year2, m2.year-m1.year as diff
FROM `movies` m1, movies m2
WHERE m1.director = m2.director and m1.name <> m2.name and m1.year<=m2.year
and m1.genre='horror' and m2.genre='horror'
) d1 group by director
) d2 WHERE diff>4
First, in the inner Select it will list all movie pairs of directors' horror movies with year difference calculated, then minimum of these are selected (for consecutiveness), then longer than 4 years differences are selected...

Sql Query for number of available and total books

I need a query which will return list of book from book table
but it will show total number of books and books available too ..
And for number of books it will count number of books with simular ISBN n amount count which will count number of books with simular ISBN with status available
Table structure
Bookid booktitle author ISBN edition publisher price supplier volume language status
status contains available,issued
I want result as
BookTitle Author Publisher Location Total available
Please also explain the query because I need to implement it on many tables
Queries i tried this far
select *,count(1) as Quantity from Book group by Book.BookTitle
It successfully gives me book list with total quantity
and
select *,count(1) as Quantity from Book where status='Available' group by Book.BookTitle
It Successfully gives me list of books with how many are available.
but i need combination of both total and available ..
In brevi:
select isbn, booktitle, sum(case when status='available' then 1 else 0 end) as avail, count(*) as total
from Book
group by isbn, booktitle
http://sqlfiddle.com/#!2/80a41/5
i definitely suggest you to read about 3rd normal form
You must group by all items in the SELECT query. You can either count(*) or sum(1). Here is how I would do:
SELECT BookId, Name, Available, count(*) as Quantity
FROM Book GROUP BY Bookid,Name,Available
select d.* from (select BookTitle,Author,Publisher,Location,count(BookTitle) as Total, Status, Count(Status) as StatusQuantity from Book group by BookTitle,Author,Publisher,Location,Status)d where d.Status='Available'
Try this one:
SELECT
BookTitle,
Author,
Publisher,
Location,
SUM(CASE WHEN status='available' THEN 1 ELSE 0 END) Available,
COUNT(*) AS Total
FROM Book
GROUP BY BookTitle,Author,Publisher,Location
Are you have any key constraints??
you must split the table..
Using the bookid or ISBN as a primary key,store the status in a separate table..
it will be easy for further process otherwise the records should be redundant..

Joining new columns in MySQL One-To-Many

I asked a similar question the other day but it seems no one was able to answer it, and searched the internet for a few days but still fruitless, perhaps I am not asking the question the right way: One-to-many Query in MySQL
So I while try again and maybe word it a bit differently. This is essentially a simplified version of what I am trying to do:
CREATE TABLE Customer(
customer_id INT NOT NULL,
first_name varchar(20),
last_name varchar(20)
);
CREATE TABLE Payment(
customer_id INT NOT NULL,
amount_paid INT,
year YEAR,
FOREIGN KEY (customer_id) REFERENCES Customer(customer_id)
);
What I want is to organize the first_name on the left, only occurring once, and then for each year list the payment amount in separate columns because I am going to be attaching this to WPF and I want a spreadsheet style representation of the data. So, ideally it would look like this:
name 2009 2008 2007
John 500 600 NULL
Anne NULL 500 600
Bob NULL NULL 600
My approach is to count the number of distinct years of payments, and use that as a loop counter. than loop through and collect the data for each year. Represent each column of amount_paid by the year number. I am not just not sure how to do that, because my initial approach was to use UNION, but than I realized that just puts everything in the same column as opposed to separate ones. So what should I be using? I am only asking for some guidance. Thank you!
Use:
SELECT c.first_name,
MAX(CASE WHEN p.year = 2009 THEN c.amount_paid ELSE NULL END) AS 2009,
MAX(CASE WHEN p.year = 2008 THEN c.amount_paid ELSE NULL END) AS 2008,
MAX(CASE WHEN p.year = 2007 THEN c.amount_paid ELSE NULL END) AS 2007
FROM CUSTOMER c
JOIN PAYMENT p ON p.customer_id = c.customer_id
GROUP BY c.first_name
mysql unfortunately has no pivot feature, so the only possible way is to format the result set with any programming language.
I hadn't test it but I think something like this works:
select * from ((select name, sum(amount_paid) as 2009 from customer, payment
where customer.customer_id = payment.customer_id
and payment.year = 2009) a,
(select name, sum(amount_paid) as 2008 from customer, payment
where customer.customer_id = payment.customer_id
and payment.year = 2008) b
(select name, sum(amount_paid) as 2007 from customer, payment
where customer.customer_id = payment.customer_id
and payment.year = 2007) c);