NULL基础知识
IS NULL或
IS NOT NULL判断,可以用函数转换为普通值。如 NVL 、NVL2 函数。
NULL 值对结果的影响跟 OB 没有关系,不在本文探讨范围内。
构造测试数据(MYSQL)
date && ./sysbench ./oltp_read_only.lua --mysql-db=sysbenchdb --mysql-host=172.23.152.229 --mysql-port=2883 --mysql-user=user01@obmysql01#obdemo --mysql-password=123456 --tables=1 --table_size=10000000 --report-interval=10 --threads=100 --db-driver=mysql --time=3600 --skip_trx=on --db-ps-mode=disable --create-secondary=off --mysql-ignore-errors=6002,6004,4012,2013,4016,1062 prepare && date
obclient -h127.1 -uuser01@obmysql01#obdemo -P2883 -p123456 -c -A sysbenchdb
show create table sbtest1;
show indexes from sbtest1;

obclient -h127.1 -uuser01@obmysql01#obdemo -P2883 -p123456 -c -A sysbenchdb
alter table sbtest2 modify column c char(120) null;
update /*+ query_timeout(100000000) trx_idle_timeout(1000000000) trx_idle_timeout(1200000000) */ sbtest2 set c = null where mod(id, 10000) = 0; commit;
show create table sbtest2\G
select count(*) from sbtest2 where c is null;

// 发起合并
alter system major freeze;
// 查看合并进度
SELECT ZONE,svr_ip,major_version,ss_store_count,merged_ss_store_count,modified_ss_store_count,merge_start_time,merge_finish_time,merge_process
FROM __all_virtual_partition_sstable_image_info;
// 查看合并事件
SELECT DATE_FORMAT(gmt_create, '%b%d %H:%i:%s') gmt_create_ , module, event, name1, value1, name2, value2, rs_svr_ip
FROM __all_rootservice_event_history
WHERE 1 = 1
-- AND module IN ('server')
-- AND module NOT IN ('leader_coordinator')
ORDER BY gmt_create DESC
LIMIT 10;


查看表的统计信息
SELECT t.tenant_id, a.tenant_name,d.database_name, t.table_name, tg.tablegroup_name , t.part_num , t2.partition_id, t2.ZONE, t2.svr_ip, t2.role
-- , a.primary_zone , IF(t.locality = '' OR t.locality IS NULL, a.locality, t.locality) AS locality
, t2.row_count, round(data_size/1024/1024,0) data_size_MB
FROM oceanbase.__all_tenant AS a
JOIN oceanbase.__all_virtual_database AS d ON ( a.tenant_id = d.tenant_id )
JOIN oceanbase.__all_virtual_table AS t ON (t.tenant_id = d.tenant_id AND t.database_id = d.database_id)
JOIN oceanbase.__all_virtual_meta_table t2 ON (t.tenant_id = t2.tenant_id AND (t.table_id=t2.table_id OR t.tablegroup_id=t2.table_id) AND t2.ROLE IN (1,2) )
LEFT JOIN oceanbase.__all_virtual_tablegroup AS tg ON (t.tenant_id = tg.tenant_id and t.tablegroup_id = tg.tablegroup_id)
WHERE a.tenant_id IN (1002 ) AND t.table_type IN (3)
AND d.database_name = 'sysbenchdb'
ORDER BY t.tenant_id, tg.tablegroup_name, d.database_name, t.table_name, t2.partition_id
;

测试单列索引跟NULL关系

idx_sbtest_c(c)create index idx_sbtest_c on sbtest1(c);
create index idx_sbtest_c on sbtest2(c);


