LEFT OUTER JOIN 未按预期运行
Posted
技术标签:
【中文标题】LEFT OUTER JOIN 未按预期运行【英文标题】:LEFT OUTER JOINs not acting as expected 【发布时间】:2014-02-20 21:58:50 【问题描述】:查询 #1
SELECT
dbo.CLIENT.CLIENT_ID, dbo.CLIENT.GOC, dbo.SALES_UW_REGION.SALES_UNDERWRITING
FROM dbo.CLIENT LEFT OUTER JOIN
dbo.SALES_UW_REGION ON dbo.CLIENT.GOC = dbo.SALES_UW_REGION.GOC
WHERE (dbo.CLIENT.CLIENT_ID = 23721)
CLIENT_ID, GOC, SALES_UNDERWRITING
23721 332 Underwriting
23721 332 Sales
我可以理解为什么这只会返回一行,原因是尽管 LEFT 外连接确保返回两个 CLIENT 记录,即使它们不匹配,但在连接之后应用了 FILTER,因此结果集只有一个行。
查询 #2
SELECT
dbo.CLIENT.CLIENT_ID, dbo.CLIENT.GOC, dbo.SALES_UW_REGION.SALES_UNDERWRITING
FROM dbo.CLIENT LEFT OUTER JOIN
dbo.SALES_UW_REGION ON dbo.CLIENT.GOC = dbo.SALES_UW_REGION.GOC
WHERE (dbo.CLIENT.CLIENT_ID = 23721)
and SALES_UW_REGION.SALES_UNDERWRITING = 'Sales '
CLIENT_ID GOC SALES_UNDERWRITING
23721 332 Sales
但是,如果我将“SALES”过滤器移动到 JOIN 子句,我惊讶地发现仍然返回一行。
查询 #3
SELECT
dbo.CLIENT.CLIENT_ID, dbo.CLIENT.GOC, dbo.SALES_UW_REGION.SALES_UNDERWRITING
FROM dbo.CLIENT LEFT OUTER JOIN
dbo.SALES_UW_REGION ON dbo.CLIENT.GOC = dbo.SALES_UW_REGION.GOC
and SALES_UW_REGION.SALES_UNDERWRITING = 'Sales '
WHERE (dbo.CLIENT.CLIENT_ID = 23721)
CLIENT_ID GOC SALES_UNDERWRITING
23721 332 Sales
我预计,因为它是 JOIN 子句的一部分,并且连接是 LEFT OUTER,所以我会得到 2 行。一般来说,如果 JOIN 条件涉及两列的相等匹配,一列来自 LEFT 表,另一列来自 RIGHT 表,则在 LEFT OUTER JOIN 中,LEFT 表将返回 NULL 值,用于从对。如果我们将 RIGHT 表中的值与文字匹配,为什么会有任何不同?不应该返回 LEFT 表中的行吗?
伙计,我认为这是我已经掌握的基本内容......
这是你需要重新创建我所做的事情:
CREATE TABLE [dbo].[CLIENT](
[CLIENT_ID] [bigint] NOT NULL,
[GOC] [char](3) NULL
)
go
CREATE TABLE [dbo].[SALES_UW_REGION](
[GOC] [char](3) NOT NULL,
[SALES_UNDERWRITING] [varchar](12) NULL
)
go
INSERT INTO [dbo].[CLIENT]([CLIENT_ID], [GOC])
SELECT 23721, N'332'
go
INSERT INTO [dbo].[SALES_UW_REGION]([GOC], [SALES_UNDERWRITING])
SELECT N'332', N'Underwriting' UNION ALL
SELECT N'332', N'Sales'
go
【问题讨论】:
LEFT OUTER JOINS 永远不会按预期运行 :) 我相信左表(客户端)只有一条记录,它被重复的次数与右侧的匹配行一样多。当您过滤右表时,无论在哪里,笛卡尔积中的行数都会减少。唯一的例外是当右侧没有行时,您仍然会得到左行 - 这就是为什么存在左连接。 你一定是对的,但我觉得奇怪的是,如果我在 LEFT.COL1 = RIGHT.COL2 上执行 LEFT OUTER JOIN,如果 RIGHT 上没有匹配的记录,则 LEFT 行仍然是返回,但如果我替换 LEFT 上的文字,例如 'X' = RIGHT.COL2,那么 RIGHT 上匹配行的存在可能会影响 LEFT 行是否返回。好像不一致 【参考方案1】:我认为您的困惑涉及对第一个结果集的误解:
CLIENT_ID、GOC、SALES_UNDERWRITING 23721 332 承保 23721 332 销售
虽然此结果集中有两行,但结果仅代表 Client 表中的一行数据。连接条件允许 Client 表中的单行匹配 Sales_UW_Region 表中的两行,因此 Client 表中该行的数据在结果集中重复。一开始这里只有一个Client记录,证明只有一个Client_ID,但是该记录的数据显示了两次:Sales_UW_Region中的每个匹配记录一次。
稍后,当您在连接的ON
子句中包含and SALES_UW_REGION.SALES_UNDERWRITING = 'Sales '
条件时,Client 表中的原始单条记录仅匹配Sales_UW_Region 表中的一条记录。该行的数据不再需要复制,因此只返回一行。
【讨论】:
啊!当我将 SALES_UW_REGION_Table 中的两个记录都更改为“Underwriting”并执行仅针对 SALES 记录的查询 3 时,我得到 1 行返回给客户端,其中 SALES_UNDERWRITING 值为 NULL。这表明 对于 LEFT 表中的给定记录,LEFT OUTER JOIN 将确保 LEFT 行在整个结果集中至少出现一次,LEFT OUTER JOIN 并不意味着对于每个潜在的连接在匹配 CLIENT 值时,对于在 CLIENTID 上匹配但在连接过滤器中的其他条件上匹配失败的行,一行将出现在结果集中。【参考方案2】:您的CLIENT
表中有一行CLIENT_ID = 23721
。
您将加入另一个具有多行 CLIENT_ID = 23721
的表(当在您的第一个查询中加入条件时),但是当连接条件发生更改时,它会从 RIGHT
表中排除其中一个行。
您将从LEFT
表中获取所有记录,无论它们是否加入RIGHT
表中的记录,正如预期的那样。
【讨论】:
是的,这听起来也不错,但我发现 Joel 的回答更容易理解。投票赞成,谢谢【参考方案3】:你的理解是正确的。如果过滤器在 JOIN 的 ON 子句中,它不会从第一个表中删除行。
我认为您需要在其他地方寻找您的问题...您真的是要在 'Sales '
常量中添加一个尾随空格吗?
这是一个 JSFiddle 来测试这个:http://sqlfiddle.com/#!2/bfe32/3/0
【讨论】:
我喜欢 Fiddler 的例子,但是当我点击链接时它对我不起作用,它都是空白的。谢谢。 关于尾随空格,(调整后的)SQL 是由 BI 报告生成的,我不会像那样手动编写它,但数据库设置忽略 char 和 varchar 字段中的尾随空格。不确定这是默认设置还是我处理的每个 SQL 数据库选项都选择了此设置。以上是关于LEFT OUTER JOIN 未按预期运行的主要内容,如果未能解决你的问题,请参考以下文章