【问题标题】:How to return null on a left join field if value not exists如果值不存在,如何在左连接字段上返回 null
【发布时间】:2017-04-24 14:04:26
【问题描述】:

我有五 (5) 个表:students、subjects、period_one、period_two 和 period_three。我正在执行左连接以从这五个表中选择值:

学生桌(学生)

  student_id    | Name
  --------------|-------
  1             |John
  2             |Peter
  3             |Flomo 

科目表(科目)

  subject_id       |SubjectName
  -----------------|-------
  math101          |Mathematics
  eng201           |English
  lang303          |Language Arts

一期表(period_one)

  id|student_id |subject_id| score
  --------------|----------|-----
  1 |1          | math101  |99
  2 |2          | eng201   |88
  3 |3          | lang303  |77 

周期二表(period_two)

  id|student_id |subject_id| score
  --------------|----------|-----
  1 |1          | math101  |100
  2 |2          | eng201   |60
  3 |3          | lang303  |65 

周期三表(period_three)

  id|student_id |subject_id| score
  --------------|----------|-----
  1 |1          | math101  |71
  2 |2          | eng201   |51
  3 |3          | lang303  |71

这是我用来检索记录的查询Query1

SELECT period_one.student_id, period_one.subject_id, period_one.score, period_two.score,period_three.score

from period_one

LEFT JOIN period_two
ON period_one.subject_id = period_two.subject_id
AND period_one.student_id = period_two.student_id

LEFT JOIN period_three 
on period_one.subject_id = period_three.subject_id
AND period_one.student_id = period_three.student_id

WHERE period_one.student_id = 10      