备注:
执行计划查看
gv$plan_cache_plan_stat和
gv$plan_cache_plan_explain。以前文章也介绍过。后面这个视图比较特殊,是一个类似KV的结构,必须提供完整4个等值条件才会返回结果。
// 查看缓存的SQL执行统计信息
SELECT s.tenant_id, svr_ip, svr_port , plan_Id,sql_id,TYPE, d.database_name , query_sql, first_load_time, avg_exe_usec, slow_count,executions, slowest_exe_usec
FROM `gv$plan_cache_plan_stat` s LEFT JOIN `gv$database` d ON (s.tenant_id =d.tenant_id AND s.db_id =d.database_id )
WHERE s.tenant_id=1002 -- 改成具体的 tenant_id
AND d.database_name in ('sysbenchdb')
and query_sql like '%test20210305%'
ORDER BY avg_exe_usec desc;
// 查看具体SQL的执行计划缓存
SELECT PLAN_DEPTH ,OPERATOR ,NAME ,`ROWS` ,cost,PROPERTY
FROM `gv$plan_cache_plan_explain`
WHERE tenant_id=1002 AND ip = '172.30.118.70' AND port=2882 AND plan_id=231 ;
IS NULL 测试
test202103-05,主要是方便从SQL缓存里定位到这条SQL, 不影响实际执行计划和结果正确性。
select /*+ test20210305-01 */ count(*) from sbtest1 where c is null ;
select /*+ test20210305-01 */ count(*) from sbtest2 where c is null ;




startup_filter([0])信息)。
索引列等值查询
select /*+ test20210305-02 */ * from sbtest1
where c ='10676430684-17587037436-43492067438-02978392453-17382292196-18617843758-31971657195-07771170431-78457836714-38765745127';
select /*+ test20210305-02 */ * from sbtest2
where c = '10676430684-17587037436-43492067438-02978392453-17382292196-18617843758-31971657195-07771170431-78457836714-38765745127';




索引列模糊查询
select /*+ test20210305-03 */ * from sbtest1 where c like '10676430684-%';
select /*+ test20210305-03 */ * from sbtest2 where c like '10676430684-%';
测试多列索引跟NULL关系
前导列等值查询
alter table sbtest2 modify k int null;
update /*+ query_timeout(100000000) trx_idle_timeout(1000000000) trx_idle_timeout(1200000000) */ sbtest2 set k = null where mod(id, 10500) = 0; commit;
show create table sbtest2\G
select count(*) from sbtest2 where c is null;
create index idx_sbtest_k_c on sbtest1(k, c);
create index idx_sbtest_k_c on sbtest2(k, c);


select /*+ test20210305-04 */ * from sbtest1 where k=5028116 ;
select /*+ test20210305-04 */ * from sbtest2 where k=5028116 ;




所有列等值查询
select /*+ test20210305-04 */ * from sbtest1 where k=5028116 and c = '95853251151-92786089934-87253544789-58438024614-82823928273-73185621814-81143279998-07697837769-56511602204-24615932635' ;
select /*+ test20210305-04 */ * from sbtest2 where k=5028116 and c = '95853251151-92786089934-87253544789-58438024614-82823928273-73185621814-81143279998-07697837769-56511602204-24615932635' ;




前导列 IS NULL 查询
select /*+ test20210305-05 */ * from sbtest1 where k is null and c = '95853251151-92786089934-87253544789-58438024614-82823928273-73185621814-81143279998-07697837769-56511602204-24615932635' ;
select /*+ test20210305-05 */ * from sbtest2 where k is null and c = '95853251151-92786089934-87253544789-58438024614-82823928273-73185621814-81143279998-07697837769-56511602204-24615932635' ;

没有前导列条件的查询
alter table sbtest1 drop index idx_sbtest_c ;
alter table sbtest2 drop index idx_sbtest_c ;
select /*+ test20210305-06 */ * from sbtest1 where c = '95853251151-92786089934-87253544789-58438024614-82823928273-73185621814-81143279998-07697837769-56511602204-24615932635' ;
select /*+ test20210305-06 */ * from sbtest2 where c = '95853251151-92786089934-87253544789-58438024614-82823928273-73185621814-81143279998-07697837769-56511602204-24615932635' ;




