Mysql 如何使用条件联接优化查询?
我需要优化一个连接依赖于条件的查询 我有两张桌子 表1有两列,我们称之为A列和B列,它们可以与表2的C列相关 如果列B为null,我必须将t1.A与t2.C匹配 如果列B不为null,则必须将t1.B与t2.C匹配 最后,我需要知道t1上的哪些条目与t2上的条目不匹配 更详细地说,t1是一个客户机表,a和B都是客户机代码。 A代码与B代码不同,在有B的情况下,B具有优先权(B是新客户机代码,但旧客户机没有。在B为空的情况下,A是要使用的代码)(所有这些都是因为B列是新的,而旧客户机的B值为空) t2是一个采购表。t2.C是客户机代码,但在本例中是一列,它为旧客户机存储a代码,为新客户机存储B代码 我只想知道哪些客户还没有购买,并尽可能高效地查询 我提出了几个问题,但它们的速度非常慢,我想这是因为条件是如何处理的: 第一次尝试:Mysql 如何使用条件联接优化查询?,mysql,sql,join,query-optimization,Mysql,Sql,Join,Query Optimization,我需要优化一个连接依赖于条件的查询 我有两张桌子 表1有两列,我们称之为A列和B列,它们可以与表2的C列相关 如果列B为null,我必须将t1.A与t2.C匹配 如果列B不为null,则必须将t1.B与t2.C匹配 最后,我需要知道t1上的哪些条目与t2上的条目不匹配 更详细地说,t1是一个客户机表,a和B都是客户机代码。 A代码与B代码不同,在有B的情况下,B具有优先权(B是新客户机代码,但旧客户机没有。在B为空的情况下,A是要使用的代码)(所有这些都是因为B列是新的,而旧客户机的B值为空)
select *
from t1
left join t2 on (t1.A = t2.C or t1.B = t2.C)
where t2.D is null;
请注意,我可以使用OR条件,因为我知道t1.A永远不会与任何t1.B相同,所以在t2.C中,它只能与A或B匹配,但不能同时与两者匹配(假设条件得到保证)。查询速度太慢,以至于在我的sql客户端中超时
第二次尝试
select *
from t1
left join t2 on (if(t1.B is null, t1.A = t2.C, t1.B = t2.C))
where t2.D is null;
在这种情况下,比较条件取决于t1.B,如果为null,则与A进行比较,如果不是,则与B进行比较。
同样,查询速度非常慢
我想我可以只使用两个连接,并对每个连接使用每个条件(A或B),但我不知道如何实现它,特别是因为我只需要得到A和B在t2上都不匹配的情况。(也就是说,t1客户没有t2购买)
对于这种情况,我可以选择什么来构建更高效的查询
多亏了唉,执行条件联接往往会导致非常差的查询性能。在本例中,您正在测试两个值,并可能希望查看两个值是否都不存在。尝试将其拆分为两个联接:
select *
from t1 left join
t2
on t1.A = t2.C left join
t2 t2a
on t1.B = t2a.C
where t2.D is null and t2a.D is null;
这将允许查询使用A、B和C上的索引。如果您在
t1.A
或t1.B
上没有索引,那么我怀疑这将是您的最佳选择:
select *
from t1
left join t2 on ifnull(t1.B, t1.A) = t2.C
where t2.D is null;
但是,如果对任一列进行了索引,我怀疑您将通过使用UNION-ALL
获得最佳性能:
select *
from t1
left join t2 on t1.A = t2.C
where t2.D is null
and t1.B is null
union all
select *
from t1
left join t2 on t1.B = t2.C
where t2.D is null
and t1.B is not null;
原因是,在编译过程中,优化程序不知道是否要对联接使用t1.A
或t1.B
,因此无法选择索引并选择表扫描,但如果将其分为两个查询,它知道要在联接上使用哪一列,并且可以使用适当的索引
谢谢!第一个版本(使用
ifnull
)运行得非常好,我看到的与我的第二次尝试非常相似,我在其中使用(如果(t1.B为null,t1.A=t2.C,t1.B=t2.C))
而不是ifnull(t1.B,t1.A)=t2.C上的。是什么让它们如此不同,以至于你的版本比我的版本更有效?我觉得这很有趣。事实上,尽管我为所有涉及的列(A、B和C)建立了索引,但您的ifnull版本的运行速度比union all快(.2秒vs.6秒)。原因是ifnull
比您的if语句运行得更好,因为其中不包含列t2.C
,它是静态的,所以MySQL知道它可以在这个列上使用索引,而在您的if语句中,它无法计算出t2.C
在true和false表达式中都使用,所以它不使用索引。哦,现在我明白了!因此,这不是因为简单地使用ifnull(x,y)
而不是if(x为null,y,x)
,而是因为比较列位于if
之外。因此使用(如果(t1.B为null,t1.A,t1.B)=t2.C)
与ifnull()完全相同。谢谢我接受了这个答案,但Gordon Linoff的答案也是正确的,事实上,如果有索引,它会运行得更快。工会的所有提示都为我敲定了它!