hivesql中使用join 关联表时where 和 on、join 的执行先后顺序 explain

https://blog.csdn.net/weixin_42903419/article/details/105845410

 

在hive sql 中,总会遇到表关联的同时还需要对左右表进行过滤数据,但是where ,on,join之间的先后顺序是怎么的呢?下面我们来一一探讨一下。

环境:hive 0.13.1版本

首先我们看一下t1表全表扫描的num rows 是多少:
select t1.cust_pty_no
,t2.amt
from a t1
left join b t2
on t1.cust_pty_no = t2.cust_pty_no 
1
2
3
4
5
执行计划如下:


如果使用在on 条件后使用where 对t1表进行过滤,如下
select t1.cust_pty_no
,t2.amt
from a t1
left join b t2
on t1.cust_pty_no = t2.cust_pty_no and  t2.busi_date='2020-04-17'
where t1.busi_date='2020-04-17'
1
2
3
4
5
6
使用expalin 查看执行计划如下:
看红色圈住部分,numrows 实际上在map阶段已经对t1表的busi_date进行过滤了
图1
对比一下这个sql

EXPLAIN
select t1.cust_pty_no,t2.amt
from a t1 where t1.busi_date=='2020-04-17'
1
2
3
看该sql执行计划:
和上面sql第一步扫描表的rows num 是一致的,这就说明了上面的sql 先对t1表where过滤了busi_date.
图2

那么问题来了,t2 表的busi_date 条件放在where 会不会也被首先过滤掉呢?
EXPLAIN
select t1.cust_pty_no
,t2.amt
from a t1
left join b t2
on t1.cust_pty_no = t2.cust_pty_no 
where t1.busi_date='2020-04-17' and t2.busi_date='2020-04-17'
1
2
3
4
5
6
7
查看执行计划如下:
图3

再看一单独执行t2表扫描rows :

EXPALIN
select * from b t2 where t2.busi_date='2020-04-17'
1
2

图4
对比图3和图4 的执行计划,对T2表扫描rows ,可以得出结论:
当两个表join 时,写在where 的过滤条件:

a. 如果是左表t1 则会先对busi_date 先进行过滤数据,然后再进行join 操作;
b. 但是对于右表 t2 表并没有对busi_date 进行过滤,即join操作后才对t2的busi_date 进行过滤,这样如果t2表数据非常大的话,效率就会很低。

那么如果要对T2表进行先过滤的话,除了写子查询的模式,如下:

EXPLAIN
select t1.cust_pty_no
,t2.amt
from a t1
left join (select * from b where busi_date='2020-04-17') t2
on t1.cust_pty_no = t2.cust_pty_no 
where t1.busi_date='2020-04-17' 
1
2
3
4
5
6
7
执行计划:
先对表T2进行了busi_date 过滤

但是这种写法不够简洁,易读性不够强,可以换一种写法

EXPLAIN
select t1.cust_pty_no
,t2.amt
from a t1
left join b t2
on t1.cust_pty_no = t2.cust_pty_no 
and b.busi_date='2020-04-17'
where t1.busi_date='2020-04-17' 
1
2
3
4
5
6
7
8
执行计划


看到没有,把t2表的过滤条件写在on 后面效果是一样的,在读表T2的时候就会过滤掉busi_date 条件的数据了,这样是不是代码要简洁许多呢

综合以上可以得出结论:
1.当过滤条件是分区字段时:
(1)


select * from a 
left join b on a.id = b.id
where a.busi_date='2020-04-17'  and b.busi_date='2020-04-17'
1
2
3
4
对于a表不会全扫描,在map 阶段先过滤busi_date=2020-04-17 的数据然后和b表(此时b表是全扫描) join 得到一个临时表,join完之后(在reduce阶段)再对b表busi_date 进行过滤busi_date=‘2020-04-17’
(2)

select * from a 
left join b  on a.id = b.id  and b.busi_date='2020-04-17'
where a.busi_date='2020-04-17'  
1
2
3
如上面的sql:

先对a 表进行busi_date 过滤数据,然后对b表进行busi_date 过滤数据,再用a,b表过滤之后的数据进行join操作
这种写法才是简洁高效的,因为在map阶段尽可能将不需要的数据过滤掉,减少后面对资源的占用。

可能有的小伙伴会问了,where过滤的字段与分区和非分区字段是否有区别呢??在这里我的busi_date 其实是表的分区字段,下面看一下是否非分区字段也是一样的:

EXPLAIN
select t1.cust_pty_no
,t2.amt
from a t1
left join b t2
on t1.cust_pty_no = t2.cust_pty_no 
where t1.cust_status_name='休眠'
1
2
3
4
5
6
7
执行计划如下:

表扫描还是会全表扫描,因为这里并没有对分区进行过滤,但在map 阶段还是会对t1表cust_status_name 条件进行过滤数据

EXPLAIN
select t1.cust_pty_no
,t2.amt
from a t1
left join b t2
on t1.cust_pty_no = t2.cust_pty_no 
where t1.busi_date='2020-04-17'
and t2.op_acct_date ='2004-04-22'
1
2
3
4
5
6
7
8
查看执行计划

从上图看,对于右表t2的过滤条件放在where 后面过滤,在map 阶段是不会过滤的。

综上所述,得到最终结论:
对于join关联表 如:

select * from a left b on a.id = b.id  where 过滤条件
1
结论:

(1) 如果是对左表(a)字段过滤数据,则可以直接写在where后面,此时执行的顺序是:先对a表的where条件过滤数据然后再join b 表