INDEX SKIP SCAN。尽管比
INDEX RANGE SCAN要慢一些,相比去扫描全表,那依然是个不错的选择。MySQL也只是在8.0后才支持
INDEX SKIP SCAN。
INDEX SKIP SCAN,不过OB的CBO也会考虑使用这个索引。扫描这个索引做过滤(
filter)操作,可能会比去扫描全表更快。当然这取决于这个SQL返回的记录数。如果记录数很大的话,内部回表还是有一定成本,CBO可能也会最终选择扫描全表。
INDEX SKIP SCAN也是同理。
ORACLE租户跟MySQL租户有区别吗
构造测试数据
从MySQL租户导出表数据
ob-dumper-loader导出 MySQL租户的两个表。
./obdumper --csv -h 127.1 -P 2883 -t obmysql01 -c obdemo -u user01 -p 123456 -D sysbenchdb --table sbtest1,sbtest2 -f /data/1/obdumper --sys-password `cat ~/.syspassword`

/data/1/obdumper/data/sysbenchdb/TABLE。
准备oracle租户环境
CREATE USER user01 identified BY 123456;
GRANT CONNECT,resource TO user01;
obclient -hobserver00 -P2883 -uuser01@oboracle01#obdemo -p123456 -c -A user01
CREATE TABLE sbtest1 (
id number NOT NULL ,
k number NOT NULL DEFAULT '0',
c char(120) NOT NULL DEFAULT '',
pad char(60) NOT NULL DEFAULT '',
PRIMARY KEY (id)
);
CREATE TABLE sbtest2 (
id number NOT NULL ,
k number NULL ,
c char(120) NULL ,
pad char(60) NOT NULL DEFAULT '',
PRIMARY KEY (id)
);
到ORACLE租户导入表数据

cd /data/1/obdumper/data/sysbenchdb/TABLE
ls *.csv |awk -F'.csv' '{print "mv "$1".csv "toupper($1)".csv"}' |bash
./obloader --csv -h 127.1 -P 2883 -t oboracle01 -c obdemo -u user01 -p 123456 -D user01 --table SBTEST1,SBTEST2 -f /data/1/obdumper --sys-password `cat ~/.syspassword`


单表查询测试
多表连接测试

SELECT table_owner, table_Name, index_name, listagg( column_name,',') WITHIN GROUP (ORDER BY column_position) indexed_columns
FROM all_ind_columns
WHERE table_owner = 'USER01'
GROUP BY table_owner, table_name, index_name
ORDER BY table_owner, table_name, index_name
;
+-------------+------------+-------------------------------+-----------------+
| TABLE_OWNER | TABLE_NAME | INDEX_NAME | INDEXED_COLUMNS |
+-------------+------------+-------------------------------+-----------------+
| USER01 | SBTEST1 | IDX_SBTEST1_C_2 | C |
| USER01 | SBTEST1 | IDX_SBTEST1_K_C_2 | K,C |
| USER01 | SBTEST1 | SBTEST1_OBPK_1614952866197016 | ID |
| USER01 | SBTEST11 | IDX_SBTEST1_C | C |
| USER01 | SBTEST11 | IDX_SBTEST1_K_C | K,C |
| USER01 | SBTEST11 | SBTEST1_OBPK_1614932239752654 | ID |
| USER01 | SBTEST12 | IDX_SBTEST2_C | C |
| USER01 | SBTEST12 | IDX_SBTEST2_K_C | K,C |
| USER01 | SBTEST12 | SBTEST2_OBPK_1614932244447062 | ID |
| USER01 | SBTEST2 | IDX_SBTEST2_C_2 | C |
| USER01 | SBTEST2 | IDX_SBTEST2_K_C_2 | K,C |
| USER01 | SBTEST2 | SBTEST2_OBPK_1614952868052137 | ID |
+-------------+------------+-------------------------------+-----------------+
12 rows in set (0.01 sec)
INNER JOIN 查询
EXPLAIN
SELECT t1.*, t2.*
FROM SBTEST1 t1 JOIN SBTEST11 t2 ON (t1.k=t2.K)
WHERE t2.C LIKE '04420889655%'
;
EXPLAIN
SELECT t1.*, t2.*
FROM SBTEST2 t1 JOIN SBTEST12 t2 ON (t1.k=t2.K)
WHERE t2.C LIKE '04420889655%'
;
obclient> EXPLAIN
-> SELECT t1.*, t2.*
-> FROM SBTEST1 t1 JOIN SBTEST11 t2 ON (t1.k=t2.K)
-> WHERE t2.C LIKE '04420889655%'
-> \G
*************************** 1. row ***************************
Query Plan: ==========================================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
----------------------------------------------------------
|0 |NESTED-LOOP JOIN| |1 |84 |
|1 | TABLE SCAN |T2(IDX_SBTEST1_C) |1 |84 |
|2 | TABLE SCAN |T1(IDX_SBTEST1_K_C_2)|6 |115 |
==========================================================
Outputs & filters:
-------------------------------------
0 - output([T1.ID], [T1.K], [T1.C], [T1.PAD], [T2.ID], [T2.K], [T2.C], [T2.PAD]), filter(nil),
conds(nil), nl_params_([T2.K])
1 - output([T2.K], [T2.C], [T2.ID], [T2.PAD]), filter([(T_OP_LIKE, T2.C, ?, '\')]),
access([T2.K], [T2.C], [T2.ID], [T2.PAD]), partitions(p0)
2 - output([T1.K], [T1.ID], [T1.C], [T1.PAD]), filter(nil),
access([T1.K], [T1.ID], [T1.C], [T1.PAD]), partitions(p0)
1 row in set (0.00 sec)
obclient> EXPLAIN
-> SELECT t1.*, t2.*
-> FROM SBTEST2 t1 JOIN SBTEST12 t2 ON (t1.k=t2.K)
-> WHERE t2.C LIKE '04420889655%'
-> \G
*************************** 1. row ***************************
Query Plan: ==========================================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
----------------------------------------------------------
|0 |NESTED-LOOP JOIN| |1 |84 |
|1 | TABLE SCAN |T2(IDX_SBTEST2_C) |1 |84 |
|2 | TABLE SCAN |T1(IDX_SBTEST2_K_C_2)|6 |115 |
==========================================================
Outputs & filters:
-------------------------------------
0 - output([T1.ID], [T1.K], [T1.C], [T1.PAD], [T2.ID], [T2.K], [T2.C], [T2.PAD]), filter(nil),
conds(nil), nl_params_([T2.K])
1 - output([T2.K], [T2.C], [T2.ID], [T2.PAD]), filter([(T_OP_LIKE, T2.C, ?, '\')]),
access([T2.K], [T2.C], [T2.ID], [T2.PAD]), partitions(p0)
2 - output([T1.K], [T1.ID], [T1.C], [T1.PAD]), filter(nil),
access([T1.K], [T1.ID], [T1.C], [T1.PAD]), partitions(p0)
1 row in set (0.01 sec)
LEFT JOIN 查询
EXPLAIN
SELECT t1.*, t2.*
FROM SBTEST1 t1 LEFT JOIN SBTEST11 t2 ON (t1.k=t2.K AND t2.C LIKE '04420889655%')
;
EXPLAIN
SELECT t1.*, t2.*
FROM SBTEST2 t1 LEFT JOIN SBTEST12 t2 ON (t1.k=t2.K AND t2.C LIKE '04420889655%')
;
obclient> EXPLAIN
-> SELECT t1.*, t2.*
-> FROM SBTEST1 t1 LEFT JOIN SBTEST11 t2 ON (t1.k=t2.K AND t2.C LIKE '04420889655%')
-> \G
*************************** 1. row ***************************
Query Plan: ==============================================================
|ID|OPERATOR |NAME |EST. ROWS|COST |
--------------------------------------------------------------
|0 |HASH RIGHT OUTER JOIN| |10000000 |7488265|
|1 | TABLE SCAN |T2(IDX_SBTEST1_C)|1 |84 |
|2 | TABLE SCAN |T1 |10000000 |3945573|
==============================================================
Outputs & filters:
-------------------------------------
0 - output([T1.ID], [T1.K], [T1.C], [T1.PAD], [T2.ID], [T2.K], [T2.C], [T2.PAD]), filter(nil),
equal_conds([T1.K = T2.K]), other_conds(nil)
1 - output([T2.K], [T2.C], [T2.ID], [T2.PAD]), filter([(T_OP_LIKE, T2.C, ?, '\')]),
access([T2.K], [T2.C], [T2.ID], [T2.PAD]), partitions(p0)
2 - output([T1.K], [T1.ID], [T1.C], [T1.PAD]), filter(nil),
access([T1.K], [T1.ID], [T1.C], [T1.PAD]), partitions(p0)
1 row in set (0.00 sec)
obclient> EXPLAIN
-> SELECT t1.*, t2.*
-> FROM SBTEST2 t1 LEFT JOIN SBTEST12 t2 ON (t1.k=t2.K AND t2.C LIKE '04420889655%')
-> \G
*************************** 1. row ***************************
Query Plan: ==============================================================
|ID|OPERATOR |NAME |EST. ROWS|COST |
--------------------------------------------------------------
|0 |HASH RIGHT OUTER JOIN| |10000000 |7488266|
|1 | TABLE SCAN |T2(IDX_SBTEST2_C)|1 |84 |
|2 | TABLE SCAN |T1 |10000000 |3945573|
==============================================================
Outputs & filters:
-------------------------------------
0 - output([T1.ID], [T1.K], [T1.C], [T1.PAD], [T2.ID], [T2.K], [T2.C], [T2.PAD]), filter(nil),
equal_conds([T1.K = T2.K]), other_conds(nil)
1 - output([T2.K], [T2.C], [T2.ID], [T2.PAD]), filter([(T_OP_LIKE, T2.C, ?, '\')]),
access([T2.K], [T2.C], [T2.ID], [T2.PAD]), partitions(p0)
2 - output([T1.K], [T1.ID], [T1.C], [T1.PAD]), filter(nil),
access([T1.K], [T1.ID], [T1.C], [T1.PAD]), partitions(p0)
1 row in set (0.00 sec)
EXISTS子查询
EXPLAIN
SELECT * FROM SBTEST1 t1
WHERE exists (
SELECT k FROM sbtest11 t2 WHERE t2.k = t1.k AND t2.C LIKE '04420889655%'
);
EXPLAIN
SELECT * FROM SBTEST2 t1
WHERE exists (
SELECT k FROM sbtest12 t2 WHERE t2.k = t1.k AND t2.C LIKE '04420889655%'
);
obclient> EXPLAIN
-> SELECT * FROM SBTEST1 t1
-> WHERE exists (
-> SELECT k FROM sbtest11 t2 WHERE t2.k = t1.k AND t2.C LIKE '04420889655%'
-> )\G
*************************** 1. row ***************************
Query Plan: ==========================================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
----------------------------------------------------------
|0 |NESTED-LOOP JOIN| |1 |84 |
|1 | SUBPLAN SCAN |VIEW1 |1 |84 |
|2 | HASH DISTINCT | |1 |84 |
|3 | TABLE SCAN |T2(IDX_SBTEST1_C) |1 |84 |
|4 | TABLE SCAN |T1(IDX_SBTEST1_K_C_2)|6 |115 |
==========================================================
Outputs & filters:
-------------------------------------
0 - output([T1.ID], [T1.K], [T1.C], [T1.PAD]), filter(nil),
conds(nil), nl_params_([VIEW1.T2.K])
1 - output([VIEW1.T2.K]), filter(nil),
access([VIEW1.T2.K])
2 - output([T2.K]), filter(nil),
distinct([T2.K])
3 - output([T2.K]), filter([(T_OP_LIKE, T2.C, ?, '\')]),
access([T2.K], [T2.C]), partitions(p0)
4 - output([T1.K], [T1.ID], [T1.C], [T1.PAD]), filter(nil),
access([T1.K], [T1.ID], [T1.C], [T1.PAD]), partitions(p0)
1 row in set (0.01 sec)
obclient> EXPLAIN
-> SELECT * FROM SBTEST2 t1
-> WHERE exists (
-> SELECT k FROM sbtest12 t2 WHERE t2.k = t1.k AND t2.C LIKE '04420889655%'
-> )\G
*************************** 1. row ***************************
Query Plan: ==========================================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
----------------------------------------------------------
|0 |NESTED-LOOP JOIN| |1 |84 |
|1 | SUBPLAN SCAN |VIEW1 |1 |84 |
|2 | HASH DISTINCT | |1 |84 |
|3 | TABLE SCAN |T2(IDX_SBTEST2_C) |1 |84 |
|4 | TABLE SCAN |T1(IDX_SBTEST2_K_C_2)|6 |115 |
==========================================================
Outputs & filters:
-------------------------------------
0 - output([T1.ID], [T1.K], [T1.C], [T1.PAD]), filter(nil),
conds(nil), nl_params_([VIEW1.T2.K])
1 - output([VIEW1.T2.K]), filter(nil),
access([VIEW1.T2.K])
2 - output([T2.K]), filter(nil),
distinct([T2.K])
3 - output([T2.K]), filter([(T_OP_LIKE, T2.C, ?, '\')]),
access([T2.K], [T2.C]), partitions(p0)
4 - output([T1.K], [T1.ID], [T1.C], [T1.PAD]), filter(nil),
access([T1.K], [T1.ID], [T1.C], [T1.PAD]), partitions(p0)
1 row in set (0.00 sec)
结论:不管列是否包含 NULL ,EXISTS 的执行计划是一样的。
NOT EXISTS 子查询
EXPLAIN
SELECT * FROM SBTEST1 t1
WHERE NOT exists (
SELECT k FROM sbtest11 t2 WHERE t2.k = t1.k AND t2.C LIKE '044208%'
);
EXPLAIN
SELECT * FROM SBTEST2 t1
WHERE NOT exists (
SELECT k FROM sbtest12 t2 WHERE t2.k = t1.k AND t2.C LIKE '044208%'
);
obclient> EXPLAIN
-> SELECT * FROM SBTEST1 t1
-> WHERE NOT exists (
-> SELECT k FROM sbtest11 t2 WHERE t2.k = t1.k AND t2.C LIKE '044208%'
-> )\G
*************************** 1. row ***************************
Query Plan: =============================================================
|ID|OPERATOR |NAME |EST. ROWS|COST |
-------------------------------------------------------------
|0 |HASH RIGHT ANTI JOIN| |9999995 |7488278|
|1 | TABLE SCAN |T2(IDX_SBTEST1_C)|1 |93 |
|2 | TABLE SCAN |T1 |10000000 |3945573|
=============================================================
Outputs & filters:
-------------------------------------
0 - output([T1.ID], [T1.K], [T1.C], [T1.PAD]), filter(nil),
equal_conds([T2.K = T1.K]), other_conds(nil)
1 - output([T2.K]), filter([(T_OP_LIKE, T2.C, ?, '\')]),
access([T2.K], [T2.C]), partitions(p0)
2 - output([T1.K], [T1.ID], [T1.C], [T1.PAD]), filter(nil),
access([T1.K], [T1.ID], [T1.C], [T1.PAD]), partitions(p0)
1 row in set (0.01 sec)
obclient> EXPLAIN
-> SELECT * FROM SBTEST2 t1
-> WHERE NOT exists (
-> SELECT k FROM sbtest12 t2 WHERE t2.k = t1.k AND t2.C LIKE '044208%'
-> )\G
*************************** 1. row ***************************
Query Plan: =============================================================
|ID|OPERATOR |NAME |EST. ROWS|COST |
-------------------------------------------------------------
|0 |HASH RIGHT ANTI JOIN| |9999995 |7488278|
|1 | TABLE SCAN |T2(IDX_SBTEST2_C)|1 |93 |
|2 | TABLE SCAN |T1 |10000000 |3945573|
=============================================================
Outputs & filters:
-------------------------------------
0 - output([T1.ID], [T1.K], [T1.C], [T1.PAD]), filter(nil),
equal_conds([T2.K = T1.K]), other_conds(nil)
1 - output([T2.K]), filter([(T_OP_LIKE, T2.C, ?, '\')]),
access([T2.K], [T2.C]), partitions(p0)
2 - output([T1.K], [T1.ID], [T1.C], [T1.PAD]), filter(nil),
access([T1.K], [T1.ID], [T1.C], [T1.PAD]), partitions(p0)
1 row in set (0.00 sec)
结论:不管列是否包含 NULL ,NOT EXISTS 的执行计划是一样的。
IN 子查询
EXPLAIN
SELECT * FROM SBTEST1 t1
WHERE t1.k IN (
SELECT k FROM sbtest11 t2 WHERE t2.C LIKE '04420889655%'
);
EXPLAIN
SELECT * FROM SBTEST2 t1
WHERE t1.k IN (
SELECT k FROM sbtest12 t2 WHERE t2.C LIKE '04420889655%'
);
obclient> EXPLAIN
-> SELECT * FROM SBTEST1 t1
-> WHERE t1.k IN (
-> SELECT k FROM sbtest11 t2 WHERE t2.C LIKE '04420889655%'
-> )
-> \G
*************************** 1. row ***************************
Query Plan: ==========================================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
----------------------------------------------------------
|0 |NESTED-LOOP JOIN| |1 |84 |
|1 | SUBPLAN SCAN |VIEW1 |1 |84 |
|2 | HASH DISTINCT | |1 |84 |
|3 | TABLE SCAN |T2(IDX_SBTEST1_C) |1 |84 |
|4 | TABLE SCAN |T1(IDX_SBTEST1_K_C_2)|6 |115 |
==========================================================
Outputs & filters:
-------------------------------------
0 - output([T1.ID], [T1.K], [T1.C], [T1.PAD]), filter(nil),
conds(nil), nl_params_([VIEW1.T2.K])
1 - output([VIEW1.T2.K]), filter(nil),
access([VIEW1.T2.K])
2 - output([T2.K]), filter(nil),
distinct([T2.K])
3 - output([T2.K]), filter([(T_OP_LIKE, T2.C, ?, '\')]),
access([T2.C], [T2.K]), partitions(p0)
4 - output([T1.K], [T1.ID], [T1.C], [T1.PAD]), filter(nil),
access([T1.K], [T1.ID], [T1.C], [T1.PAD]), partitions(p0)
1 row in set (0.01 sec)
obclient> EXPLAIN
-> SELECT * FROM SBTEST2 t1
-> WHERE t1.k IN (
-> SELECT k FROM sbtest12 t2 WHERE t2.C LIKE '04420889655%'
-> )\G
*************************** 1. row ***************************
Query Plan: ==========================================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
----------------------------------------------------------
|0 |NESTED-LOOP JOIN| |1 |84 |
|1 | SUBPLAN SCAN |VIEW1 |1 |84 |
|2 | HASH DISTINCT | |1 |84 |
|3 | TABLE SCAN |T2(IDX_SBTEST2_C) |1 |84 |
|4 | TABLE SCAN |T1(IDX_SBTEST2_K_C_2)|6 |115 |
==========================================================
Outputs & filters:
-------------------------------------
0 - output([T1.ID], [T1.K], [T1.C], [T1.PAD]), filter(nil),
conds(nil), nl_params_([VIEW1.T2.K])
1 - output([VIEW1.T2.K]), filter(nil),
access([VIEW1.T2.K])
2 - output([T2.K]), filter(nil),
distinct([T2.K])
3 - output([T2.K]), filter([(T_OP_LIKE, T2.C, ?, '\')]),
access([T2.C], [T2.K]), partitions(p0)
4 - output([T1.K], [T1.ID], [T1.C], [T1.PAD]), filter(nil),
access([T1.K], [T1.ID], [T1.C], [T1.PAD]), partitions(p0)
1 row in set (0.00 sec)
NOT IN 子查询
EXPLAIN
SELECT * FROM SBTEST1 t1
WHERE t1.k NOT IN (
SELECT k FROM sbtest11 t2 WHERE t2.C LIKE '044208%'
) AND t1.c LIKE '044208%'
;
EXPLAIN
SELECT * FROM SBTEST2 t1
WHERE t1.k NOT IN (
SELECT k FROM sbtest12 t2 WHERE t2.C LIKE '044208%'
) AND t1.c LIKE '044208%'
;
obclient> EXPLAIN
-> SELECT * FROM SBTEST1 t1
-> WHERE t1.k NOT IN (
-> SELECT k FROM sbtest11 t2 WHERE t2.C LIKE '044208%'
-> ) AND t1.c LIKE '044208%'
-> \G
*************************** 1. row ***************************
Query Plan: =============================================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
-------------------------------------------------------------
|0 |NESTED-LOOP ANTI JOIN| |0 |93 |
|1 | TABLE SCAN |T1(IDX_SBTEST1_C_2)|1 |93 |
|2 | TABLE SCAN |T2(IDX_SBTEST1_K_C)|1 |40 |
=============================================================
Outputs & filters:
-------------------------------------
0 - output([T1.ID], [T1.K], [T1.C], [T1.PAD]), filter(nil),
conds(nil), nl_params_([T1.K])
1 - output([T1.K], [T1.C], [T1.ID], [T1.PAD]), filter([(T_OP_LIKE, T1.C, ?, '\')]),
access([T1.K], [T1.C], [T1.ID], [T1.PAD]), partitions(p0)
2 - output([1]), filter([(T_OP_LIKE, T2.C, ?, '\')]),
access([T2.C]), partitions(p0)
1 row in set (0.00 sec)
obclient> EXPLAIN
-> SELECT * FROM SBTEST2 t1
-> WHERE t1.k NOT IN (
-> SELECT k FROM sbtest12 t2 WHERE t2.C LIKE '044208%'
-> ) AND t1.c LIKE '044208%'
-> \G
*************************** 1. row ***************************
Query Plan: =============================================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
-------------------------------------------------------------
|0 |NESTED-LOOP ANTI JOIN| |0 |185 |
|1 | TABLE SCAN |T1(IDX_SBTEST2_C_2)|1 |93 |
|2 | MATERIAL | |1 |93 |
|3 | TABLE SCAN |T2(IDX_SBTEST2_C) |1 |93 |
=============================================================
Outputs & filters:
-------------------------------------
0 - output([T1.ID], [T1.K], [T1.C], [T1.PAD]), filter(nil),
conds([(T_OP_OR, T1.K = T2.K, (T_OP_IS, T1.K, NULL, 0), (T_OP_IS, T2.K, NULL, 0))]), nl_params_(nil)
1 - output([T1.K], [T1.C], [T1.ID], [T1.PAD]), filter([(T_OP_LIKE, T1.C, ?, '\')]),
access([T1.K], [T1.C], [T1.ID], [T1.PAD]), partitions(p0)
2 - output([T2.K]), filter(nil)
3 - output([T2.K]), filter([(T_OP_LIKE, T2.C, ?, '\')]),
access([T2.C], [T2.K]), partitions(p0)
1 row in set (0.00 sec)

结论
更多参考
oracle的nested loop anti join :
http://www.juliandyke.com/Optimisation/Operations/NestedLoopsAnti.php
如果觉得本文有帮助就点个赞😄️,有问题欢迎进钉钉群讨论。
文章转载自数据库技术闲谈,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




