首页 » MySQL, ORACLE 9i-23c, PostgreSQL/GaussDB » Oracle、MySQL、PostgreSQL等数据库比较系列(十五): hash join

Oracle、MySQL、PostgreSQL等数据库比较系列(十五): hash join

当两张大表做join访问时,我们希望优化器使用hash join的方式连接提高查询性能,但是在主流的oracle,mysql,postgresql或openGauss中表现稍有差异,所以在数据库替换时需要注意,简单记录一下对于equi join(=),non-equi-join(<>),Semijoin(exists), Antijoin(not exists/in), outer join(left/right join)时的不同表现。

Oracle

Oracle 对hash join的表关连支持较为丰富,但是oracle 的hash join 不支持  非等值条件, 下面只列几个不同数据库相比,此类型数据库JOIN不同的形为

SQL> explain plan for select count(a.name) from testa a,testb b where a.name=b.name;
Explained.

SQL> @x2

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------
Plan hash value: 26197381
-------------------------------------------------------------------------------------------------
| Id  | Operation              | Name           | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |                |     1 |    22 |       |   385   (1)| 00:00:01 |
|   1 |  SORT AGGREGATE        |                |     1 |    22 |       |            |          |
|*  2 |   HASH JOIN            |                |   100K|  2148K|  2248K|   385   (1)| 00:00:01 |
|   3 |    TABLE ACCESS FULL   | TESTA          |   100K|  1074K|       |    81   (2)| 00:00:01 |
|   4 |    INDEX FAST FULL SCAN| IDX_TESTB_NAME |   100K|  1074K|       |    85   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("A"."NAME"="B"."NAME")

SQL> explain plan for select /*+index(a)*/count(a.name) from testa a,testb b where a.name=b.name;
Explained.

SQL> @x2

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
Plan hash value: 3352132728

-------------------------------------------------------------------------------------------------
| Id  | Operation              | Name           | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |                |     1 |    22 |       |   613   (1)| 00:00:01 |
|   1 |  SORT AGGREGATE        |                |     1 |    22 |       |            |          |
|*  2 |   HASH JOIN            |                |   100K|  2148K|  2248K|   613   (1)| 00:00:01 |
|   3 |    INDEX FULL SCAN     | IDX_TESTA_NAME |   100K|  1074K|       |   308   (1)| 00:00:01 |
|   4 |    INDEX FAST FULL SCAN| IDX_TESTB_NAME |   100K|  1074K|       |    85   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("A"."NAME"="B"."NAME")

--也可以使用+index_ffs hint使用INDEX FAST FULL SCAN

SQL> explain plan for select /*+use_hash(t1,t2)*/ count(*) from t1 ,t2 where t1.a<>t2.a
  2  ;
Explained.

SQL> @x2
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------
Plan hash value: 916166457

----------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |     1 |   104 |    26   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE     |      |     1 |   104 |            |          |
|   2 |   NESTED LOOPS      |      |  6642 |   674K|    26   (0)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| T1   |    82 |  4264 |     2   (0)| 00:00:01 |
|*  4 |    TABLE ACCESS FULL| T2   |    81 |  4212 |     0   (0)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   4 - filter("T1"."A"<>"T2"."A")

Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 2 (U - Unused (2))
---------------------------------------------------------------------------

   3 -  SEL$1 / T1@SEL$1
         U -  use_hash(t1,t2)

   4 -  SEL$1 / T2@SEL$1
         U -  use_hash(t1,t2)

Note:
在Oracle中目前对于非=值的join条件还无法使用hash join.

MySQL

MySQL且在8.0.18中,引入hash join,但是和oracle一样还需要一个对等的条件(table1.a=table2.a)才能满足hash join。在8.0.20中,取消了对等条件的约束,可以全面支持non-equi-join,Semijoin,Antijoin,Left outer join/Right outer join。 官方样例

MYSQL_root@127.0.0.1 [anbob]> create table testa(id int,name varchar(20),addr varchar(3000), primary key(id));
Query OK, 0 rows affected (0.40 sec)

MYSQL_root@127.0.0.1 [anbob]> insert into testa
WITH RECURSIVE cte (n) AS
(
 SELECT 1
 UNION ALL
 SELECT n + 1 FROM
 cte WHERE n < 100000)
 SELECT n,concat('anbob',n),rpad(n,100,'*') name FROM cte;

MYSQL_root@127.0.0.1 [anbob]> create table testc as select * from testa;
Query OK, 100000 rows affected (3.00 sec)
Records: 100000  Duplicates: 0  Warnings: 0

