MS Access SQL - Left Join a table that only has unique matches - sql

I'm using an example database I got through DBeaver. It contains a table called Artists and a table called Albums. Some artists have no albums, some have one and some have many.
What I'm trying to achieve is a left join to show all artists but only display the album name of the artists who only have one album.
The following works using DBeaver but doesn't work if executed directly from MS Access (which i need to be able to do):
SELECT
ar.AName,
al.title
FROM
Artist ar
LEFT JOIN (
SELECT
al.*
FROM
album al
INNER JOIN (
SELECT
artistid,
COUNT( artistid ) artistCount
FROM
album
GROUP BY
artistid ) alc ON
alc.artistid = al.artistid
AND alc.artistcount = 1 ) al ON
al.artistid = ar.artistid
ORDER BY
ar.aname
Tables have the following columns:
Artist
ArtistID (Key)
Name
Album
AlbumID (Key)
Title
ArtistID
Many thanks in advance. I can't find an answer to this anywhere. I understand the version of SQL used in MS Access isn't the same as found in various other variants.
Cheers
Patrick

I would go with NOT EXISTS :
SELECT a.ArtistID, a.Name, al.Title
FROM Artist a LEFT JOIN
Album al
ON al.ArtistID = a.ArtistID AND
NOT EXISTS (SELECT 1
FROM Album al1
WHERE al1.ArtistID = al.ArtistID AND a1l.AlbumID <> al.AlbumID
);

Thank you for your help. I have managed to produce the results I was expecting using SQL in MS Access (will need to test to ensure its 100%).
I used the following:
SELECT
ar.AName,
al.title
FROM
Artist ar
LEFT JOIN (
SELECT
al.*
FROM
album al,
(
SELECT
al1.artistid,
COUNT( al1.artistid ) AS al1Count
FROM
album al1
GROUP BY
al1.artistid ) al1
WHERE
al.artistID = al1.artistid
AND al1.al1count = 1 ) al ON
al.artistid = ar.artistid
ORDER BY
ar.aname
Thanks again!

You don't want a left join. If an artist has exactly one album, then a regular join will work. The key is aggregation:
select ar.aname, max(al.title) as title
from artist as ar inner join
album as al
on ar.artistid = al.artistid
group by ar.artistid, ar.aname -- I supposed two artists could have the same name
having count(*) = 1;
The key idea is that if there is only one album, then max(al.title) returns that album's name.
EDIT: (based on comment)
select ar.aname,
switch(al.artistid is null, "No album"
count(*) = 1, max(al.title),
1=1, "> 1"
) as album_title
from artist as ar left join
album as al
on ar.artistid = al.artistid
group by ar.artistid, ar.aname;

Related

is there a more efficient alternative for this SQL query?

Im working on a movie data set that has tables for movies, genre and a bridge table in_genre.
The following query tries to find common genres between two movies. Im doing two joins to get the genre list and a intersect to find common genres.
Is there a more efficient way?
Table schema:
movie : movie_id(PK)(int)
in_genre(bridge_table): movie_id(FK)(int), genre_id(int)
SELECT count(*) as common_genre
FROM(
// getting genres of first movie
SELECT in_genre.genre_id
FROM movie INNER JOIN in_genre ON movie.id = in_genre.movie_id
WHERE movie.id = 0109830
INTERSECT
// getting genres of second movie
SELECT in_genre.genre_id
FROM movie INNER JOIN in_genre ON movie.id = in_genre.movie_id
WHERE movie.id = 1375666
) as genres
If it only needs the data from in_genre then there's no need to join the movie table.
And you can use an EXISTS to find the common genres.
SELECT COUNT(DISTINCT genre_id) as common_genre
FROM in_genre ig
WHERE movie_id = 0109830
AND EXISTS
(
SELECT 1
FROM in_genre ig2
WHERE ig2.movie_id = 1375666
AND ig2.genre_id = ig.genre_id
)
If you want the genres, I would simply do:
SELECT genre_id as common_genre
FROM in_genre ig
WHERE movie_id IN (0109830, 1375666)
GROUP BY genre_id
HAVING COUNT(*) = 2;
If you want the count, a subquery is simple enough:
SELECT COUNT(*)
FROM (SELECT genre_id as common_genre
FROM in_genre ig
WHERE movie_id IN (0109830, 1375666)
GROUP BY genre_id
HAVING COUNT(*) = 2
) g;
If you want full information about the genres, then I would suggest exists:
select g.*
from genres g
where exists (select 1
from in_genre ig
where ig.genre_id = g.genre_id and ig.movie_id = 0109830
) and
exists (select 1
from in_genre ig
where ig.genre_id = g.genre_id and ig.movie_id = 1375666
);

