SQL EXISTS Query - sql

I am trying to write a query to select an album from a table to which at least one artist has been assigned using the EXISTS query.
Albums and Artists are contained in separate tables and it is possible to have albums to which no artists have been assigned, where the value returns as NULL.
Can someone provide an example of how to go about creating this query.
EDIT: Adding the non-working example below
SELECT artist_name FROM artist
JOIN album ON artist.artist_id = album.artist_id
WHERE EXISTS (SELECT album_id FROM album)
The query is returning the correct result, but I don't think the last line is correct because it isn't using the operation for where at least one exists, so I'm thinking there needs to be an operator in the sub-query, or something to do with a NULL value.

If the tables are like :
artist_id, artist_name, album_id
and
album_id, album_name
Then the query will be
select *
from album alb
left join artist art on(alb.album_id = art.album_id)
where art.artist_id is null
Using exists:
select *
from album alb
where not exists
(select * from artist art where art.album_id = alb.album_id)

Your query works, but for an arcane reason:
The join is doing the work.
The exists is always returning true (assuming album has at least one row).
Actually, it sort-of works, because the question is about albums and you are returning artists.
Don't use the join in the outer query. Instead, you want a correlated subquery:
SELECT al.*
FROM album al
WHERE EXISTS (SELECT 1 FROM artist al WHERE a.artist_id = al.artist_id)

Related

SQLite Subqueries and Inner Joins

I was doing a practice question for SQL which asks to create a list of album titles and unit prices for the artist "Audioslave" and find out how many records are returned.
Here is the relational database picture given in the question:
Initially, I used an inner join to retrieve the list and actually got the correct answer (40 records returned). The code is shown below:
select a.Title, t.UnitPrice
from albums a
inner join tracks t on t.AlbumId = a.AlbumId
inner join artists ar on ar.ArtistId = a.ArtistId
where ar.Name = 'Audioslave';
Although I finished the question, I was curious to try to solve this problem using nested subqueries instead and tried to first retrieve the AlbumId and UnitPrice from tracks. I got the correct answer but not the correct list (the question asked for album title and not AlbumId). Here is the code:
select AlbumId, UnitPrice
from tracks
where AlbumId in (
select AlbumId
from albums
where ArtistId in (
select ArtistId
from artists
where Name = 'Audioslave'));
In order to solve the problem with the list, I tried combining the previous codes. However, I get a completely different amount of records being returned (10509).
select a.Title, t.UnitPrice
from albums a
inner join tracks t
where a.AlbumId in (
select AlbumId
from albums
where ArtistId in (
select ArtistId
from artists
where Name = 'Audioslave'));
I don't understand what I'm doing wrong with the last code...Any help would be appreciated! Also, sorry if I wrote too much, I just wanted to convey my thinking process clearly.
Some databases (SQLite, MySQL, Maria, maybe others) allow you to write an INNER JOIN without specifying ON, and they just cross every record on the left with every record on the right in that case. If there were 2 albums and 3 tracks, 6 rows would result. If the albums were A and B, and the tracks were 1, 2 and 3, the rows would be the combination of all: A1, A2, A3, B1, B2, B3
Other databases (Postgres, SQLServer, Oracle, maybe others) refuse to do it unless you specify ON. To get an "every row on the left combined with every row on the right" you have to write CROSS JOIN (or write an inner join with an ON that is always true)
It might help your mental model of what happens during a join to consider that the db takes all the rows on the left and connects them to all the rows on the right, then for each combination of rows, assesses the truth of the ON clause, and the WHERE clause, before deciding to return the row
For example, this will return 10509 rows:
SELECT * FROM albums INNER JOIN tracks ON 1=1
The on clause is always true
This will return 10509 tracks, but only if the query is run on Monday
SELECT * FROM albums INNER JOIN tracks ON strftime('%w', 'now') = 1
What goes in the ON or WHERE doesn't have to have anything to do with the data in the table.. it just has to be something that resolves to a Boolean

(SQL) Creating an uncorrelated query

I had to write an SQL-Query for a given Database (it's huge, I won't be able to post it here, but its about artists with albums and release dates, genres etc.).
The Task was to find all artists involved in albums which contains the word "drop". I had to write an correlated and an uncorrelated query. I got the correlated:
SELECT artist
FROM CDDB.ARTISTS ar
WHERE EXISTS
(SELECT album
FROM CDDB.ALBUMS al
INNER JOIN CDDB.ARTIST2ALBUM aa ON al.albumid = aa.albumid
WHERE ar.artistid = aa.artistid
AND album LIKE '\%drop\%');
Now I have to make that uncorrelated, but I don't know how. Is it possible that one can help me without the given tables etc.?
Uncorrelated subqueries are subqueries that can be run independently from the outer query.
Generally speaking, EXISTS is correlated, IN is uncorrelated.
If you change your query to something like:
SELECT artist
FROM CDDB.ARTISTS ar
INNER JOIN CDDB.ARTIST2ALBUM aa ON ar.artistid = aa.artistid
WHERE album in
(SELECT album
FROM CDDB.ALBUMS
WHERE album LIKE '%drop%');
It is now uncorrelated.

