【问题标题】:Join column of same type but different length on 2 tables in Oracle在Oracle中的2个表上连接相同类型但长度不同的列
【发布时间】:2020-03-01 18:13:48
【问题描述】:

我有 2 个表,每个表都有一个 nvarchar2 类型的列 sub_id,但长度不同(一个是 30,另一个是 255 的长度)。我在这两个表上各自的 sub_id 列上创建了 db 索引。我正在加入 sub_id 列上的这些表。

我的表有数百万行,因此不使用索引的连接需要很多时间。我不确定这里是否使用了索引,因为我怀疑列长度的差异可能会导致全表扫描。

请提供见解,因为我对这些基本的数据库管理概念相当陌生。 我试图阅读更多关于此的内容,但可以找到足够具体的内容。

编辑: 另一个查询,我们可以将这 2 个 sub_id 列作为一个 varchar2 另一个作为 nvarchar 并使用索引吗?

【问题讨论】:

  • 查看说明计划,看看索引是否正在使用。但是,如果这些是两个表之间的键,则应定义正确的 foreign key 关系并修复类型,使它们具有相同的长度。
  • Oracle 将使用索引——前提是索引的使用是有意义的。索引并不是“让一切变得更快”的神奇工具。
  • @a_horse_with_no_name 如果您能指出一些描述何时使用索引有意义以及何时不使用索引的文献,将不胜感激。据我所知,索引的使用应该会加快我正在使用的查询,但鉴于列类型的差异,我怀疑索引是否会发挥作用。

标签: sql oracle database-indexes


【解决方案1】:

如果数据类型兼容 - 即不需要(隐式)转换 - 优化器可以使用索引来连接表。

我的表有数百万行,因此不使用索引的连接需要 很多时间

这取决于!

如果您要从两个表中获取大部分* 行,则对这两个表进行全盘扫描会更快。然后hash join结果。

例如,这将连接两个表中的所有行。你得到了一切,所以没有必要使用索引:

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 |    
-------------------------------------------------------------------------------------

如果您从一张表中获得几行*几行怎么办?

您需要使用索引来查找它们。并且 - 提供这些链接中的每一个都指向另一个表中的少数行 - 在嵌套循环连接中使用第二个表上的索引。如本例所示,它从一个表中获取三行。每一个都互相连接:

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 |    
-------------------------------------------------------------------------------------------------

请注意,这确实依赖于 nvarchar2varchar2 的连接列。这些是不兼容的类型。因此,如果您混合和匹配这些,优化器将无法在连接列上使用索引。

nvarchar2 切换t1.c1 -> 在前面的示例中varchar2 显示了这一点。现在,尽管从两个表中获取的行数很少,但优化器完全扫描了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 中进一步讨论这个问题。

【讨论】:

  • 绝妙的答案!干杯!
猜你喜欢
  • 2019-02-23
  • 2013-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-20
  • 2013-06-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多