views:

227

answers:

6

I have the following query:

select * from ACADEMIC a
left join RESIDENCY r on a.PEOPLE_CODE_ID = r.PEOPLE_CODE_ID
where a.ACADEMIC_TERM='Fall' 
and r.ACADEMIC_TERM='Fall'
    and a.ACADEMIC_SESSION=''
and a.ACADEMIC_YEAR = (Select Year(GetDate())) 
and r.ACADEMIC_YEAR = (Select Year(GetDate()))
and (CLASS_LEVEL LIKE 'FR%'
     OR a.CLASS_LEVEL LIKE 'SO'
     OR a.CLASS_LEVEL LIKE 'JR'
     OR a.CLASS_LEVEL LIKE 'SR%') 
and r.RESIDENT_COMMUTER='R'

For each person in the database it returns two rows with identical information. Yet, when I do the same query without the left join:

select * from ACADEMIC a
where a.ACADEMIC_TERM='Fall' 
    and a.ACADEMIC_SESSION=''
and a.ACADEMIC_YEAR = (Select Year(GetDate())) 
and (CLASS_LEVEL LIKE 'FR%'
     OR a.CLASS_LEVEL LIKE 'SO'
     OR a.CLASS_LEVEL LIKE 'JR'
     OR a.CLASS_LEVEL LIKE 'SR%') 
     ORDER BY PEOPLE_ID

It returns only one row for each person. I'm doing a left join - why is it adding an extra row? Shouldn't it only do that if I add a right join?

+1  A: 

The reason you're getting the extra rows is the fact you've added conditions in the where clause for the left join table. Move those conditions such as r.RESIDENT_COMMUTER='R' to the join clause and you won't get these issues.

For example

select * from ACADEMIC a
left join RESIDENCY r on a.PEOPLE_CODE_ID = r.PEOPLE_CODE_ID
and r.ACADEMIC_YEAR = (Select Year(GetDate()))
and r.RESIDENT_COMMUTER='R'
and r.ACADEMIC_TERM='Fall'
where a.ACADEMIC_TERM='Fall' 
    and a.ACADEMIC_SESSION=''
and a.ACADEMIC_YEAR = (Select Year(GetDate())) 
and (CLASS_LEVEL LIKE 'FR%'
     OR a.CLASS_LEVEL LIKE 'SO'
     OR a.CLASS_LEVEL LIKE 'JR'
     OR a.CLASS_LEVEL LIKE 'SR%')
Avitus
I'd think that adding conditions to the WHERE clause for the left joined table would only _reduce_ the number of rows that you'd get back, not increase the number of rows.
Matt Gibson
@Matt - _adding_ conditions to a where clause will only reduce rows. _Moving_ conditions to a where clause that should belong elsewhere can increase rows.
Joel Coehoorn
@Joel It's Friday afternoon here, and I'm sleepy after watching election coverage last night :) Can you help me out? Could you give me an example of a scenario where moving a condition on a column in a table you're LEFT JOINing to from the JOIN clause to the WHERE clause could increase the number of rows returned?
Matt Gibson
A: 

What's actually in your residency table? What does

SELECT 
  PEOPLE_CODE_ID, 
  COUNT(*) 
FROM 
  residency 
GROUP BY 
  PEOPLE_CODE_ID 
HAVING 
  COUNT(*) > 1

give as a result?

A LEFT JOIN will give you all the rows in your ACADEMIC table, regardless of whether or not there is a matching row in your RESIDENCY table. But if there are multiple matching rows in RESIDENCY for your join condition, you'll still get multiple rows back, just as you would with an INNER JOIN.

Matt Gibson
So, what what if I only want to return one row from the residency table? I don't want multiple rows?
davemackey
Then you have to decide _which_ row you want, and specify that as criteria, either in the JOIN, or in the WHERE clause, depending on your data and what result you actually want.If you don't care which row you want back from the residency table, then (a) I'd suggest your data design needs a second look, and (b) you have to do something nasty like SELECT DISTINCT or GROUP BY in a subquery or derived table, and join to that instead of the real table.
Matt Gibson
A: 

Not entirely sure why it is happening because I don't know the relationship between the two tables you are using (one to many, many to many, etc.). However, I can tell you that when you add a column from the "left joined" table in your WHERE clause, it cancels out the left join and makes it an inner join.

Birdman
So I should do as commented elsewhere and play the clauses as AND statements right under the join and before the where clause?
davemackey
+3  A: 

I'm doing a left join - why is it adding an extra row?

You are doing an INNER JOIN.

Conditions like this:

and r.ACADEMIC_TERM='Fall'

effectively filter out the fake rows produced by the left join, leaving only the records that would be returned by an INNER JOIN on the same conditions.

The reason because you are getting two records per academic is that there are two records in residency per academic that satisfy the other join condition.

If you want to return only one residency per academic, you need to define which one would it be.

Quassnoi
+1  A: 

Your join is taking rows that fit criteria from your left table, and pairing them with rows from the right table, in every case that "a.PEOPLE_CODE_ID = r.PEOPLE_CODE_ID" is true this combination will be returned. Obviously this condition is satisfied for 2 rows for every left hand row you have.

marr75
+1  A: 

Let's explore two differnt options for getting what you want. First you need to fix the general query to move any where conditons on table r to the join. See this link to understand why what you are doing is incoorect if you really need a left join: http://wiki.lessthandot.com/index.php/WHERE_conditions_on_a_LEFT_JOIN

And do not ever again use select * in production code, this is bad practice.

Here is the fixed query:

select [List columns here do not list the join column twice!] 
from ACADEMIC a 
left join RESIDENCY r on a.PEOPLE_CODE_ID = r.PEOPLE_CODE_ID 
and r.RESIDENT_COMMUTER='R'
and r.ACADEMIC_TERM='Fall' 
and r.ACADEMIC_YEAR = (Select Year(GetDate())) 
where a.ACADEMIC_TERM='Fall'  
    and a.ACADEMIC_SESSION='' 
and a.ACADEMIC_YEAR = (Select Year(GetDate()))  
and (CLASS_LEVEL LIKE 'FR%' 
     OR a.CLASS_LEVEL LIKE 'SO' 
     OR a.CLASS_LEVEL LIKE 'JR' 
     OR a.CLASS_LEVEL LIKE 'SR%')  

If that doesn;t give you waht you need, then you may need to do something like this:

select [List columns here  for a and r1, do not list the join column twice!] 
from ACADEMIC a 
left join(select min(people_code_id) from  RESIDENCY r on a.PEOPLE_CODE_ID = r.PEOPLE_CODE_ID 
    and RESIDENT_COMMUTER='R'
    and ACADEMIC_TERM='Fall' 
    and ACADEMIC_YEAR = (Select Year(GetDate())) ) r on a.PEOPLE_CODE_ID = r.PEOPLE_CODE_ID 
left join RESIDENCY r1 on r.PEOPLE_CODE_ID = r1.PEOPLE_CODE_ID 
where a.ACADEMIC_TERM='Fall'  
    and a.ACADEMIC_SESSION='' 
    and a.ACADEMIC_YEAR = (Select Year(GetDate()))  
    and (CLASS_LEVEL LIKE 'FR%' 
     OR a.CLASS_LEVEL LIKE 'SO' 
     OR a.CLASS_LEVEL LIKE 'JR' 
     OR a.CLASS_LEVEL LIKE 'SR%')  
HLGEM