MYSQL_root@127.0.0.1 [anbob]> create index idx_testc on testc(name);
Query OK, 0 rows affected (1.15 sec)
Records: 0  Duplicates: 0  Warnings: 0

MYSQL_root@127.0.0.1 [anbob]> create index idx_testa on testa(name);
Query OK, 0 rows affected (1.62 sec)
Records: 0  Duplicates: 0  Warnings: 0

MYSQL_root@127.0.0.1 [anbob]> show index from testa;
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| testa |          0 | PRIMARY   |            1 | id          | A         |       99212 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| testa |          1 | idx_testa |            1 | name        | A         |       99212 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
2 rows in set (0.00 sec)

MYSQL_root@127.0.0.1 [anbob]> show index from testc;
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| testc |          1 | idx_testc |            1 | name        | A         |       99203 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
1 row in set (0.01 sec)

MYSQL_root@127.0.0.1 [anbob]> select version();
+-------------------+
| version()         |
+-------------------+
| 8.0.20-commercial |
+-------------------+
1 row in set (0.00 sec)

MYSQL_root@127.0.0.1 [anbob]> show variables like '%optimizer_switch%' \G
*************************** 1. row ***************************
Variable_name: optimizer_switch
 Value: index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,
engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,
batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,
subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,
use_invisible_indexes=off,skip_scan=on,hash_join=on
1 row in set (0.00 sec)

MYSQL_root@127.0.0.1 [anbob]> explain format=tree select count(*) from testa a join testc c on a.name=c.name;       
+----------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                |
+----------------------------------------------------------------------------------------------------------------------------------------+
| -> Aggregate: count(0)                                                                                            
    -> Nested loop inner join  (cost=118728.20 rows=99203)                                                          
        -> Filter: (c.`name` is not null)  (cost=10168.55 rows=99203)                                               
            -> Index scan on c using idx_testc  (cost=10168.55 rows=99203)                                          
        -> Index lookup on a using idx_testa (name=c.`name`)  (cost=0.99 rows=1)                                    
|                                                                                                                  
+--------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)                                                                                                                 
                                                                                                    
																																		
## Use NO_INDEX() hint to force hash join to be used

MYSQL_root@127.0.0.1 [anbob]> explain format=tree select /*+no_index(a) no_index(c)*/ count(*) from testa a join testc c on a.name=c.name;     
+--------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                          |
+--------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Aggregate: count(0)                                                                                                                       
    -> Inner hash join (a.`name` = c.`name`)  (cost=984230411.40 rows=99203)                                                                   
        -> Table scan on a  (cost=0.07 rows=99212)                                                                                             
        -> Hash                                                                                                                                
            -> Table scan on c  (cost=10168.55 rows=99203)                                                                                     
 |                                                                                                                                             
+--------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)                                                                                                                        

MYSQL_root@127.0.0.1 [anbob]>  explain format=tree select count(*) from testa a join testc c on a.name<>c.name;   
+------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                          |
+------------------------------------------------------------------------------------------------------------------+
| -> Aggregate: count(0)                                                                                          
    -> Filter: (a.`name` <> c.`name`)  (cost=984230411.40 rows=8857914998)                                        
        -> Inner hash join (no condition)  (cost=984230411.40 rows=8857914998)                                    
            -> Index scan on a using idx_testa  (cost=0.16 rows=99212)                                            
            -> Hash                                                                                               
                -> Index scan on c using idx_testc  (cost=10168.55 rows=99203)                                    
 |                                                                                                                
+------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

hash join 也并不是总比NL join性能优,取决于对表数据的选择率selectivity, 且在mysql 8新的版本迭代中hash join的性能也可能有提升。

Hash join in MySQL 8.0 gives better join performance when
• No index is available
• Query is IO-bound
• Large part of a table will be accessed
• Selective conditions on multiple tables

Note:

注意在mysql中使用hash join 关连的列上不能使用index, 对于<>操作是个例外,它及能使用index也能使用hash join, 所以如果我们按照oracle的优化思路, 通常对于大表join的列上建议创建索引,而在mysql中可能会导致性能下降, 有hash join变化nest loop join  , 关于 8.0 hash join  PPT更多可以查看这里

Postgresql 13

anbob=# explain select count(a.name) from testa a,testc b where a.name=b.name;
                                                  QUERY PLAN