上面代码的问题是,如果我要查找的学生 id 不在第一个表中(periodOne),则左连接应用于查询返回 null,即使该学生的记录在其他表中表(周期二和周期三)。我查找了问题 (https://www.w3schools.com/sql/sql_join_left.asp) 并确认这不是最好的处理方式。

然后我做了一些更改

因此,我将查询更新为如下所示(Query2):

SELECT students.student_id, period_one.score, period_one.subject_id, 
period_two.score, period_two.subject_id, period_three.score, 
period_three.subject_id
from students

LEFT JOIN period_one
ON students.student_id = period_one.student_id

LEFT JOIN period_two
ON students.student_id = period_two.student_id

LEFT JOIN period_three 
ON students.student_id = period_three.student_id

WHERE students.student_id = 3 或 period_one.student_id = 3 或 period_two.student_id = 3 或 period_three.student_id = 3

这很完美,因为学生表是所有周期表引用的主表。这样,如果学生 ID 不在 period_one 和 period_two 表中而是在 period_there 中,则返回该表的 studentId、subjectId 和 score。

然后又出现了一个问题

在我更新我的代码之前,我以我想要的方式显示记录,但没有以期望的方式获取/检索记录。这就是促使我更改查询的原因,因为我注意到这是问题所在。

现在,根据我的第一个查询 (Query1),我从 select 语句中的各个表中选择了 subject_id。当我显示记录时,我将查询返回的 subject_id 传递到获取该 ID 的主题名称的函数中。这就是我显示结果的方式:

如果学生 ID 在分配给 from 子句的表中,则此方法有效,否则不返回任何内容。这就是我更改代码的原因。

但现在我将代码更改为 (Query2) 我无法显示主题 ID 及其名称,因为它不在学生表中。以下是关于我如何显示我的记录的要点:https://gist.github.com/nathansiafa/e9d22791800d4ba3a00e2b98de52baec

有什么方法可以让它更好地工作吗?将感谢建议和反馈。谢谢!

【问题讨论】:

  • 如果您可以发布创建表查询,它会更容易提供帮助。
  • students.current_class_id = period_one.class_id 既不是 students 也不是 period_one 表有这些列。你能提供当前表格的结构吗?
  • 另外,如果您可以包含一些示例数据,那也会很有帮助。
  • @Stefano Zanini 对不起。你们是对的。但我尽我所能删除一些现在不需要的东西。在我的主要项目中, students.current_class_id = period_one.class_id 在那里,但我觉得这里没有必要,因为它对我想要完成的事情没有太大影响。请看一下我已经做了一些更新的代码。

标签: php mysql database join


【解决方案1】:

从技术/程序的角度来看,您似乎有点想多了,没有关注您想要拥有的数据的语义含义。

您真正需要的是科目的列表,以及特定学生的这些科目的分数。学生在这里有些正交,因为他们正在构建一个表,但学生不是该表的一部分。

所以第一步是选择我们想要的数据 - 科目和他们的分数:

SELECT s.subject_id, s.subject_name, p1.score period_1_score, p2.score period_2_score, p3.score period_3_score 
FROM subject s
LEFT JOIN period_one p1 ON p1.subject_id = s.subject_id 
    AND p1.student_id = 10
LEFT JOIN period_two p2 ON p2.subject_id = s.subject_id 
    AND p2.student_id = 10
LEFT JOIN period_three p3 ON p3.subject_id = s.subject_id 
    AND p3.student_id = 10
ORDER BY s.subject_name;

这将为您提供所需的表格数据 - 首先是所有科目,然后是它们所在的分数。

现在,如果您坚持在同一个查询中加载学生数据(我建议为此单独查询 SELECT * FROM student WHERE student_id=10),那么您可以将其左连接:

SELECT s.subject_id, s.subject_name, p1.score period_1_score, p2.score period_2_score, p3.score period_3_score, st.name student_name
FROM subject s
LEFT JOIN period_one p1 ON p1.subject_id = s.subject_id 
    AND p1.student_id = 10
LEFT JOIN period_two p2 ON p2.subject_id = s.subject_id 
    AND p2.student_id = 10
LEFT JOIN period_three p3 ON p3.subject_id = s.subject_id 
    AND p3.student_id = 10
LEFT JOIN student st ON st.student_id = 10
ORDER BY s.subject_name;

【讨论】:

  • @mkilmans 你的想法很棒。我没有教过这个,但是我如何结合这两个选择查询?如果那是你要我做的。
  • @NathanSiafa 您不需要将它们组合在一起 - 第二个查询已经与第一个相同,但在末尾有额外的选择列和额外的左连接
  • 这很好用,但唯一的问题是它返回所有科目,而它应该只返回学生得分的科目。很抱歉,我忘记在帖子中提及这一点。有没有办法改变查询?
  • 再次 - 使用相同的方法尝试考虑 what 您想要做什么,很自然地您希望根据这个新要求过滤结果,并且这就是WHERE 的用途。只需在最后一个 LEFT JOINORDER BY 之间添加 WHERE p1.score IS NOT NULL OR p2.score IS NOT NULL OR p3.score IS NOT NULL
【解决方案2】:

对我来说,问题是数据不是正常形式导致您出现问题。由于科目和分数基本上是相同的结构,只是没有句点符号,我首先将所有三个表合并在一起,并在结果中创建一个“句点”列。

SELECT X.*, 1 as Period from Period_one X
UNION ALL 
SELECT Y.*, 2 as Period FROM PERIOD_TWO Y
UNION ALL
SELECT Z.*, 3 as Period FROM PERIOD_THREE Z

然后,我将其用作内联视图,女巫成为左连接的一部分,因此在您的第一个查询中,我们不会遇到某些时期缺少学生的问题。

SELECT Student_ID
     , max(Case when Period = 1 then B.Subject_ID end) as Period_one_Subject
     , max(case when Period = 1 then B.Score end) as Period_one_Score 
     , max(Case when Period = 2 then B.Subject_ID) end as Period_Two_Subject
     , max(case when Period = 2 then B.Score end as Period_two_Score 
     , max(Case when Period = 3 then B.Subject_ID end) as Period_Three_Subject
     , max(case when Period = 3 then B.Score end) as Period_Three_Score 
FROM STUDENTS S
LEFT JOIN (SELECT Y.*, 1 as Period from Period_one X
           UNION ALL 
           SELECT X.*, 2 as Period FROM PERIOD_TWO Y
           UNION ALL
           SELECT Z.*, 3 as Period FROM PERIOD_THREE Z) B
 on S.Student_ID = B.Student_ID
GROUP BY Student_ID

我们使用 max 和 group by student 来“透视”数据,使我们能够同时显示分数和主题。

如果我们需要实际的主题名称,只需根据 subject_ID 键左连接到您的主题表即可。

我假设一个学生在你的每个周期表中只能出现一次。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-15
    • 1970-01-01
    相关资源
    最近更新 更多