简体   繁体   中英

Not getting all records from Table A in left join

I have a table of Quality Assurance statuses for recipes and want to select all 'discard' records, including info from 'perfect' statuses where they overlap. However, I'm only getting the intersection and I want all 'discard' records.

I want to perform a left join that will give me all 'discard' rows from the recipe_qa table, joined with any 'perfect' rows.

select *
from recipe_qa as bad
left join recipe_qa as good on good.id = bad.id
where bad.type = 'discard'
and good.type = 'perfect'

The query above is returning rows where there is a 'perfect' and a 'discard' record (24 rows) for the id. What I want is a query that will give me all the 'discard' rows (76 rows), with either the 'perfect' id or a null where there is no corresponding row.

Here's a fiddle: http://sqlfiddle.com/#!2/faa49/4

What am I doing wrong?

Put simply, your where clause eliminates them.

When working with left (or outer) joins you have to consider when the data limit is imposed vs when the Cartesian is created.

Say you want all records from one table (A) and only those that match in the other (B). When the join is executed, NULL values will be present in B (unmatched records to A). Adding a limiting criteria (where clause) against B fields, will in-fact eliminate records you want from A; as the where clause executes AFTER the join. This has the same effect as if you started with an INNER JOIN! (in this case good.type = 'perfect' will eliminate all those records where bad.type = 'discard' because when good.id doesn't exist for bad.id, good.type will be null, not 'perfect'; thus the where eliminates such records)

This situation can be avoided simply by moving the limiting criteria on the B table to the join when using outer joins. This way the Cartesian is generated AFTER the limit has been imposed ensuring the "All Records" from table A remains all records. If not on the join, as you've seen, the limit is imposed after the Cartesian and thus null values are removed, and you no longer get "All Records" thus the LEFT join is negated. It's as if you were doing an INNER join in the first place. This is why doing an OR statement to return nulls and the value also works but only if it is a NOT NULL column (or type of null has no semantic meaning) as LC points out in comments below.

In this case your where clause of good.type is eliminating the results of the left join so either add the criteria to the join which forces the limits before the Cartesian is generated (allowing the nulls to exist)

select *
from recipe_qa as bad
left join recipe_qa as good 
  on good.id = bad.id
 and good.type = 'perfect'
where bad.type = 'discard'

http://sqlfiddle.com/#!2/faa49/8/0

OR use an is null condition to not exclude records from left join. This has some risks indicted in the below comments.

select *
from recipe_qa as bad
left join recipe_qa as good 
  on good.id = bad.id
where bad.type = 'discard'
 and (good.type = 'perfect' or good.type is null)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM