如果多个条件之一为真,我如何执行 JOIN?

Posted

技术标签:

【中文标题】如果多个条件之一为真,我如何执行 JOIN?【英文标题】:How do I perform a JOIN if one of multiple conditions is true? 【发布时间】:2014-12-29 21:55:39 【问题描述】:

我的最终目标...SELECT 来自qry1 的所有字段位于家庭电话、手机或工作电话与tbl2 匹配的行上

这是我当前“失败”的 SQL 语法。失败是指它在 10-15 分钟后尚未完成执行,而个人加入每个(独立)运行一两分钟。

SELECT qry1.* FROM qry1 INNER JOIN tbl2 ON ((qry1.CellPhone = tbl2.CellPhone) OR (qry1.HomePhone = tbl2.HomePhone) OR (qry1.WorkPhone = tbl2.WorkPhone));

问题: 我的 SQL 语法有错误吗? 有没有更好的方法来完成我的任务? 对我来说,简单地独立运行 3 个(家庭、工作、单元)JOIN 查询,将它们联合起来,然后在必要时进行重复数据删除是否更有意义?

【问题讨论】:

您的 SQL 语法没有错误;如果有,查询将不会运行。桌子大概有多大?每行有多少行,以及典型的行大小。结果集中大概有多少行?这主要是出于好奇——但每个单独的查询需要 1-2 分钟,这表明表必须非常大。 在 Excel(xlsx 格式)中,馈送 qry1 的较大表格为 80 MB,tbl2 为 50 MB。 tbl2 非常宽。 【参考方案1】:

如果单个连接工作得很快,那么复合 OR 条件可能会很慢,因为它不能使用单个索引,而单个条件可以对三个连接条件使用单个索引。因为它不能使用一个索引,它很可能是在进行非索引顺序表扫描。 (您应该研究查询计划,以便了解优化器实际在做什么。)

鉴于各个查询的运行速度相当快,因此,您应该使用 UNION 获得更好的性能(除非您的 DBMS 中的优化器存在盲点):

SELECT qry1.* FROM qry1 INNER JOIN tbl2 ON qry1.CellPhone = tbl2.CellPhone
UNION
SELECT qry1.* FROM qry1 INNER JOIN tbl2 ON qry1.HomePhone = tbl2.HomePhone
UNION
SELECT qry1.* FROM qry1 INNER JOIN tbl2 ON qry1.WorkPhone = tbl2.WorkPhone

这应该与 3 个单独的查询一样快地为您提供结果。它不会那么快,因为 UNION 确实进行了重复消除(当然,单个查询不会)。您可以使用 UNION ALL,但如果两个表中有很多行,其中 2 或 3 对字段匹配,则可能会导致结果中出现大量重复。

【讨论】:

我还想补充一点,您使用UNION 进行重复消除正是我所需要的。【参考方案2】:

or 对于 SQL 优化器来说可能相当困难。我会建议 3 个索引和以下查询:

SELECT qry1.*
FROM qry1
WHERE EXISTS (SELECT 1 FROM tbl2 WHERE qry1.CellPhone = tbl2.CellPhone) OR
      EXISTS (SELECT 1 FROM tbl2 WHERE qry1.HomePhone = tbl2.HomePhone) OR
      EXISTS (SELECT 1 FROM tbl2 WHERE qry1.WorkPhone = tbl2.WorkPhone);

三个索引分别是tbl2(CellPhone)tbl2(HomePhone)tbl2(WorkPhone)

【讨论】:

我认为SELECT NULL ... 比其他虚拟SELECT-s 更轻。过去我使用SELECT * 表示EXISTS 子查询,但后来看到了一些NULL 的示例。不确定我的方式是否更好...... 这段代码很慢,因为我没有创建正确的索引。我的错。 @i486:任何半途而废的查询优化器都会忽略 EXISTS 子查询中的选择列表;不管你写的是*1NULL,还是别的什么。 @i486 。 . .为了进一步说明 Jonathan 的观点,一些 SQL 引擎甚至会接受像 1/0 这样的东西作为被选择的东西。【参考方案3】:

如果这本质上是一个自联接,其中相同的人同时存在于 tbl1 和 qry1 中,那么您可能不会对人们匹配自己的位置以及结果的重复排列感兴趣。我的意思是,自我加入通常有两条记录,其中 Bob 和 Jane 具有相同的电话号码,因此您的结果已加入记录:

Bob, Jane
Jane, Bob
Jane, Jane
Bob, Bob

在这些人中,您可能只需要Bob, Jane,因为它告诉您这两个人有一个匹配的电话号码。 如果您有一些唯一 ID,则添加 where qry1.SSN < tbl2.SNN 将消除其中许多重复项并减少您的结果集。此列最好有一个索引。

【讨论】:

在这种特殊情况下,您的回答/评论并不真正适用,但我知道我将来会遇到与此非常相似的事情。谢谢!【参考方案4】:

以下类似于https://***.com/a/27696729/4350148,但使用了不相交的联合。可能效率更高

 SELECT qry1.* FROM qry1 INNER JOIN tbl2 ON qry1.CellPhone = tbl2.CellPhone
 UNION distinct
 SELECT qry1.* FROM qry1 INNER JOIN tbl2 ON  
    qry1.CellPhone != tbl2.CellPhone 
    and qry1.HomePhone = tbl2.HomePhone
 UNION distinct
 SELECT qry1.* FROM qry1 INNER JOIN tbl2 ON 
    qry1.CellPhone != tbl2.CellPhone 
    and qry1.HomePhone != tbl2.HomePhone
    and qry1.WorkPhone = tbl2.WorkPhone

【讨论】:

以上是关于如果多个条件之一为真,我如何执行 JOIN?的主要内容,如果未能解决你的问题,请参考以下文章

如何仅在特定条件下执行 MongoDB 聚合阶段之一?

if() if() else else if() else怎么运用,请高手详细的解说一下,一定采纳!

Rails - 验证:如果一个条件为真

join 子句中的表达式之一的类型不正确。对“加入”的调用中的类型推断失败。-具有多个条件的 Linq JOIN

如果 if 语句的条件为真,可以停止执行吗?

如何选用forwhiledo while循环