'Using SQL to join tables with a junction table

I am trying to output data from 2 different tables in my database which are joined by a junction table.

Table 1: musician
Columns: musicianID, surname, fName

Table 2: musician_band
Columns: musicianID, bandID

Table 3: band
Columns: bandID, bandName, genre, yearFormed, origin

Just to start with I want to output:

fName, bandName

I tried using this query:

SELECT DISTINCT fName, bandName
FROM musician_band
JOIN musician ON musician.musicianID = musician_band.musicianID
JOIN band ON band.bandID = band.bandID

But instead of giving me a list of musicians and the bands that they're in, it is instead repeating the first name of the first musician in the databse and then a band name on the right, this is repeated for every band in the database, even where there shouldn't be a relationship between that particular musician and band. Like this:

fname         bandName
musician1     band1
musician1     band2
musician1     band3
musician2     band1
musician2     band2
musician2     band3

etc...

How can I correct this so that it only outputs musicians in their correct bands?



Solution 1:[1]

There is a problem with your aliases. You need distinct alias for each referenced tables in your JOIN:

This sample query would work fine:

SELECT DISTINCT fName, bandName
FROM @musician_band mb
JOIN @musician m ON m.musicianID = mb.musicianID
JOIN @band b ON mb.bandID = b.bandID

Using this sample in SQL Server:

declare @musician table(musicianID int, surname varchar(50), fName varchar(50));
insert into @musician(musicianID, surname, fName
) values
( 1, 'Pete Doherty', 'Pete Doherty')
, ( 2, 'Damon Albarn', 'Damon Albarn')

declare @musician_band table(musicianID int, bandID int);
insert into @musician_band(musicianID, bandID) values
(1, 1)
, (1, 2)
, (2, 3)
, (2, 4)

declare @band table(bandID int, bandName varchar(50), genre varchar(50), yearFormed int, origin varchar(50));
insert into @band(bandID, bandName, genre, yearFormed, origin
) values
( 1, 'The Libertines', '', 0, '')
, (2, 'Babyshambles', '', 0, '')
, (3, 'Blur', '', 0, '')
, (4, 'Gorillaz', '', 0, '')

The output is:

fName           bandName
Damon Albarn    Blur
Damon Albarn    Gorillaz
Pete Doherty    Babyshambles
Pete Doherty    The Libertines

Just replace @tablename by your own table. Is DISTINCT useful in this case?

Solution 2:[2]

You have not joined your tables correct

JOIN band ON band.bandID = band.bandID

Will get 1 row for EACH row in band.

Try this instead:

SELECT DISTINCT fName, bandName
FROM musician_band
JOIN musician ON musician.musicianID = musician_band.musicianID
JOIN band ON band.bandID = musician_band.bandID

You may even be able to remove the keyword DISTINCT with this solution

Solution 3:[3]

Assuming you want to include solo musicians:

select fName, bandName
from musician m
  left join musician_band mb on mb.musicianID = m.musicianID
  inner join band b on b.bandID = mb.bandID
order by fName, bandName

Even if you keep the inner join instead of left join, conceptually it's still ideal for the query to begin with the musician table, not musician_band. If you wanted musicians per band, the table order would be reversed. Either way there's no logic in starting with the junction table.

Multiple rows per musician is the expected result.

If you want this instead:

fName         bandName
musician1     band1, band2, band3
musician2     band1, band2, band3

That requires a stored procedure or your app's code looping the query to build your desired data structure for output to the user.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Julien Vavasseur
Solution 2 t-clausen.dk
Solution 3 Walter Monroe