---------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=2319.70..2319.71 rows=1 width=8)
   ->  Hash Join  (cost=280.90..2294.70 rows=10000 width=10)
         Hash Cond: ((a.name)::text = (b.name)::text)
         ->  Index Only Scan using testa_name_idx on testa a  (cost=0.42..1539.22 rows=100000 width=10)
         ->  Hash  (cost=155.48..155.48 rows=10000 width=9)
               ->  Index Only Scan using idx_testc_uppname2 on testc b  (cost=0.29..155.48 rows=10000 width=9)
(6 行记录)

anbob=# explain select count(a.name) from testa a,testc b where a.name=b.name;
                                                  QUERY PLAN
---------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=2319.70..2319.71 rows=1 width=8)
   ->  Hash Join  (cost=280.90..2294.70 rows=10000 width=10)
         Hash Cond: ((a.name)::text = (b.name)::text)
         ->  Index Only Scan using testa_name_idx on testa a  (cost=0.42..1539.22 rows=100000 width=10)
         ->  Hash  (cost=155.48..155.48 rows=10000 width=9)
               ->  Index Only Scan using idx_testc_uppname2 on testc b  (cost=0.29..155.48 rows=10000 width=9)
(6 行记录)

anbob=# explain select count(a.name) from testa a,testc b where a.name<>b.name;
                                                        QUERY PLAN                                                      
---------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=12730580.84..12730580.85 rows=1 width=8)
   ->  Gather  (cost=12730580.63..12730580.84 rows=2 width=8)
         Workers Planned: 2
         ->  Partial Aggregate  (cost=12729580.63..12729580.64 rows=1 width=8)
               ->  Nested Loop  (cost=0.70..11687924.38 rows=416662500 width=10)
                     Join Filter: ((a.name)::text <> (b.name)::text)
                     ->  Parallel Index Only Scan using testa_name_idx on testa a  (cost=0.42..955.88 rows=41667 width=10)
                     ->  Index Only Scan using idx_testc_uppname2 on testc b  (cost=0.29..155.48 rows=10000 width=9)
(8 行记录)

anbob=# explain select  * from testa a where exists(select 1 from testc b where a.name=b.name) ;
                                               QUERY PLAN
---------------------------------------------------------------------------------------------------------
 Hash Semi Join  (cost=280.49..2195.24 rows=10000 width=14)
   Hash Cond: ((a.name)::text = (b.name)::text)
   ->  Seq Scan on testa a  (cost=0.00..1541.00 rows=100000 width=14)
   ->  Hash  (cost=155.48..155.48 rows=10000 width=9)
         ->  Index Only Scan using idx_testc_uppname2 on testc b  (cost=0.29..155.48 rows=10000 width=9)
(5 行记录)

anbob=# explain select  * from testa a where not exists(select 1 from testc b where a.name=b.name) ;
                                               QUERY PLAN
---------------------------------------------------------------------------------------------------------
 Hash Anti Join  (cost=280.49..2995.24 rows=90000 width=14)
   Hash Cond: ((a.name)::text = (b.name)::text)
   ->  Seq Scan on testa a  (cost=0.00..1541.00 rows=100000 width=14)
   ->  Hash  (cost=155.48..155.48 rows=10000 width=9)
         ->  Index Only Scan using idx_testc_uppname2 on testc b  (cost=0.29..155.48 rows=10000 width=9)
(5 行记录)

openGauss 5

anbob=# explain select /*+ hashjoin(a b) */ count(a.name) from testa a,testb b where a.name=b.name;
                                                 QUERY PLAN
------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=6304.01..6304.02 rows=1 width=18)
   ->  Hash Join  (cost=1296.45..6068.01 rows=94401 width=10)
         Hash Cond: ((a.name)::text = (b.name)::text)
         ->  Index Only Scan using idx_name on testa a  (cost=0.00..3077.55 rows=200000 width=10)
         ->  Hash  (cost=716.09..716.09 rows=46429 width=10)
               ->  Index Only Scan using idx_testb_name on testb b  (cost=0.00..716.09 rows=46429 width=10)
(6 rows)

anbob=# explain select /*+ hashjoin(a b) */ count(a.name) from testa a,testb b where a.name<>b.name;
WARNING:  unused hint: HashJoin(a b)
                                                 QUERY PLAN
------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=162505173.71..162505173.72 rows=1 width=18)
   ->  Nested Loop  (cost=0.00..139290909.71 rows=9285705599 width=10)
         Join Filter: ((a.name)::text <> (b.name)::text)
         ->  Index Only Scan using idx_name on testa a  (cost=0.00..3077.55 rows=200000 width=10)
         ->  Materialize  (cost=0.00..948.23 rows=46429 width=10)
               ->  Index Only Scan using idx_testb_name on testb b  (cost=0.00..716.09 rows=46429 width=10)
