连接涉及到两个表A和B,通俗的讲嵌套循环链接相当于遍历A中的每一条记录(满足A表条件),然后再在B表中遍历记录直至找到匹配的记录,等同于两层循环。而哈希链接和排序合并,可以看作是先各自处理自身的记录(排序或hash),处理完成后再批量的进行匹配。哈希链接和排序合并链接适合处理返回大量数据的操作,大吞吐量的查询对于嵌套循环来说效率不会太高。
准备工作是建立两个表test_join_t1和test_join_t2,两个表分别有记录100条和10000条。
create table test_join_t1 as select rownum as col1, rownum + 1 as col2, rownum + 2 as col3 from dual connect by level <= 100;
create table test_join_t2 as select rownum as col1, rownum + 1 as col2, rownum + 2 as col3 from dual connect by level <= 10000;
嵌套循环连接
嵌套循环连接,在一般的应用中占有绝大多数的比例。首先执行最基本的连接查询语句
select
test_join_t1 t1, test_join_t2 t2
where
t1.col1 = t2.col1;
----------------------------------------------------------
Plan hash value: 1262213046
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 7800 | 11 (10)| 00:00:01 |
|* 1 | HASH JOIN | | 100 | 7800 | 11 (10)| 00:00:01 |
| 2 | TABLE ACCESS FULL| TEST_JOIN_T1 | 100 | 3900 | 2 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| TEST_JOIN_T2 | 10000 | 380K| 8 (0)| 00:00:01 |
-----------------------------------------------------------------------------------
从执行计划中可以看出,Oracle优化器选择了Hash Join。并没有走嵌套循环链接,这里我们不深究Oracle如何做出执行计划选择。为了验证循环嵌套链接,给SQL添加HINT /*+ leading(t1) use_nl(t2) */。HINT的含义是test_join_t1表作为启动表使用循环嵌套方式进行表连接。
select
/*+ leading(t1) use_nl(t2) */
test_join_t1 t1, test_join_t2 t2
where
t1.col1 = t2.col1;
Plan hash value: 3805247585
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 100 |00:00:00.10 | 2717 |
| 1 | NESTED LOOPS | | 1 | 100 | 100 |00:00:00.10 | 2717 |
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
| 2 | TABLE ACCESS FULL| TEST_JOIN_T1 | 1 | 100 | 100 |00:00:00.01 | 10 |
|* 3 | TABLE ACCESS FULL| TEST_JOIN_T2 | 100 | 1 | 100 |00:00:00.10 | 2707 |
---------------------------------------------------------------------------------------------
更改HINT,让test_join_t2表作为驱动表
select
/*+ leading(t2) use_nl(t1) */
test_join_t1 t1, test_join_t2 t2
where
t1.col1 = t2.col1;
Plan hash value: 2586695766
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 100 |00:00:00.18 | 30041 |
| 1 | NESTED LOOPS | | 1 | 100 | 100 |00:00:00.18 | 30041 |
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
| 2 | TABLE ACCESS FULL| TEST_JOIN_T2 | 1 | 10000 | 10000 |00:00:00.01 | 34 |
|* 3 | TABLE ACCESS FULL| TEST_JOIN_T1 | 10000 | 1 | 100 |00:00:00.17 | 30007 |
---------------------------------------------------------------------------------------------
先介绍一下执行计划中的各个字段含义:
- Starts:表访问次数
- E-Rows:Oracle预估返回条数
- A-Rows:Oracle真实返回记录条数
- A-Time:真实执行时间
通过对比以上两次执行计划可以得出一下结论:
- 驱动表只被访问一次
- 被驱动表访问的次数跟驱动表的记录数一致(应该是驱动表返回记录数一致,后面会再做测试)
接下来让t1表作为驱动表加上where限制条件col2在[10,50)范围内
select
/*+ leading(t1) use_nl(t2) */
test_join_t1 t1, test_join_t2 t2
where
t1.col1 = t2.col1 and
t1.col2 >= 10 and
t1.col2 < 50;
Plan hash value: 3805247585
---------------------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 40 |00:00:00.04 | 1089 |
| 1 | NESTED LOOPS | | 1 | 40 | 40 |00:00:00.04 | 1089 |
|* 2 | TABLE ACCESS FULL| TEST_JOIN_T1 | 1 | 40 | 40 |00:00:00.01 | 6 |
|* 3 | TABLE ACCESS FULL| TEST_JOIN_T2 | 40 | 1 | 40 |00:00:00.04 | 1083 |
---------------------------------------------------------------------------------------------
从执行计划可以看出,驱动表t1只被访问1次,被驱动表t2被访问40次,与驱动表真实返回的记录数一致。修改上面的sql,驱动表仍然为t1,对被驱动表t2添加限制条件col2的范围为[10,50)
select
/*+ leading(t1) use_nl(t2) */
test_join_t1 t1, test_join_t2 t2
where
t1.col1 = t2.col1 and
t2.col2 >= 10 and
t2.col2 < 50;
Plan hash value: 3805247585
---------------------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 40 |00:00:00.08 | 2709 |
| 1 | NESTED LOOPS | | 1 | 40 | 40 |00:00:00.08 | 2709 |
| 2 | TABLE ACCESS FULL| TEST_JOIN_T1 | 1 | 100 | 100 |00:00:00.01 | 6 |
|* 3 | TABLE ACCESS FULL| TEST_JOIN_T2 | 100 | 1 | 40 |00:00:00.08 | 2703 |
---------------------------------------------------------------------------------------------
从执行计划可以看出,驱动表t1访问次数仍然是1,被驱动表访问次数为100,而非限制条件所限定的40条。因此,可以得出嵌套循环的特点:
- 驱动表只被访问1次
- 被驱动表访问的次数为驱动表的“实际返回记录数”,被驱动表添加查询条件是不会影响被访问次数的。
Plan hash value: 3428653669
-----------------------------------------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows| A-Time | Buffers | Reads |
-----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 40|00:00:00.01 | 24 | 2|
| 1 | NESTED LOOPS | | 1 | | 40|00:00:00.01 | 24 | 2|
| 2 | NESTED LOOPS | | 1 | 40 | 4000|00:00:00.01 | 20 | 2|
| 3 | TABLE ACCESS FULL | TEST_JOIN_T1 | 1 | 100 | 100|00:00:00.01 | 6 | 0|
|* 4 | INDEX RANGE SCAN | INDEX_T2_COL2 | 100 | 45 | 4000|00:00:00.01 | 14 | 2|
|* 5 | TABLE ACCESS BY INDEX ROWID| TEST_JOIN_T2 | 4000 | 1 | 40|00:00:00.01 | 4 | 0|
----------------------------------------------------------------------------------------------------------------
可以看出,t2表确实走了索引采用INDEX RANGE SCAN进行范围扫描。驱动表t1仍然只被访问1次,被驱动表范围扫描执行了100次,从执行计划可以看出索引被访问了100次,每次扫描完索引后又根据rowid回表读数,这样t2表就被访问了4000次。大大多于了未建索引时的100次。当然这里是因为我们使用了HINT,要求Oracle将t1作为驱动表。接下来将t2表作为驱动表看执行计划如何
select
/*+ leading(t2) use_nl(t1) */
test_join_t1 t1, test_join_t2 t2
where
t1.col1 = t2.col1 and
t2.col2 >= 10 and
t2.col2 < 50;
Plan hash value: 2941912635
--------------------------------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows| A-Time | Buffers |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 40|00:00:00.01 | 132 |
| 1 | NESTED LOOPS | | 1 | 40 | 40|00:00:00.01 | 132 |
| 2 | TABLE ACCESS BY INDEX ROWID| TEST_JOIN_T2 | 1 | 40 | 40|00:00:00.01 | 9 |
|* 3 | INDEX RANGE SCAN | INDEX_T2_COL2 | 1 | 45 | 40|00:00:00.01 | 5 |
|* 4 | TABLE ACCESS FULL | TEST_JOIN_T1 | 40 | 1 | 40|00:00:00.01 | 123 |
--------------------------------------------------------------------------------------------------------
对于驱动表t2先走利用了索引获取到最终的记录,然后再进入循环访问被驱动表t1。经过上面的测试,可以看出嵌套循环就是由两层for循环操作构成:
- 查询驱动表记录获取满足查询条件的所有记录
- 遍历第一步得到的驱动表记录,带入链接条件值获取满足链接条件的所有被驱动表记录
- 遍历第二步中得到的被驱动表记录,判断是否满足查询条件,如果是则返回结果,如果不是则跳过记录
到此可以总结一下嵌套循环的主要特点以及适用场景:
- 嵌套循环有驱动表和被驱动表的概念,驱动顺序不同执行计划差异非常大
- 驱动表只被访问一次,被驱动表被访问多次。嵌套循环访问表的次数直接受驱动表的返回记录数的影响。因此应当让实际返回记录数(A-Rows)小的表作为驱动表,返回记录数大的表作为被驱动表。
- 在驱动表的查询条件上建立索引可以改善查询效率
- 在连接条件上建立索引也可以改善查询效率
- 在被驱动表查询条件建立索引对查询效率的影响要视情况而定,不一定带来好处
连接方式Oracle的表连接方式有以下三种:嵌套循环链接(Nested Loops Join)哈希链接(Hash Join)排序合并(Merge Sort Join)连接涉及到两个表A和B,通俗的讲嵌套循环链接相当于遍历A中的每一条记录(满足A表条件),然后再在B表中遍历记录直至找到匹配的记录,等同于两层循环。而哈希链接和排序合并,可以看作是先各自处理自身的记录(排序或ha
将两个表排序,然后再进行join
①首先生产drving table【驱动表】需要的数据,然后对这些数据按照连接操作关联列进行排序;
②然后生产probed table【被探查表】需要的数据,然后对这些数据按照与driving table对应的连接操作列进行排序;
③最后两边已经排序的行被放在一起执行合并操作。
应用场景:
①排序是一个费时、费资源的操作,特别对于大表。所以SMJ通常不是一个特别有效的连接方法,但是如果driving
Mysql算法内部算法 -
嵌套循环连接算法1.循环
连接算法// 循环
连接算法分为两种
1.
嵌套循环连接算法
2.块
嵌套循环连接算法2.
嵌套循环连接算法一个简单的
嵌套循环连接(NLJ)算法从一个循环中的第一个表中读取一行中的行,将每行传递给
嵌套循环,以处理
连接中的下一个表。该过程重复多次,因为还有待
连接的表。
假设三个表之间的
连接 t1,t2以及 t3,那么NLJ算法会这么来执行:// 规则
今天写了一个SQL,表数据太大,表索引也加了,可和另一张表关联一起查一个月的数据总是很慢,大约要60多秒。优化后只用了0.07秒,记录一下防止以后忘记了。
select /*+leading(t) use_nl(t, d)*/t.a,t.b,t.c, d.a,d.b,d.c
from tablea t, tableb d
where t.id = d.id
and t.a is not null
and t.b is not n...
要连接Oracle数据库,需要使用Oracle客户端软件和相应的驱动程序。在Linux或Unix系统中,可以使用Oracle Instant Client或Oracle客户端软件来连接Oracle数据库。在Windows系统中,可以使用Oracle客户端软件来连接Oracle数据库。
连接Oracle数据库的步骤如下:
1. 安装Oracle客户端软件或Oracle Instant Client。
2. 配置环境变量,将Oracle客户端软件或Oracle Instant Client的路径添加到PATH环境变量中。
3. 在shell中使用sqlplus命令连接Oracle数据库。例如:
sqlplus username/password@database
其中,username是Oracle数据库的用户名,password是密码,database是Oracle数据库的服务名或SID。
4. 如果连接成功,将会显示SQL>提示符。可以使用SQL语句来查询和操作Oracle数据库。例如:
SELECT * FROM table_name;
5. 使用exit命令退出sqlplus。
以上就是连接Oracle数据库的基本步骤。需要注意的是,Oracle客户端软件和驱动程序的版本需要与Oracle数据库的版本匹配。
沐小枫丶: