如何从另一个 SQL 表中获取两个不同列的匹配数据:Inner Join 和/或 Union?

Posted

技术标签:

【中文标题】如何从另一个 SQL 表中获取两个不同列的匹配数据:Inner Join 和/或 Union?【英文标题】:How to get matching data from another SQL table for two different columns: Inner Join and/or Union? 【发布时间】:2015-02-25 05:56:11 【问题描述】:

我在 MS Access 中有两个表格,用于跟踪班级辅导员和他们主持的班级。这两个表的结构如下:

tbl_facilitators

facilID -> a unique autonumber to keep track of individual teachers
facilLname -> the Last name of the facilitator
facilFname -> the First name of the facilitator

tbl_facilitatorClasses

classID -> a unique autonumber to keep track of individual classes
className -> the name of the class (science, math, etc)
primeFacil -> the facilID from the first table of a teacher who is primary facilitator
secondFacil -> the facilID  from the first table of another teacher who is backup facilitator

我不知道如何编写一个以这种格式提取结果的内部联接:

Column 1:  Class Name
Column 2:  Primary Facilitator's Last Name
Column 3:  Primary Facilitator's First Name
Column 4:  Secondary Facilitator's Last Name
Column 5:  Secondary Facilitator's First Name

如果我只单独请求主要协调人或仅单独请求次要协调人,我能够提出并获得正确的结果。不过,我不能让他们两个都解决。

这是我的工作内连接:

SELECT tbl_facilitatorClasses.className,
    tbl_facilitators.facilLname, tbl_facilitators.facilFname
FROM tbl_facilitatorClasses
INNER JOIN tbl_facilitators
ON tbl_facilitatorClasses.primeFacil = tbl_facilitators.facilID;

出于绝望,我也尝试了一个 Union,但它并没有像我希望的那样成功。非常感谢您的帮助。在这一点上,我真的很难取得任何进展。我不经常使用 SQL。

解决方案

感谢@philipxy,我提出了以下查询,该查询最终有效:

SELECT tblCLS.className,
    tblP.facilLname, tblP.facilFname, tblS.facilLname, tblS.facilFname
FROM (tbl_facilitatorClasses AS tblCLS
INNER JOIN tbl_facilitators AS tblP
ON tblCLS.primeFacil=tblP.facilID)
INNER JOIN tbl_facilitators AS tblS
ON tblCLS.secondFacil=tblS.facilID;

在MS Access中执行多个Inner Joins时,需要括号...As described in this other post.

【问题讨论】:

根据@popovitsj 的建议,在第一个内部联接之后立即尝试了第二个内部联接,但没有成功。 MS Access 返回“语法错误(缺少运算符)”消息。 您需要告诉我们在什么情况下您的表中会出现 NULL,以及在什么情况下您希望结果中有 NULL。即辅导员和班级总是有名字吗?一个班级是否总是有一个主要和/或备用促进者?除非您告诉我们,否则我们无法知道这一点。 【参考方案1】:

(当每一行都是 SQL DISTINCT 时,以下适用,并且外部 SQL 代码同样将 NULL 视为另一个值。)

每个基表都有一个语句模板,也就是 谓词,由列名参数化,我们可以通过该模板将一行放入或排除。我们可以为谓词使用(标准谓词逻辑)简写,就像它的 SQL 声明一样。

-- facilitator [facilID] is named [facilFname] [facilLname]
facilitator(facilID, facilLname, facilFname)
-- class [classID] named [className] has prime [primeFacil] & backup [secondFacil]
class(classID, className, primeFacil, secondFacil)

将一行插入谓词会给出一个陈述,即命题。提出真命题的行放在表中,提出假命题的行留在表中。 (因此,表格说明了每个当前行的命题并且不说明每个缺席行的命题。)

-- facilitator f1 is named Jane Doe
facilitator(f1, 'Jane', 'Doe')
-- class c1 named CSC101 has prime f1 & backup f8
class(c1, 'CSC101', f1, f8)

但是每个表表达式值都有一个谓词。 SQL 的设计使得如果表 TU 分别保存 T(...) 和 U(...) 的(NULL-free non-duplicate)行,那么:

T CROSS JOIN U 包含 T(...) AND U(...) 的行 T INNER JOIN U ONcondition 包含 T(...) AND U(...) AND condition 的行 T LEFT JOIN U ONcondition 保存行,其中(对于 U-only 列 U1,...) T(...) AND U(...) AND 条件 或 T(...) AND NOT 存在 U1 的值,... where [U(...) AND condition] AND U1 为 NULL AND ... T WHEREcondition 包含 T(...) AND 条件 的行 T INTERSECT U 保存有 T(...) AND U(...) 的行 T UNION U 包含 T(...) OR U(...) 的行 T EXCEPT U 包含 T(...) AND NOT U(...) 的行 SELECT DISTINCT * FROM T 保存有 T(...) 的行 SELECT DISTINCTcolumns to keepFROM T 保存行其中要删除的列有 EXISTS 值 其中 T(...) VALUES (C1, C2, ...)((v1,v2, ...), ...) 保存行其中 C1 = v1 AND C2 = v2 AND ... OR ...

还有:

(...) IN T 表示 T(...) scalar= T 表示 T(标量) T(..., X, ...) AND X = Y 表示 T(..., Y, ...) AND X = Y

因此,为了查询,我们找到了一种方法,用自然语言使用基表谓词来表达我们想要的行的谓词,然后使用基表谓词的简写形式,然后在除输出列之外的列名中使用别名的简写形式,然后在 SQL 中使用基表名称加上 ON 和 WHERE 条件等。如果我们需要两次提及基表,那么我们给它起别名。

-- natural language
there EXISTS values for classID, primeFacil & secondFacil where
    class [classID] named [className]
        has prime [primeFacil] & backup [secondFacil]
AND facilitator [primeFacil] is named [pf.facilFname] [pf.facilLname]
AND facilitator [secondFacil] is named [sf.facilFname] [sf.facilLname]

-- shorthand
there EXISTS values for classID, primeFacil & secondFacil where
    class(classID, className, primeFacil, secondFacil)
AND facilitator(pf.facilID, pf.facilLname, pf.facilFname)
AND pf.facilID = primeFacil
AND facilitator(sf.facilID, sf.facilLname, sf.facilFname)
AND sf.facilID = secondFacil

-- shorthand using aliases everywhere but result
-- use # to distinguish same-named result columns in specification
there EXISTS values for c.*, pf.*, sf.* where
    className = c.className
AND facilLname#1 = pf.facilLname AND facilFname#1 = pf.facilFname
AND facilLname#2 = sf.facilLname AND facilFname#2 = sf.facilFname
AND class(c.classID, c.className, c.primeFacil, c.secondFacil)
AND facilitator(pf.facilID, pf.facilLname, pf.facilFname)
AND pf.facilID = c.primeFacil
AND facilitator(sf.facilID, sf.facilLname, sf.facilFname)
AND sf.facilID = c.secondFacil

-- table names & SQL (with MS Access parentheses)
SELECT className, pf.facilLname, pf.facilFname, sf.facilLname, sf.facilFname
FROM (class JOIN facilitator AS pf ON pf.facilID = primeFacil)
JOIN facilitator AS sf ON sf.facilID = secondFacil

当一个类并不总是有两个促进者或某些东西并不总是具有所有名称时,将使用OUTER JOIN。 (即,如果一列可以为 NULL。)但是您没有为您的基表和查询提供特定的谓词或关于何时事物可能为 NULL 的业务规则,所以我假设没有 NULL。

Is there any rule of thumb to construct SQL query from a human-readable description?

(关于 MS Access JOIN 括号,请参阅 this from SO 和 this from MS。)

【讨论】:

非常感谢。这是我需要的解决方案。我反映了您的 SELECT 语句并提出了一个有效的查询。我感激不尽。【参考方案2】:

我会通过两次加入 tbl_facilitators 表来做到这一点,但您可能希望确保每个课程确实需要第二个促进者,因为第二个连接应该是外部连接。事实上,假设它不是必填字段可能更安全。

【讨论】:

不幸的是,在这种情况下总是需要一个备份/辅助引导者。我将按照您的建议研究外部联接。 @LeviTonet Outer join(s) 是如果一个类总是有两个促进者或某物总是有所有名称。 (即如果一列可以为 NULL。)【参考方案3】:

只需为次要促进者做一个额外的连接(请使用表别名!):

SELECT fc.className, f1.facilLname, f2.facilFname
FROM tbl_facilitatorClasses fc
INNER JOIN tbl_facilitators f1 ON fc.primeFacil = f1.facilID
INNER JOIN tbl_facilitators f2 ON fc.secondFacil = f2.facilID;

【讨论】:

这在精神上是正确的,但它不是 MS Access 语法。您需要 as 用于连接周围的表别名和括号。 尝试了您的第二个内部连接,但在我尝试时收到了语法错误(缺少运算符)。很抱歉没有使用别名。以后我会牢记这一点。

以上是关于如何从另一个 SQL 表中获取两个不同列的匹配数据:Inner Join 和/或 Union?的主要内容,如果未能解决你的问题,请参考以下文章

如何从另一张表中获取完全匹配的数据?

查询列的 SQL 连接问题

基于匹配值的雪花SQL计数和从另一个表求和

如何从 SQL Server 中的表中获取不匹配的数据

从另一个表中逐行计数,在sql中的两个表上保存一些条件

如果存在匹配值,则从另一个表中检索数据 - 否则基于第一个连接条件返回