【问题标题】:Left join results in extra records左连接导致额外的记录
【发布时间】:2013-12-11 00:34:17
【问题描述】:

这是一个基本的左连接问题,我已经阅读了许多解释发生了什么的文章,但不知何故,解决方案并没有在我的脑海中点击。我的左表有唯一的记录。我的右表对于左侧的每条记录都有几条记录。

在我一直在阅读的文章中,这通常被解释为左表有客户,右表有订单。这非常相似,但不完全是我所面临的。

在我的情况下,左表有唯一记录,而右表有重复数据要迁移到左表所在的数据库中。所以我正在尝试编写一个查询,该查询将加入两者共享的键,但我只需要右起一张记录。我得到的结果当然有多个记录,因为单个左侧匹配右侧多次。

我想我需要添加某种过滤,例如 Top(1),但仍在阅读/学习,并希望从这个列表中的聪明人那里获得反馈/方向。

这是我正在使用的简单架构:

DECLARE @Customer TABLE
(
Id int,
Name varchar(50),
email varchar(50)
)

INSERT @Customer VALUES(1, 'Frodo', 'frodo@middleearth.org')
INSERT @Customer VALUES(2, 'Bilbo', 'Bilbo@middleearth.org')
INSERT @Customer VALUES(3, 'Galadriel', 'Galadriel@middleearth.org')
INSERT @Customer VALUES(4, 'Arwen', 'Arwen@middleearth.org')
INSERT @Customer VALUES(5, 'Gandalf', 'Gandalf@middleearth.org')

DECLARE @CustomerJobs TABLE
(
Id int,
email varchar(50),
jobname varchar(50)
)

INSERT @CustomerJobs VALUES(1, 'frodo@middleearth.org', 'RingBearer')
INSERT @CustomerJobs VALUES(2, 'frodo@middleearth.org', 'RingBearer')
INSERT @CustomerJobs VALUES(3, 'frodo@middleearth.org', 'RingBearer')
INSERT @CustomerJobs VALUES(4, 'frodo@middleearth.org', 'RingBearer')
INSERT @CustomerJobs VALUES(5, 'frodo@middleearth.org', 'RingBearer')
INSERT @CustomerJobs VALUES(6, 'Bilbo@middleearth.org', 'Burglar')
INSERT @CustomerJobs VALUES(7, 'Bilbo@middleearth.org', 'Burglar')
INSERT @CustomerJobs VALUES(8, 'Bilbo@middleearth.org', 'Burglar')
INSERT @CustomerJobs VALUES(9, 'Galadriel@middleearth.org', 'MindReader')
INSERT @CustomerJobs VALUES(10, 'Arwen@middleearth.org', 'Evenstar')
INSERT @CustomerJobs VALUES(10, 'Arwen@middleearth.org', 'Evenstar')
INSERT @CustomerJobs VALUES(11, 'Gandalf@middleearth.org', 'WhiteWizard')
INSERT @CustomerJobs VALUES(12, 'Gandalf@middleearth.org', 'WhiteWizard')


SELECT 
Cust.Name,
Cust.email,
CJobs.jobname

FROM 
@Customer Cust

LEFT JOIN @CustomerJobs CJobs ON
Cjobs.email = Cust.email

我正在玩弄 row_number over partition(),因为也许我应该加入一个带有 row_number over partition 而不是表本身的 cte ???

我的另一个限制是我无法从右表中删除重复项。

对于这个过于简单化的问题,我再次表示歉意,并感谢您的帮助。

【问题讨论】:

  • 只需要右边的一条记录是什么意思?哪个记录?还是您只关心具有 ANY 记录的正确表而不关心哪条记录?在您的示例中,它们始终是同一个人的相同记录;您的所有数据都是这种情况吗?
  • 我只需要将作业名添加到左侧数据库中,由于右侧重复,我不在乎它使用哪个记录,我只想要一次。

标签: sql-server tsql left-join


【解决方案1】:

不要使用左连接,而是使用外部应用...然后您可以使用top 子句来限制返回的行...

select
    Cust.Name
,   Cust.email
,   CJobs.jobname
from @Customer Cust
 outer apply (
    select top 1 *
    from @CustomerJobs CJobs
    where Cjobs.email = Cust.email
 ) cjobs;

【讨论】:

  • 我在实践中发现这往往比 row_numbers 或 distincts 更快,但当然要尝试所有选项来确定,因为它取决于它提出的查询计划。
  • 我发现每个人的答案都非常有帮助,但是这个答案教会了我一些新的东西,那就是 APPLY 运算符。来自 Tech Net:有两种形式的 APPLY:CROSS APPLY 和 OUTER APPLY。 CROSS APPLY 仅返回外部表中从表值函数生成结果集的行。 OUTER APPLY 返回生成结果集的行和不生成结果集的行,在表值函数生成的列中具有 NULL 值。
  • 基本上,交叉应用是内连接,外应用是左连接。
【解决方案2】:

您必须想出一些人为的方法,将第二个表格减少到每封电子邮件一行。例如:

SELECT 
Cust.Name,
Cust.ID,
Cust.email,
CJobs.jobname

FROM 
@Customer Cust

LEFT JOIN 
(select min(id) as id,email, jobname
from
@CustomerJobs
group by email, jobname) as  CJobs ON
Cjobs.email = Cust.email

但这几乎是随机的。有没有办法确定您的 CustomerJobs 表中的哪一行是“正确的”行?

【讨论】:

  • AFAIK / 可以看出它们是真正相同的数据,所以它们都很好,因此我只需要一个....任何一个。
【解决方案3】:
SELECT DISTINCT
Cust.Name,
Cust.email,
CJobs.jobname

FROM 
@Customer Cust

LEFT JOIN @CustomerJobs CJobs ON
Cjobs.email = Cust.email

附加的 DISTINCT 关键字应该可以得到你想要的。

【讨论】:

    【解决方案4】:

    这将起作用:

    SELECT 
        Cust.Name,
        Cust.ID,
        Cust.email,
        CJobs.jobname
    FROM @Customer Cust
    LEFT JOIN 
        (SELECT DISTINCT email, jobname
        FROM @CustomerJobs) C2 ON C2.email = C.email
    

    【讨论】:

    • 抱歉,赶时间。是的,不需要分组。但是,我认为需要 distinct,因为该表中有多行具有相同的电子邮件和工作名称。
    • 是的,您需要一个或另一个 - 按所有列分组和使用 distinct 是等效的,即使在查询计划中也是如此。
    • 好点,两种方式都可以完成同样的事情。有趣的是,查询计划也是相同的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    • 2018-05-23
    • 2015-08-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多