Jeff Beagley
Jeff Beagley

Reputation: 1025

SQL - Left Join many-to-many only once

I have a two tables that are setup like the following examples

tablea

ID | Name
1  | val1
1  | val2
1  | val3
2  | other1
3  | other

tableb

ID | Amount
1  | $100
2  | $50

My desired output would be to left join tableb to tablea but only join tableb once on each value. ID is the only relationship

tablea.ID | tablea.Name | tableb.id | tableb.amount
1         | val1        | 1         | $100
1         | val2
1         | val3
2         | other1      | 2         | $50
3         | other     

Microsoft SQL

Upvotes: 0

Views: 174

Answers (3)

sarin
sarin

Reputation: 5307

You can do the following:

select ROW_NUMBER() OVER(ORDER BY RowID ASC) as RowNum, ID , Name
from tablea

which gives you :

RowNum | RowID | Name
1      | 1     | val1
2      |1      | val2
3      |1      | val3
4      |2      | other1
5      |3      | other

You then get the minimum row number for each RowID:

Select RowId, min(RowNum)
From (
    select ROW_NUMBER() OVER(ORDER BY RowID ASC) as RowNum, ID , Name
    from tablea )
Group By RowId

Once you have this you can then join tableb onto tablea only where the RowId is the minimum

WITH cteTableA As (
    select ROW_NUMBER() OVER(ORDER BY RowID ASC) as RowNum, ID , Name
    from tablea ), 

cteTableAMin As (
    Select RowId, min(RowNum) as RowNumMin
    From cteTableA
    Group By RowId
    )
Select a.RowID, a.Name, b.Amount
From cteTableA a
Left join cteTableAMin amin on a.RowNum = amin.RowNumMin
                            and a.ID = amin.RowId
Left join tableb b on amin.ID = b.ID

This can be tidied up... but helps to show whats going on.

Upvotes: 1

Sysketov Alexey
Sysketov Alexey

Reputation: 115

Use:

select
  a.id,
  a.name,
  b.amount
from
   (select
      id,
      name,
      row_number() over (partition by id order by name) as rn
      from tablea) a
left join (
    select
      id,
      amount,
      row_number() over (partition by id order by amount) as rn
    from tableb) b
  on a.id = b.id
    and a.rn = b.rn
order by a.id, a.name

Upvotes: 0

Charles Bretana
Charles Bretana

Reputation: 146499

Then you MUST specify which row in tableA you wish to join to. If there are more than one row in the other table, How can the query processor know which one you want ?

If you want the one with the lowest value of name, then you might do this:

Select * from tableB b
    join tableA a
       on a.id = b.Id
          and a.name =
             (Select min(name) from tableA
              where id = b.id)

but even that won't work if there multiple rows with the same values for both id AND name. What you might really need is a Primary Key on tableA.

Upvotes: 0

Related Questions