【问题标题】:How do you do a condition SQL left join based on the status of the second table?如何根据第二张表的状态做条件 SQL 左连接?
【发布时间】:2018-06-28 04:36:21
【问题描述】:

所以例如我有两个表

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

然后我也有(当前实际上是一个整数,但我使用文本使其更具可读性):

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

我想加入这两个表,这样我就可以得到每个人的列表,但我只想用左连接包括当前的工作。换句话说,我希望结果是:

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

如何通过加入来做到这一点。我试着做:

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

但问题是,在这种情况下,我会看到 Jane 的两个条目,一个是她过去的 Doctor 工作以及她的新工作 Driver。如果我添加一个 where 子句,例如:

WHERE job.status='current' 

然后它从列表中删除 John 和 Mary。

如何编写一个显示所有人员但仅包含当前工作的联接?

【问题讨论】:

  • 小子,简是不是被大大降职了……从医生到司机?发生了什么?!

标签: mysql sql join jdbc left-join


【解决方案1】:

至少在 Oracle 中你应该能够做到这一点:

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

所以当person.id=job.person.id AND job.status = 'current' 进行连接时,当条件为假时,您只会得到person 数据。

我不知道 MySQL 是否支持 ON 子句中的复合条件,但我想是的,它并不是完全开创性的技术。

更新:

貌似支持,看https://dev.mysql.com/doc/refman/8.0/en/join.html

【讨论】:

  • 这就是问题所在,如果我这样做,那么它不会包括没有工作的人。换句话说,约翰和玛丽不会出现在结果中。
  • 他们为什么不呢?您有一个外连接,但连接条件不满足,因此 db 应返回主表中的值。
  • 我不敢相信我错过了这个;我已经向很多人教过 SQL,并且通常会涵盖这种确切类型的东西。我想深夜回答问题是个坏主意……
【解决方案2】:
   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;

你能试试这个查询吗?

【讨论】:

  • 约翰和玛丽将如何出现在这个查询中?
  • 它是否有效,如果是,那么我将解释否则将尝试其他一些查询:)
【解决方案3】:

试试这个

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 = '@'

【讨论】:

  • 我不明白这个查询在软管中的情况下会起作用,但无论如何它不包括任何玛丽和约翰
  • 对于 Mary 和 John 的工作状态将为空,因此查询将返回这些行。在此查询中,我将获取 job.status 为当前(如果可用)或 job.status 仅为 null 的行。如果满足任何条件,则 case 表达式将返回 @ 否则将返回 job.status
【解决方案4】:

有几种方法可以做到这一点;最佳选择取决于您的数据库以及不同方法的执行方式。

方法一:用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

方法 2:使用 UNION ALL 进行两个查询(这会删除没有当前值或空值的行)

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;

方法 2b:使用 UNION ALL 的两个查询(保留没有当前或空值的行)

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

方法三:分选

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;

【讨论】:

  • 如果说玛丽与以前的工作有关联会怎样?换句话说,玛丽有工作程序员,但状态既不是当前的也不是空的(比如潜力、过去等)
  • 好吧,你想让它做什么?根据您希望它的行为方式,我会以不同的方式处理它。方法 1 会像方法 2 一样将 Mary 从结果中排除,但方法 3 仍会返回 Mary。
  • 我刚刚意识到应该修改前两个查询(没有意识到您想要所有用户,抱歉)。实际上,第一种方法不适用于您想要的,因为您有多行,但是如果您修改第二个查询,第二种方法可以满足您的要求。让我更新原始响应。
  • 我是个白痴; @gpeche 实际上是正确的;将作业限制放在 ON 子句中,它只会在评估为 TRUE 的地方加入。因此,它将返回所有人员,并且仅返回满足 ON 子句中的所有值的工作。在这种情况下,将过滤器放在 ON 子句中的行为与 WHERE 子句不同。我不敢相信我搞砸了。无论如何,我将保留其他方法,以防它有助于学习不同的 SQL 技巧。我更新了方法 1 以反映这一点。
猜你喜欢
  • 2012-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多