Reputation: 165
I have three tables: albums
, songs
, and images
.
The tables look something like this:
albums
table:
id | title | image_id
5 | First Album | 1
6 | Another Album | 2
songs
table:
id | album_id | title | image_id
32 | 5 | My Song | 3
33 | 5 | Another One | 4
34 | 5 | First Song | 0
35 | 6 | My Song #2 | 0
36 | 6 | Fancy Title | 0
37 | 6 | My Love Song | 5
images
table:
id | path
1 | path/to/image1.jpg
2 | path/to/image2.jpg
3 | path/to/image3.jpg
4 | path/to/image4.jpg
5 | path/to/image5.jpg
I am trying to display a list of songs with their titles and corresponding images.
If the songs don't have an image (if the image_id
column is 0
), then I want to just use the album image.
The only code I have so far is this, and I am stuck:
SELECT songs.title, images.path
FROM songs
LEFT JOIN images
ON images.id = songs.image_id
// ...if songs.image_id is 0, i want to just use the image_id of the corresponding album instead
It would be nice if it was an efficient query that could work with large tables.
Upvotes: 4
Views: 1040
Reputation: 6065
Here is a solution using multiple LEFT JOINs, with demo using data you provided.
The final query:
SELECT
s.title,
COALESCE(i1.path, i2.path)
FROM songs s
LEFT JOIN albums a ON s.album_id = a.id
LEFT JOIN images i1 ON s.image_id = i1.id
LEFT JOIN images i2 ON a.image_id = i2.id
ORDER BY s.id;
Below is a full demo.
SQL:
-- Data for demo
create table albums(id int, title char(100), image_id int);
insert into albums values
(5 , 'First Album' , 1),
(6 , 'Another Album' , 2);
create table songs(id int, album_id int, title char(100), image_id int);
insert into songs values
(32 , 5 , 'My Song' , 3),
(33 , 5 , 'Another One' , 4),
(34 , 5 , 'First Song' , 0),
(35 , 6 , 'My Song #2' , 0),
(36 , 6 , 'Fancy Title' , 0),
(37 , 6 , 'My Love Song' , 5);
create table images(id int, path char(200));
insert into images values
(1 , 'path/to/image1.jpg'),
(2 , 'path/to/image2.jpg'),
(3 , 'path/to/image3.jpg'),
(4 , 'path/to/image4.jpg'),
(5 , 'path/to/image5.jpg');
SELECT * FROM albums;
SELECT * FROM songs;
SELECT * FROM images;
-- SQL needed
SELECT
s.title,
COALESCE(i1.path, i2.path)
FROM songs s
LEFT JOIN albums a ON s.album_id = a.id
LEFT JOIN images i1 ON s.image_id = i1.id
LEFT JOIN images i2 ON a.image_id = i2.id
ORDER BY s.id;
Output:
mysql> SELECT
-> s.title,
-> COALESCE(i1.path, i2.path)
-> FROM songs s
-> LEFT JOIN albums a ON s.album_id = a.id
-> LEFT JOIN images i1 ON s.image_id = i1.id
-> LEFT JOIN images i2 ON a.image_id = i2.id
-> ORDER BY s.id;
+--------------+----------------------------+
| title | COALESCE(i1.path, i2.path) |
+--------------+----------------------------+
| My Song | path/to/image3.jpg |
| Another One | path/to/image4.jpg |
| First Song | path/to/image1.jpg |
| My Song #2 | path/to/image2.jpg |
| Fancy Title | path/to/image2.jpg |
| My Love Song | path/to/image5.jpg |
+--------------+----------------------------+
6 rows in set (0.00 sec)
Upvotes: 2
Reputation: 21
You can also use UNION in this way:
SELECT songs.title, images.path
FROM songs
LEFT JOIN images ON (images.id = songs.image_id)
WHERE songs.image_id != 0
UNION
SELECT songs.title, images.path
FROM songs
LEFT JOIN albums ON (songs.album_id = albums.id)
LEFT JOIN images ON (images.id = albums.image_id)
WHERE albums.image_id != 0 AND songs.image_id = 0
Basically you first select all the songs with an image, then append all the songs with an album image if the song itself doesn't have it.
Upvotes: 1
Reputation: 21269
Something like this should work:
SELECT s.title, COALESCE(si.path, ai.path)
FROM albums AS a
INNER JOIN songs s ON a.id = s.album_id
LEFT JOIN images ai ON i.id = a.image_id
LEFT JOIN images si ON i.id = s.image_id
Note that you have to join the images
table to the albums
table AND to the songs
table. ai
and si
serve as separate namespacing for these joins so that COALESCE
knows how to find options to choose from.
(Note that the INNER JOIN
is debatable depending on how you expect your data to look.)
Upvotes: 3