(2) 如果是对右表(b)字段过滤数据,则应该写在on 条件后面或者单独写个子查询嵌套进去,这样才能实现先过滤b表数据再进行join 操作;
如果直接把b表过滤条件放在where后面,执行顺序是:先对a表数据过滤,然后和b表全部数据关联之后,在reduce 阶段才会对b表过滤条件进行过滤数据,此时如果b表数据量很大的话,效率就会很低。因此对于应该在map 阶段尽可能对右表进行数据过滤。

(3)至于是否全表扫描取决于是否对表的分区字段过滤。这个具体从业务方面考虑是否需要对分区过滤,要想sql 高效那么尽可能在map阶段将不需要的数据过滤,减少后面资源的占用,提高效率
————————————————
版权声明:本文为CSDN博主「春风化雨~_~」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42903419/article/details/105845410

<think>好的,用户想比较SQL查询中INNER JOIN使用ONWHERE条件的性能差异。首先,我需要回忆这两种写法的区别。ON子句用于指定连接条件,而WHERE子句用于过滤结果。不过在INNER JOIN中,使用ONWHERE的组合可能会影响执行计划。 接下来,我得考虑数据库优化器如何处理这两种情况。大多数现代数据库如Oracle、MySQL、PostgreSQL的优化器会将逻辑上等价的查询转换为相同的执行计划。例如,将连接条件过滤条件都放在ON里,或者分开在ONWHERE中,优化器可能识别并生成最优的执行方式。但有候,如果写法不当,可能导致不同的执行路径,比如影响连接顺序或索引使用。 然后要分析具体例子。比如用户提到的IN-list的情况,引用[2]中提到使用IN列表可能触发索引扫描,这可能条件的放置有关。如果过滤条件放在WHERE中,优化器可能更有效地利用索引。例如,在JOINON子句中加入额外的过滤条件,可能会限制连接的行数,从而提升性能。 还要考虑历史写法的影响。旧版本的数据库可能对ONWHERE的处理不够智能,导致性能差异。例如,在SQL Server 2005之前,将过滤条件放在ONWHERE可能有不同的执行计划,但现在优化器更智能,可能统一处理。 另外,索引的使用是关键。如果连接字段过滤字段都有合适的索引,无论条件放在哪里,性能可能相近。但如果过滤条件在WHERE中,并且该字段有索引,数据库可能先过滤再连接,减少处理的数据量,提高效率。 需要给出建议,比如优先将连接条件放在ON,过滤条件放在WHERE,这样逻辑清晰且便于优化器理解。同使用EXPLAIN PLAN来验证执行计划是否一致,如引用[2]中的例子,检查是否使用了合适的索引。 最后,生成相关问题,涵盖执行计划分析、其他JOIN类型的条件放置影响、索引设计等,帮助用户深入理解。</think>在SQL查询优化中,`INNER JOIN`使用`ON`子句`WHERE`条件的性能差异主要取决于数据库优化器的处理方式索引设计。以下是关键分析: ### 1. **语法逻辑与执行顺序** - **`ON`子句**:用于指定表之间的连接条件,决定如何匹配两个表的行。在`INNER JOIN`中,`ON`子句与`WHERE`子句在功能上可能等效,但执行顺序不同。例如: ```sql SELECT * FROM table1 INNER JOIN table2 ON table1.id = table2.id AND table1.status = 'active' -- 同包含连接条件过滤条件 ``` - **`WHERE`子句**:对所有连接后的结果进行过滤。例如: ```sql SELECT * FROM table1 INNER JOIN table2 ON table1.id = table2.id WHERE table1.status = 'active' -- 后置过滤条件 ``` ### 2. **性能差异分析** - **优化器行为**:现代数据库(如Oracle、MySQL、PostgreSQL)通常会将`ON`中的过滤条件与`WHERE`条件合并优化,生成等效的执行计划[^2]。例如,索引`INLISTITERATOR`操作可能同作用于`ON`或`WHERE`中的`IN`条件。 - **索引影响**: - 如果`status`字段有索引,将过滤条件放在`WHERE`中可能更早过滤数据,减少连接操作的数据量。 - 若连接条件(如`id`)过滤条件(如`status`)均有索引,优化器可能自动选择最优路径,性能差异可忽略。 - **特殊场景**: ```sql -- 可能影响连接顺序的写法 SELECT * FROM table1 INNER JOIN table2 ON table1.id = table2.id AND table2.value > 100 -- 在ON中过滤table2 ``` 这种写法可能优先过滤`table2`,再执行连接,类似于`WHERE`条件的效果。 ### 3. **优化建议** - **统一写法**:优先将**连接条件**放在`ON`子句,**过滤条件**放在`WHERE`子句,以提高可读性并避免歧义。 - **验证执行计划**:使用`EXPLAIN PLAN`工具(如Oracle的`EXPLAIN`或MySQL的`EXPLAIN FORMAT=JSON`)检查查询是否有效利用索引。 - **索引设计**:为高频过滤字段(如`status`)连接字段(如`id`)建立复合索引,例如: ```sql CREATE INDEX idx_table1_id_status ON table1(id, status); ``` ### 4. **实验验证** 通过对比以下两种写法的执行计划: ```sql -- 写法A(过滤条件在ON) SELECT * FROM orders INNER JOIN customers ON orders.customer_id = customers.id AND customers.country = 'US'; -- 写法B(过滤条件在WHERE) SELECT * FROM orders INNER JOIN customers ON orders.customer_id = customers.id WHERE customers.country = 'US'; ``` 若`customers.country`有索引,两种写法可能生成相同的执行计划(如`NESTED LOOP`或`HASH JOIN`),但若索引未覆盖字段,`WHERE`写法可能更早过滤数据。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值