在 Oracle 中的 2 个表上加入相同类型但不同长度的列
Join column of same type but different length on 2 tables in Oracle
我有 2 个 table,每个都有一个 nvarchar2 类型的列 sub_id,但长度不同(一个是 30,另一个是 255)。我在这两个 table 各自的 sub_id 列上创建了数据库索引。我正在加入 sub_id 列中的这些 table。
我的 table 有数百万行,因此不使用索引的连接会花费很多时间。我不确定这里是否使用了索引,因为我怀疑列长度的差异可能会导致完整的 table 扫描。
请提供见解,因为我对此类基本的数据库管理概念还很陌生。
我试图阅读更多相关内容,但找不到足够具体的内容。
编辑:
另一个查询,我们可以将这 2 sub_id 列作为一个 varchar2,另一个作为 nvarchar 并使用索引吗?
只要数据类型兼容——即不需要(隐式)转换——优化器可以使用索引来连接 tables。
My tables have millions of rows thus a join without index use takes a
lot of time
视情况而定!
如果您要从两个 table 中获取大部分*行,则完全扫描两者会更快。然后哈希加入结果。
例如,这将连接来自两个 table 的所有行。你得到了一切,所以没有必要使用索引:
create table t1 (
c1, c2
) as
select cast ( level as nvarchar2(30) ) , rpad ( 'stuff', 100, 'f' )
from dual
connect by level <= 1000;
create table t2 (
c1, c2, c3
) as
select cast ( level as nvarchar2(255) ) , mod ( level, 333 ) , rpad ( 'stuff', 100, 'f' )
from dual
connect by level <= 1000;
create index i1
on t1 ( c1 );
create index i2
on t2 ( c1 );
create index i2_c2
on t2 ( c2 );
exec dbms_stats.gather_table_stats ( user, 't1' ) ;
exec dbms_stats.gather_table_stats ( user, 't2' ) ;
set serveroutput off
alter session set statistics_level = all;
select * from t1
join t2
on t1.c1 = t2.c1;
select *
from table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST'));
-------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1000 |00:00:00.01 | 45 |
|* 1 | HASH JOIN | | 1 | 1000 | 1000 |00:00:00.01 | 45 |
| 2 | TABLE ACCESS FULL| T1 | 1 | 1000 | 1000 |00:00:00.01 | 18 |
| 3 | TABLE ACCESS FULL| T2 | 1 | 1000 | 1000 |00:00:00.01 | 27 |
-------------------------------------------------------------------------------------
如果您从一个 table 中得到几行*怎么办?
您需要使用索引来查找它们。并且 - 将这些链接中的每一个链接提供到另一个 table 中的少数行 - 在嵌套循环连接中的第二个 table 上使用索引。如本例所示,从一行 table 中获取三行。每个连接到另一个:
select * from t1
join t2
on t1.c1 = t2.c1
where t2.c2 = 0;
select *
from table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST'));
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 3 |00:00:00.01 | 13 |
| 1 | NESTED LOOPS | | 1 | 3 | 3 |00:00:00.01 | 13 |
| 2 | NESTED LOOPS | | 1 | 3 | 3 |00:00:00.01 | 10 |
| 3 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 3 | 3 |00:00:00.01 | 5 |
|* 4 | INDEX RANGE SCAN | I2_C2 | 1 | 3 | 3 |00:00:00.01 | 2 |
|* 5 | INDEX RANGE SCAN | I1 | 3 | 1 | 3 |00:00:00.01 | 5 |
| 6 | TABLE ACCESS BY INDEX ROWID | T1 | 3 | 1 | 3 |00:00:00.01 | 3 |
-------------------------------------------------------------------------------------------------
请注意,这确实依赖于连接列都是 nvarchar2
或 varchar2
。这些是 不兼容的 类型。因此,如果您混合搭配这些,优化器将无法在连接列上使用索引。
在前面的示例中从 nvarchar2
-> varchar2
切换 t1.c1 显示了这一点。现在,尽管从两个 table 中得到的行很少,但优化器会全面扫描 t3
:
create table t3 as
select cast ( c1 as varchar2(30) ) c1, c2 from t1;
select * from t3
join t2
on t3.c1 = t2.c1
where t2.c2 = 0;
select *
from table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST'));
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 3 |00:00:00.01 | 24 |
|* 1 | HASH JOIN | | 1 | 3 | 3 |00:00:00.01 | 24 |
| 2 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 3 | 3 |00:00:00.01 | 5 |
|* 3 | INDEX RANGE SCAN | I2_C2 | 1 | 3 | 3 |00:00:00.01 | 2 |
| 4 | TABLE ACCESS FULL | T3 | 1 | 1000 | 1000 |00:00:00.01 | 19 |
------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("T2"."C1"=SYS_OP_C2C("T3"."C1"))
3 - access("T2"."C2"=0)
注意到 t3.c1
上的 SYS_OP_C2C
操作了吗?这是一个功能。这意味着数据库不能在此列上使用(非基于函数的)索引。所以你有一个完整的扫描。
注意* 少而多是相对词!这些没有绝对值。我会在 this video series.
中进一步讨论这个问题
我有 2 个 table,每个都有一个 nvarchar2 类型的列 sub_id,但长度不同(一个是 30,另一个是 255)。我在这两个 table 各自的 sub_id 列上创建了数据库索引。我正在加入 sub_id 列中的这些 table。
我的 table 有数百万行,因此不使用索引的连接会花费很多时间。我不确定这里是否使用了索引,因为我怀疑列长度的差异可能会导致完整的 table 扫描。
请提供见解,因为我对此类基本的数据库管理概念还很陌生。 我试图阅读更多相关内容,但找不到足够具体的内容。
编辑: 另一个查询,我们可以将这 2 sub_id 列作为一个 varchar2,另一个作为 nvarchar 并使用索引吗?
只要数据类型兼容——即不需要(隐式)转换——优化器可以使用索引来连接 tables。
My tables have millions of rows thus a join without index use takes a lot of time
视情况而定!
如果您要从两个 table 中获取大部分*行,则完全扫描两者会更快。然后哈希加入结果。
例如,这将连接来自两个 table 的所有行。你得到了一切,所以没有必要使用索引:
create table t1 (
c1, c2
) as
select cast ( level as nvarchar2(30) ) , rpad ( 'stuff', 100, 'f' )
from dual
connect by level <= 1000;
create table t2 (
c1, c2, c3
) as
select cast ( level as nvarchar2(255) ) , mod ( level, 333 ) , rpad ( 'stuff', 100, 'f' )
from dual
connect by level <= 1000;
create index i1
on t1 ( c1 );
create index i2
on t2 ( c1 );
create index i2_c2
on t2 ( c2 );
exec dbms_stats.gather_table_stats ( user, 't1' ) ;
exec dbms_stats.gather_table_stats ( user, 't2' ) ;
set serveroutput off
alter session set statistics_level = all;
select * from t1
join t2
on t1.c1 = t2.c1;
select *
from table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST'));
-------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1000 |00:00:00.01 | 45 |
|* 1 | HASH JOIN | | 1 | 1000 | 1000 |00:00:00.01 | 45 |
| 2 | TABLE ACCESS FULL| T1 | 1 | 1000 | 1000 |00:00:00.01 | 18 |
| 3 | TABLE ACCESS FULL| T2 | 1 | 1000 | 1000 |00:00:00.01 | 27 |
-------------------------------------------------------------------------------------
如果您从一个 table 中得到几行*怎么办?
您需要使用索引来查找它们。并且 - 将这些链接中的每一个链接提供到另一个 table 中的少数行 - 在嵌套循环连接中的第二个 table 上使用索引。如本例所示,从一行 table 中获取三行。每个连接到另一个:
select * from t1
join t2
on t1.c1 = t2.c1
where t2.c2 = 0;
select *
from table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST'));
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 3 |00:00:00.01 | 13 |
| 1 | NESTED LOOPS | | 1 | 3 | 3 |00:00:00.01 | 13 |
| 2 | NESTED LOOPS | | 1 | 3 | 3 |00:00:00.01 | 10 |
| 3 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 3 | 3 |00:00:00.01 | 5 |
|* 4 | INDEX RANGE SCAN | I2_C2 | 1 | 3 | 3 |00:00:00.01 | 2 |
|* 5 | INDEX RANGE SCAN | I1 | 3 | 1 | 3 |00:00:00.01 | 5 |
| 6 | TABLE ACCESS BY INDEX ROWID | T1 | 3 | 1 | 3 |00:00:00.01 | 3 |
-------------------------------------------------------------------------------------------------
请注意,这确实依赖于连接列都是 nvarchar2
或 varchar2
。这些是 不兼容的 类型。因此,如果您混合搭配这些,优化器将无法在连接列上使用索引。
在前面的示例中从 nvarchar2
-> varchar2
切换 t1.c1 显示了这一点。现在,尽管从两个 table 中得到的行很少,但优化器会全面扫描 t3
:
create table t3 as
select cast ( c1 as varchar2(30) ) c1, c2 from t1;
select * from t3
join t2
on t3.c1 = t2.c1
where t2.c2 = 0;
select *
from table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST'));
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 3 |00:00:00.01 | 24 |
|* 1 | HASH JOIN | | 1 | 3 | 3 |00:00:00.01 | 24 |
| 2 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 3 | 3 |00:00:00.01 | 5 |
|* 3 | INDEX RANGE SCAN | I2_C2 | 1 | 3 | 3 |00:00:00.01 | 2 |
| 4 | TABLE ACCESS FULL | T3 | 1 | 1000 | 1000 |00:00:00.01 | 19 |
------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("T2"."C1"=SYS_OP_C2C("T3"."C1"))
3 - access("T2"."C2"=0)
注意到 t3.c1
上的 SYS_OP_C2C
操作了吗?这是一个功能。这意味着数据库不能在此列上使用(非基于函数的)索引。所以你有一个完整的扫描。
注意* 少而多是相对词!这些没有绝对值。我会在 this video series.
中进一步讨论这个问题