Is there a way to count and loop with plain SQL?

I have the following tables:
album:
albumID, albumTitle, albumReleaseDate, albumLabel, albumDigitalImg, albumCoverStory
albumTrack:
trackID, albumID, recordingID, albumTrackNumber, cd
italic = primary key
bold = foreign key
The question is as follows:
List the total number of tracks on each album that has any. Give the
column containing the total number of tracks a sensible name and use
albumId to identify each total.
My only idea would be to iterate over each albumID and check how many tracks are assigned to it but obviously loops aren't a thing in plain SQL? Is there any way to do this using plain SQL?
This only checks how many tracks are assigned for albumID 1, not for all albums and I'm really lost as to how I could do this without a loop.
SELECT COUNT(albumID)
FROM albumTrack
WHERE albumID = 1;
I'm using Oracle.
Below query will give you the number of tracks against each albumID,if any.
SELECT albumID,count(trackID) as NumberOfTracks
FROM albumTrack
Group By albumID;
You can try:
Select ab.albumID,
Count(Distinct trackID) As trackCount
From album ab
Inner Join albumTrack at
on at.albumID = ab.albumID
Group By ab.albumID;
This will give the count of tracks for each album.
The question asks for the counts per albumID.
For just that, a select on albumTrack alone with a GROUP BY on the albumID should be sufficient.
SELECT albumID, COUNT(*) AS TotalTracks
FROM albumTrack
GROUP BY albumID
ORDER BY albumID
But if albumTrack.albumID can be NULL, or isn't a foreign key on album.albumID?
Then an INNER JOIN on the "album" table should still be used.
To make sure that it only counts for albumID's that actually exist in the "album" table.
SELECT
tracks.albumID,
COUNT(*) AS TotalTracks
FROM albumTrack AS tracks
JOIN album ON album.albumID = tracks.albumID
GROUP BY tracks.albumID
ORDER BY tracks.albumID
And if you'd like to show the counts per albumTitle:
SELECT
a.albumTitle,
COUNT(t.trackID) AS TotalTracks
FROM album a
JOIN albumTrack t ON t.albumID = a.albumID
-- WHERE a.albumID = 1
GROUP BY a.albumID, a.albumTitle
ORDER BY a.albumTitle

SQL add attribute from another table

Table WORK
(WORK ID, TITLE, DESCRIPTION, ARTIST ID)
Table ARTIST
(ARTIST ID, FIRSTNAME, LAST NAME, DATE OF BIRTH, DATE OF DEATH)
Hi
My problem is that I have to display the details of all works of art (including the name of the artist who created the work) that are signed.
I managed to go until
Select * from Work WHERE DESCRIPTION LIKE '%Signed%'
and it displays
WorkID TITLE DESCRIPTION ARTISTID
123 1455 Signed 12
However, I must display
WorkID TITLE DESCRIPTION ARTISTID FIRSTNAME LASTNAME
123 1455 Signed 12
How do I do that? I know it has something to do with JOIN but I'm not very sure.
Inner Join will do or can try left join if u need to
SELECT W.*, A.FirstNAme, A.LastName from work w inner join Artist A on
A.Artistid
= W.ArtistId where A.Description LIKE '%Signed%'
Use inner join to get the records that exist in both tables
Select W.WorkID, W.TITLE, W.DESCRIPTION, W.ARTISTID, A.FIRSTNAME, A.LASTNAME
From Work W
Inner Join Artist A on W.WorkID = A.WorkID
Where Description LIKE '%Signed%'
a left outer join would get all the records in the work table and the matching ones in the artist table.
Check this post out to understand joins a bit more : JOINS

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 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