(6 rows)


anbob=#  explain select /*+hashjoin(a b)*/ * from testa a where not exists(select 1 from testb b where a.name=b.name);
                                              QUERY PLAN
------------------------------------------------------------------------------------------------------
 Hash Anti Join  (cost=1296.45..6065.64 rows=105599 width=14)
   Hash Cond: ((a.name)::text = (b.name)::text)
   ->  Seq Scan on testa a  (cost=0.00..3082.00 rows=200000 width=14)
   ->  Hash  (cost=716.09..716.09 rows=46429 width=10)
         ->  Index Only Scan using idx_testb_name on testb b  (cost=0.00..716.09 rows=46429 width=10)
(5 rows)

anbob=# explain select /*+hashjoin(a b)*/ * from testa a where exists(select 1 from testb b where a.name=b.name);
                                              QUERY PLAN
------------------------------------------------------------------------------------------------------
 Hash Semi Join  (cost=1296.45..5953.66 rows=94401 width=14)
   Hash Cond: ((a.name)::text = (b.name)::text)
   ->  Seq Scan on testa a  (cost=0.00..3082.00 rows=200000 width=14)
   ->  Hash  (cost=716.09..716.09 rows=46429 width=10)
         ->  Index Only Scan using idx_testb_name on testb b  (cost=0.00..716.09 rows=46429 width=10)
(5 rows)


anbob=#  explain select /*+hashjoin(a b) indexscan(a)*/ * from testa a where exists(select 1 from testb b where a.name=b.name) ;
                                              QUERY PLAN
------------------------------------------------------------------------------------------------------
 Hash Semi Join  (cost=1296.45..6057.41 rows=94401 width=14)
   Hash Cond: ((a.name)::text = (b.name)::text)
   ->  Index Scan using idx_name on testa a  (cost=0.00..3185.75 rows=200000 width=14)
   ->  Hash  (cost=716.09..716.09 rows=46429 width=10)
         ->  Index Only Scan using idx_testb_name on testb b  (cost=0.00..716.09 rows=46429 width=10)
(5 rows)

Note:
在PostgreSQL和openGauss当前版本中和oracle较为接近, 同样<>无法使用 join,其它可以hash join也可以使用index [only] scan.

equi-join(=) non-equi-join(<>) Semijoin(exists) Antijoin(not exists/in) outer join(left/right join)
oracle yes no yes yes yes
mysql8.20 yes① yes yes② yes② yes②
Postgresql13 yes no yes yes yes
openGauss5 yes no yes yes yes
 OceanbaseV3  yes no  yes  yes  yes
①列上无索引
②内表不使用索引

OCEANBASE

# ob for mysql
obclient [test]> select version();
+---------------------------+
| version()                 |
+---------------------------+
| 5.7.25-OceanBase-v3.2.4.0 |
+---------------------------+
1 row in set (0.002 sec)

obclient [test]> explain select count(*) from testa a join testc c on a.name=c.name\G
*************************** 1. row ***************************
Query Plan: ========================================================
|ID|OPERATOR             |NAME        |EST. ROWS|COST  |
--------------------------------------------------------
|0 |SCALAR GROUP BY      |            |1        |116329|
|1 | MERGE JOIN          |            |97689    |112600|
|2 |  PX COORDINATOR     |            |100000   |47986 |
|3 |   EXCHANGE OUT DISTR|:EX10000    |100000   |38681 |
|4 |    TABLE SCAN       |c(idx_testc)|100000   |38681 |
|5 |  TABLE SCAN         |a(idx_testa)|100000   |38681 |
========================================================

obclient [test]> explain select /*+full(a) full(c)*/ count(*) from testa a join testc c on a.name=c.name\G
*************************** 1. row ***************************
Query Plan: ====================================================
|ID|OPERATOR             |NAME    |EST. ROWS|COST  |
----------------------------------------------------
|0 |SCALAR GROUP BY      |        |1        |267277|
|1 | HASH JOIN           |        |108800   |263125|
|2 |  PX COORDINATOR     |        |100001   |47986 |
|3 |   EXCHANGE OUT DISTR|:EX10000|100001   |38681 |
|4 |    TABLE SCAN       |c       |100001   |38681 |
|5 |  TABLE SCAN         |a       |111373   |47918 |
====================================================