Using NOT IN in Access

I have two tables: GENRE and MOVIE. GENRE contains genre_code and genre_desc, while MOVIE has tile, genre_code, etc... I am trying to list all the genre_desc that are not are not associated with a title. I need to use NOT IN and avoid EXISTS.
I have tried -
SELECT GENRE.MOVIE_GENRE_DESC
FROM GENRE INNER JOIN MOVIE ON MOVIE.MOVIE_GENRE_CODE = MOVIE.GENRE_CODE
WHERE GENRE.MOVIE_GENRE_DESC Not In ([MOVIE].[GENRE_CODE]);
I just get a list of desc that have titles
You can use this SQL with LEFT JOIN:
SELECT Genre.genre_code, Genre.genre_desc
FROM Genre LEFT JOIN Movie ON Genre.genre_code = Movie.genre_code
WHERE Movie.id Is Null
or with NOT IN:
SELECT Genre.genre_code, Genre.genre_desc
FROM Genre
WHERE Genre.genre_code not in (SELECT Movie.genre_code FROM Movie )
First variant should work faster, NOT IN means not using indexes.

Doing a FULL OUTER JOIN in Sqlite3 to get the combination of two columns?

I'm currently working on a database project and one of the problems calls for the following:
The Genre table contains twenty-five entries. The MediaType table contains 5
entries. Write a single SQL query to generate a table with three columns and 125
rows. One column should contain the list of MediaType names; one column
should contain the list of Genre names; the third column should contain a count of
the number of tracks that have each combination of media type and genre. For
example, one row will be: “Rock MPEG Audio File xxx” where xxx is the
number of MPEG Rock tracks, even if the value is 0.
Recognizing this, I believe I'll need to use a FULL OUTER JOIN, which Sqlite3 doesn't support. The part that is confusing me is generating the column with the combination. Below, I've attached the two methods I've tried.
create view T as
select MediaTypeId, M.Name as MName, GenreId, G.Name as GName
from MediaType M, Genre G
SELECT DISTINCT GName, MName, COUNT(*) FROM (
SELECT *
FROM T
OUTER LEFT JOIN MediaType
ON MName = GName
UNION ALL
SELECT *
FROM Genre
OUTER LEFT JOIN T
) GROUP BY GName, MName;
However, that returned nearly 250 rows and the GROUP BY or JOIN(s) is totally wrong.
I've also tried:
SELECT Genre.Name as GenreName, MediaTypeName, COUNT(*)
FROM Genre LEFT OUTER JOIN (
SELECT MediaType.Name as MediaTypeName, Track.Name as TrackName
FROM MediaType LEFT OUTER JOIN Track) GROUP BY GenreName, MediaTypeName;
Which returned 125 rows but they all had the same count of 3503 which leads me to believe the GROUP BY is wrong.
Also, here is a schema of the database:
https://www.dropbox.com/s/onnbwqfrfc82r1t/IMG_2429.png?dl=0
You don't use full outer join to solve this problem.
Because it looks like a homework problem, I'll describe the solution.
First, you want to generate all combinations of genres and media types. Hint: This uses a cross join.
Second, you want to count all the combinations that you have. Hint: this uses an aggregation.
Third, you want to combine these together. Hint: left join.

Select all in table where a column in table 1 exists in another table and a second column equals a variable in second table

I know the title is confusing but its the best I could explain it. Basically im developing a cinema listings website for a company which owns two cinemas. So I have a database which has the two tables "Films" and "Listings" with data for both cinemas in them.
I'm trying to select all films and their data for one cinema if the films name shows up in the listings (since the two cinemas share all films but in the table but the may not have the same films showing)
Here is what i have come up with but I run into a problem as when the "SELECT DISTINCT" returns more than one result it obviously cant be matched with the FilmName on tbl Films.
How can i check this value for all FilmNames on tblFilms?
SELECT *
FROM tblFilms
WHERE FilmName = (SELECT DISTINCT FilmName FROM tblListings WHERE Cimema = 1)
use IN if the subquery return multiple values,
SELECT *
FROM tblFILMS
WHERE FilmName IN (SELECT DISTINCT FilmName FROM tblListings WHERE Cimema = 1)
Another way to solve thius is by using JOIN (which I recommend)
SELECT DISTINCT a.*
FROM tblFILMS a
INNER JOIN tblListings b
ON a.FilmName = b.FilmName AND
b.Cimema = 1
for faster query execution, add an INDEX on FilmName on both tables.
If you have your schemas for the tables, that would help.
That said, I believe what you want to look at is the JOIN keyword. (inner/outer/left/etc). That's exactly what JOIN is meant to do (ie your title).