简体   繁体   中英

How do you do a condition SQL left join based on the status of the second table?

So for example I have two tables

Person Table
ID : Name
1 : Bob
2 : Jane
3 : Mike
4 : John
5: Mary

I then also have (current is actually an integer but I used text to make it more readable):

Job Table
ID : Name : PersonID : JobStatus
1 : Plumber : 1 : current
2 : Doctor : 2 : past
3 : Driver : 2 : current
4 : Cook : 3 : current
5: Programmer : 5 : past

I would like to join the two tables so that I can get a list of everyone, but I only want to include current jobs with the left join. In other words I want the result to be:

Bob : Plumber : current
Jane : Driver : current
Mike : Cook : current
John : :  
Mary : : 

How can I do this with a join. I tried doing:

SELECT 
  person.name, job.name, job.status 
FROM 
  person
LEFT JOIN 
  job ON person.id=job.person.id

But the problem is that in this case I will see two entries for Jane, one for her past job of Doctor as well as her new job of Driver. And if I add a where clause such as:

WHERE job.status='current' 

Then it removes removes John and Mary from the list.

How can I write a join that will show all the people yet only include jobs that are current?

At least in Oracle you should be able to do this:

SELECT 
  person.name, job.name, job.status 
FROM 
  person
LEFT JOIN 
   job ON person.id=job.personId AND job.status = 'current';

so when person.id=job.person.id AND job.status = 'current' the join is made, and when that condition is false you only get person data.

I don't know if MySQL supports composite conditions in ON clauses but I guess so, it is not exactly groundbreaking technology.

UPDATE:

Looks like it is supported, read https://dev.mysql.com/doc/refman/8.0/en/join.html

There are a few ways to do this; the best option depends on your database, and how the differing methods perform.

Method 1: Left Join with OR

SELECT p.name, j.name, j.status
FROM person AS p
LEFT JOIN job AS j ON j.personId = p.id AND j.status = 'current';
-- Returns all persons, and only jobs w/ value 'current'
-- If the filter on j.status were in the WHERE clause, it would remove non-matching

Method 2: Two queries with UNION ALL (this removes rows w/ no current or null vals)

SELECT p.name, j.name, j.status
FROM person AS p
INNER JOIN job AS j ON j.personId = p.id
WHERE j.status = 'current'
OR j.status IS NULL

UNION ALL

SELECT p.name, j.name, j.status
FROM person AS p
LEFT JOIN job AS j ON j.personId = p.id
WHERE j.status IS NULL;

Method 2b: Two queries with UNION ALL (this retains rows w/ no current or null vals)

SELECT p.name, j.name, j.status
FROM person AS p
INNER JOIN job AS j ON j.personId = p.id
WHERE j.status = 'current'
OR j.status IS NULL

UNION ALL

SELECT p.name, j.name, j.status
FROM person AS p
LEFT JOIN job AS j ON j.personId = p.id
WHERE NOT EXISTS 
  (SELECT 1 FROM jobs AS t WHERE t.personId=p.Id AND t.status='current')
GROUP BY p.Id; 
-- GROUP BY ensures we only get one row back per person, but will be random if there are multiples

Method 3: Sub-selects

SELECT p.name, j.name, j.status
FROM person AS p
LEFT JOIN (
  SELECT x.name
  FROM job AS x
  WHERE x.status = 'current'
) AS j;
   Select p.name, j.name,j.status 
     From person p 
Left Join
         (Select name,status,personId 
            From job 
            Where status ='current'
         ) j On p.id = j.personId;

Can you try this query?

Try this

SELECT 
  person.name, job.name, job.status 
FROM 
  person
LEFT JOIN 
  job ON person.id=job.person.id
WHERE CASE WHEN job.status IS NULL THEN '@' WHEN job.status ='current' THEN '@' else job.status END = '@'

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