obclient [test]> explain select count(*) from testa a join testc c on a.name<>c.name\G
*************************** 1. row ***************************
Query Plan: ==============================================================
|ID|OPERATOR              |NAME        |EST. ROWS |COST      |
--------------------------------------------------------------
|0 |SCALAR GROUP BY       |            |1         |4075097142|
|1 | NESTED-LOOP JOIN     |            |9899903312|3697255733|
|2 |  TABLE SCAN          |a(idx_testa)|100000    |38681     |
|3 |  MATERIAL            |            |100000    |49286     |
|4 |   PX COORDINATOR     |            |100000    |47986     |
|5 |    EXCHANGE OUT DISTR|:EX10000    |100000    |38681     |
|6 |     TABLE SCAN       |c(idx_testc)|100000    |38681     |
==============================================================

obclient [test]> explain select /*+ full(a) full(c) */ count(*) from testa a right join testc c on a.name=c.name\G
*************************** 1. row ***************************
Query Plan: ====================================================
|ID|OPERATOR             |NAME    |EST. ROWS|COST  |
----------------------------------------------------
|0 |SCALAR GROUP BY      |        |1        |267277|
|1 | HASH OUTER JOIN     |        |108800   |263125|
|2 |  PX COORDINATOR     |        |100001   |47986 |
|3 |   EXCHANGE OUT DISTR|:EX10000|100001   |38681 |
|4 |    TABLE SCAN       |c       |100001   |38681 |
|5 |  TABLE SCAN         |a       |111373   |47918 |
====================================================


obclient [test]> explain select /*+ full(a) */ count(*) from testa a where not  exists(select /*+full(c)*/ 1 from testc c where a.name=c.name)\G
*************************** 1. row ***************************
Query Plan: ====================================================
|ID|OPERATOR             |NAME    |EST. ROWS|COST  |
----------------------------------------------------
|0 |SCALAR GROUP BY      |        |1        |244381|
|1 | HASH ANTI JOIN      |        |1000     |244342|
|2 |  TABLE SCAN         |a       |100000   |38681 |
|3 |  PX COORDINATOR     |        |100000   |47986 |
|4 |   EXCHANGE OUT DISTR|:EX10000|100000   |38681 |
|5 |    TABLE SCAN       |c       |100000   |38681 |
====================================================

obclient [test]> explain select /*+ full(a) */ count(*) from testa a where exists(select /*+full(c)*/ 1 from testc c where a.name=c.name)\G
*************************** 1. row ***************************
Query Plan: ====================================================
|ID|OPERATOR             |NAME    |EST. ROWS|COST  |
----------------------------------------------------
|0 |SCALAR GROUP BY      |        |1        |239082|
|1 | HASH RIGHT SEMI JOIN|        |110      |239078|
|2 |  PX COORDINATOR     |        |100001   |47986 |
|3 |   EXCHANGE OUT DISTR|:EX10000|100001   |38681 |
|4 |    TABLE SCAN       |c       |100001   |38681 |
|5 |  TABLE SCAN         |a       |111373   |47918 |
====================================================

Note:
OBmysql虽然是5.7 mysql,但是在OB中同样支持hash join. 但是<> 默认没有和mysql 8一样使用hash join,即使增加full 和use_hash hint。其他同样支持SEMI 、 ANTI 、 OUTER的 HASH JOIN

# OB for oracle
obclient [TBCS]> explain select count(a.name) from testa a join testc c on a.name=c.name\G
*************************** 1. row ***************************
Query Plan: ========================================================
|ID|OPERATOR             |NAME        |EST. ROWS|COST  |
--------------------------------------------------------
|0 |SCALAR GROUP BY      |            |1        |132984|
|1 | MERGE JOIN          |            |103890   |129019|
|2 |  TABLE SCAN         |A(IDX_TESTA)|99999    |38681 |
|3 |  PX COORDINATOR     |            |99999    |47985 |
|4 |   EXCHANGE OUT DISTR|:EX10000    |99999    |38681 |
|5 |    TABLE SCAN       |C(IDX_TESTC)|99999    |38681 |
========================================================

note:
OBoracle对于= 没有像oracle一样使用hash join index,而是merge join加分布式并行的方法.

在不同的数据库中对于表相同数据,join可能会产生不同的形为方式,即使是语法兼容也可能产生较大的性能差异, 尤其是在使用hint时,今天发现一个use_nl hint在达梦数据库上的不同表现,导致错误的认为达梦比oracle差了几百倍的性能,回头有达梦环境测试一下。数据库替换任重道远。

注: 如果发现不对之处请联系我。

打赏

对不起,这篇文章暂时关闭评论。