数据库表设计-架构知识学习笔记(超实用干货)

表架构

常见数据库:
ORACLE、DB2、SQLSERVER 、MYSQL、INFORMIX、SYBASE、GBASE、南大金仓、达梦、NOSQL、MANQODB、KEV-VALUE、HADOOP     

ORACLE是什么架构的数据库?
1、是关系型数据库(表与表之间有关系:体现方式如下:a.id=b.id)

2、ORACLE是行存储的数据库

行存储有什么优点:

比如银行或者即时交易 的数据库必须用行存储数据库。如果用列存储,插入一行,就会导致所有的列全部重新分布。原因是你按照列存储的,一个列的值肯定是重复的值比较多,是不是适合做压缩。

你用列存储去UPDATE ,DELETE ,INSERT 它是不是会把所有行给锁住。所以说列存储DB不能并发。

行存储支持大量的DML,支持复杂SQL,支持业务复杂的查询


列存储有什么优点:

一个列是不是重复值特别多,一个表的列是不是很多。列存储数据库一个人插入的时候,其他人就不能插入。


OLAP数据仓库有没有DML   很少是吧。如果用ORACLE作数据仓库,是不是表特别大,


行存储要让SQL跑得快,最牛B的就是做业务设计、表设计


如果 用 列存储数据库 尽可能的 怎么设计表?

尽可能把所有功能整合到一个大表中去,避免关联。   

一行一行的存储的数据,重复值是不是很少?而且一行一行的存储,基本都是在一个块一个块中。普通的ORACLE也可以压力,但压缩比例很少。比如一个表10G,最多压缩到6-8G。DML很频繁的表也不能压力,压缩就死人。ORACLE索引可以压缩吗?可以的哈。

列存储数据库也是按块存储,一个块存储的是列数据。既然是存的列的数据,大家想一想,一个列的数据是不是重复值非常多。这个时候作压缩是不是非常明显。一般来说都可以压缩到7倍以上。一个表,在ORACLE中有7G,在列存储中可能就1个G,最多两个G。因为列存储是按列压力,ORACLE是按表压缩。在ORACLE中你要扫描10G,你用列存储就扫描1个G。而性能就会提高10倍。当然ORACLE可以建索引。
假设一个表列非常多,比如100个列的表,10GB。而在列存储中可能就1个G。一般我不可能全部访问,最多访问20个列。如果是ORACLE还是需要把整体表,10G给扫描了,而用列存储就只访问需要的列。也就是1G/5=200M。也就是说ORACLE要扫描10G,列存储只扫描200M,是不是秒杀了。还有更的,ORACLE数据库可以对一个列或者多个列建索引。而列存储数据库需要建索引吗?我们建索引是为了减少扫描列,通过where条件列来进行范围扫描。而列存储 数据库单个列都是自己建了索引的。多个列也不需要建索引,因为它单个列自带了索引,可以自己组合起来。所以说列存储数据库更绝的就是不建索引。列存储用作OLAP特别牛B。列存储优化就是通过业务设计和表设计来优化。

列存储数据库一般是采用什么架构?

ORACLE是share disk共享磁盘的架构。而列存储是share nothing架构。而sysbase IQ是个奇葩,是shre disk架构,最新版本才支持share nothing.

RAC要配置共享磁盘,为什么ORACLE要用RAC架构。因为ORACLE它是行存储的,行存储的数据库一个表存在哪个地方?  是不是都存储在同样一个存储上面。share nothing架构是一个表存储在多个节点,每个节点都有自己的存储。

share disk 存储是共享的       share nothing  存储是分开的(分布式架构)

如果我现在有10个节点,列存储数据库中是不是10个节点里面都要存储数据。刚才说了列存储里面压缩了只有1个G的表,而这1个G的表是不是分布在10个节点,也就是说每个节点存100M。但是我又不需要把100M数据全读了,说白了我只需要读每个节点的20M,我靠,这相当于开了10个并行,直接秒杀了。这就是列存储的强大效果。又压缩,又并行,能不快吗?列存储做OLAP,如果是单表访问,基本上全都是秒杀。


用ORACLE数据库最担心的就是I/O,我们去买存储一般买什么存储,是不是要考虑RAID10,IOPS高,MBPS   如果用列存储就买个垃圾磁盘就行了,因为I/O都缩小、分散了。节点越多什么是瓶颈?只能是网络对吧。


下面来说说列存储的缺点:

1、 DML

2、扩容,比如我以前有10个节点,现在搞成11个节点,是不是其他数据库中库的数据就会迁移一部分到新节点。所以说扩容就死。不能实时,只能关机。

3、某一个节点挂了,是不是整个系统全挂。这个时候可靠性又来了。节点越多出问题的机率就越大。所以share nothing架构是不是每个节点都要备份啊,并且都要作镜像。我有10个节点,是不是要搞20台机器。而这20台机器互为镜像,这时投入是不是超大了。虽然说买的是垃圾存储,但这些投入加起来,已经差不我可以买一个牛B机器了。这么多机器,另外是不是管理起来TM的特别麻烦了,简直不能叫特别,是TM超级。

4、表设计TM要特别重要,刚才说的主要是单表访问。列存储表设计一定最好是单表,统统搞成单表才行。但是列存储也没法干掉关联啊。如果多表关联呢?
select 几个列 from a, b where a.id=b.id;

A和B是不是要分别存储到10个节点里面。那作关联的时候要关联几次?是不是单个结点作关联,然后进行汇总。你一个表要分布到10个节点里面,是不是要按照某个KEY来分布,对吧。这时我们如果是按照ID列分布的,这个SQL就可以每个节点自己关联,再汇总。但是一个表是不是特别复杂。比如,我现在又要A和C关联。
select 几个列 from a, b where a.xx=c.xx

A通过ID和B关联,通过XX和C关联。但是如果C.XX我在本地机器找不到,  这时怎么办?是不是要先把A全部读出来,再通过XX列重新分布,写到临时表。如果内存够用还好。如果内存不够,或者用的人多了,就傻B了。所以列存储数据库不支持比较高的并发。而且复杂的业务也不知道选谁当分布键。只能关联了就重新分布数据。所以share nothing架构最重要的  就是设计要避免关联,尽量单表。这样基本上就不会出现问题。这也是为什么列存储架构非常优秀,但使得的人不多的原因。

混合型数据坚决不能用。。业务复杂的也不能用。因为业务复杂,关联SQL就复杂。

ORACLE  

大表就并行HASH

大表小表就广播


任何数据库都要收集统计信息

目前最牛B的两个DB解决方案:

1、EXADATA      2、HANA


最牛B的数据库   EXADATA

EXADATA是什么架构?

是shre disk 架构。从架构上来说它支持7*24  有HA的作用。

EXADATA的存储也是按行存储,所以支持OLTP/OLAP

EXADATA搞了一个最底层存储软件,CELL,按照列存储。但是ORACLE实例并不知道它是按照列存储的。

这时事实它是行列混合存储。表面是行,底层是列。

所以EXADATA不需要建索引了。

一个表不建索引,是不是作批量导入这些就非快了。0延时插入。


HANA用的SYSBASE IQ,现在结合了另外一个数据库了。真正PK性能 ,HANA比EXADATA更快。






















表设计软件:一般都用    powerdesign
 
一定要注意表与表之间的关系,是一对一还是一对多

举例:

我们现开发一个类QQ的软件:

首先要创建用户表:

给字段取名字,确定字段类型,字段长度限制

怎么实现好友功能:

好友肯定是一对多对吧。一个人肯定会有上百上千的好友。

这时我们是不是需要再搞一个表出来,而这个表与用户表是什么关系,是不是一对多的关系。而 我们新创建的关系表一定要有哪些字段?

1、首先要有一个主键对吧,主键一般用ID嘛。

2、新创建的表既然是关系。用户表的ID肯定就在用户表了对吧。而我关系表已经有ID了。是不是还要有个好友ID。再加上加好友的时间。

3、好友分组,是不是加一个字段,作了标签就行了。

4、群功能。一个群号是唯一的。群号和人是什么关系。一个群对应多个人,是不是又要搞一个表出来了。
你要申请 一个群,QQ就要往DB里插入一条数据。记录表名,
 创建人,公告之类的。而某些人要加这个群,是不是又要搞一个关系表了。就是群和人的关系表。也是一对多。群的关系表就是群的ID,用户ID,群等级,群名片

做表设计,读懂需求就对了。先不要管性能,先实现需求。表设计好了,写SQL的时候再考虑该合并,合并 ,该拆分,拆分

另外最关键的就是搞清楚一对一还是一对多。




案例:利用ROWID   提升查询性能

ETL 开发人员发来邮件说,能不能想办法 提升一下如下update语句的性能


UPDATE OPT_ACCT_FDIM A
   SET ACCT_SKID = (SELECT ACCT_SKID
                      FROM OPT_ACCT_FDIM_BKP B
                     WHERE A.ACCT_ID = B.ACCT_ID);
                     

SELECT COUNT(*) FROM OPT_ACCT_FDIM;             -------这个表 有 226474  条数据


SELECT COUNT(*) FROM OPT_ACCT_FDIM_BKP;       ------ 这个表 有 227817 条数据

 

SELECT COUNT(*)
  FROM OPT_ACCT_FDIM A, OPT_ACCT_FDIM_BKP B
 WHERE A.ACCT_ID = B.ACCT_ID
   AND A.ACCT_SKID <> B.ACCT_SKID;                  -------要更新  226474 条   

 SELECT COUNT(*) FROM  OPT_ACCT_FDIM a
WHERE  EXISTS (SELECT 'x' FROM OPT_ACCT_FDIM_BKP b WHERE  A.ACCT_ID = B.ACCT_ID  );        这个才是正确的
 


那么现在已经很清楚了,业务逻辑就是根据 根据2个表的acct_id 字段关联,然后根据B表的字段update A表,那么这里呢 要更新整个A表

UPDATE的执行计划我们就不用看了,肯定是HASH JOIN,开发人员说 这个update 跑了30分钟,还没完成,其实我估计 这个SQL至少得1小时才能跑完。

 

其实,select 语句是很好优化的,但是update,delete这样的SQL, 如果要想从SQL上面优化,几乎不可能,优化update,delete我们要用PL/SQL来实现。

 

对于我们这里的UPDATE语句,我们可以利用rowid 来快速更新,PL/SQL 代码如下:

 

SQL> DECLARE
  2    CURSOR CUR_B IS
  3      SELECT 
  4       B.ACCT_ID, B.ACCT_SKID, A.ROWID ROW_ID
  5        FROM OPT_ACCT_DIM A, OPT_ACCT_DIM_BKP B
  6       WHERE A.ACCT_ID = B.ACCT_ID
  7       ORDER BY A.ROWID;  ---如果表的数据量不是很大,可以不用 order by rowid
  8    V_COUNTER NUMBER;
  9  BEGIN
 10    V_COUNTER := 0;
 11    FOR ROW_B IN CUR_B LOOP
 12      UPDATE OPT_ACCT_DIM
 13         SET ACCT_SKID = ROW_B.ACCT_SKID
 14       WHERE ROWID = ROW_B.ROW_ID;
 15      V_COUNTER := V_COUNTER + 1;
 16      IF (V_COUNTER >= 1000) THEN
 17        COMMIT;
 18        V_COUNTER := 0;
 19      END IF;
 20    END LOOP;
 21    COMMIT;
 22  END;
 23  /

PL/SQL procedure successfully completed.

Elapsed: 00:01:21.58

 

现在多快啊,1分22秒搞定

 

其实,以前的update就相当于下面的PL/SQL代码:

declare
  cursor c_update is
    select b.acct_skid, a.acct_id
      from opt_acct_fdim a, opt_acct_fdim_bkp b
     where a.acct_id = b.acct_id;
  v_counter number;
begin
  v_counter := 0;
  for v_row in c_update loop
    update opt_acct_fdim
       set acct_skid = v_row.acct_skid
     where acct_id = v_row.acct_id;   ---注意,这里没有rowid
    v_counter := v_counter + 1;
    if (v_counter >= 1000) then
      commit;
      v_counter := 0;
    end if;
    end loop;
    commit;
end;
/

 

我自己测试了一下上面的PL/SQL 代码,跑了30分钟没跑完,为什么跑这么久呢?

其实原因就在于这里:

update opt_acct_fdim
       set acct_skid = v_row.acct_skid
     where acct_id = v_row.acct_id; 

因为缺少 rowid定位,那么又会对表进行全表扫描,而且每更新一行就会去做全表扫描。

而我们利用rowid定位block,那么不用 全表扫描了 性能提升上 百倍。

 12      UPDATE OPT_ACCT_DIM
 13         SET ACCT_SKID = ROW_B.ACCT_SKID
 14       WHERE ROWID = ROW_B.ROW_ID;


其实这本书 Oracle Database 10g PL/SQL 程序设计 ---清华大学出版社 p132页 里面就有这个方法

itpub 这篇帖子:http://www.itpub.net/viewthread.php?tid=1052077 也提到过这个方法

 

总结:对于大批量的update,delete,我们可以利用rowid 来进行优化,性能往往提升 上百倍。


 怎么去检查要更新多少行数据???
 SELECT COUNT(*) FROM  OPT_ACCT_FDIM a
WHERE  EXISTS (SELECT 'x' FROM OPT_ACCT_FDIM_BKP b WHERE  A.ACCT_ID = B.ACCT_ID  );       落总原来的那个是错误的, 这个才是正确的


UPDATE OPT_ACCT_FDIM A
   SET ACCT_SKID = (SELECT ACCT_SKID
                      FROM OPT_ACCT_FDIM_BKP B
                     WHERE A.ACCT_ID = B.ACCT_ID);

从A里面取一条,传到子查询中去,然后再把A更新了。就是NL对吧。

B表是什么表,新创建的备份表,没有任何索引。因为这是TEMP中的备份表,肯定没有索引。而B表要被扫描A表总行数那么多次。B本来只有22W行,我们假设它有100M。意思就是跑个SQL 就有22W*100M这么多的I/O,所以说为什么这个SQL跑不出结果?


这个SQL不改代码怎么搞?

不改代码,B表始终要被搞22W次。我们是不是要想方设少减少B的体积。B要访问几个字段,是不是只访问两个列,这时我们建索引,NL的被驱动表是不是连接列在前。所以应该这样建
(ACCT_ID,ACCT_SKID)。这时通过索引当表用确实可以提高很多倍的性能。最优化的方法,是不是让两个都只扫描一次。

注意:UPDATE   主表  set OOXX =(select OOXX.....),当括号时面有主表连接条件的SQL只能走NL

 禁止 写 update ....where (子查询 跟主表 的 连接条件 或者过滤条件),如果非要这样写就必须改成merge into 





提问:

刚才的UPDATE 能开并行的吗?PLSQL能开并行吗?

答:PLSQL是不能开并行的。第一个UPDATE语句是可以开并行的,它会自动切片。但是这个并行很垃圾,因为B表要走索引,而索引没法并行。INDEX RANGE SCAN 是无法并行的







批量更新,MERGE语句性能最好,因为它可以多块读,并且可以并行执行,但是缺点就是消耗比较多的UNDO,一旦down机死事物恢复较慢。
          ORDER BY ROWID 在 buffer cache 不够大的情况下性能较好好(没Merge快,因为Merge可以多块读,走ROWID只能单块读)。
                                                                  优点就是可以批量提交。缺点就是不能并行更新。
          不 ORDER BY ROWID 在 buffer cache足够大(能确保被更新的表不被page out) 的情况下性能较好。
          


create table a as select * from dba_objects;
create table b as select * from dba_objects;
insert into b select * from b; --- 直到插入60W数据
SQL> SELECT COUNT(*) FROM B;

  COUNT(*)
----------
    616864

SQL>  SELECT SUM(BYTES)/1024/1024 "SIZE(MB)" FROM DBA_SEGMENTS WHERE SEGMENT_NAME='B';

  SIZE(MB)
----------
        72

create index idx_a on a(object_name,object_id);
create index idx_b on b(object_id);


比如要执行这个update b set b.object_name=(select a.object_name from a where a.object_id=b.object_id);

可以用MERGE代替
-------------------------MERGE版本,使用MERGE一定要确保MERGE into 的表走全表扫描----------------
alter session set db_file_multiblock_read_count=128;

如果要更新的表很大,alter session enable parallel dml;
                    alter session set workarea_size_policy=manual;
                    alter session set sort_area_size=xxx;
                    alter session set hash_area_size=xxx;

merge /*+ USE_HASH(C,H) FULL(C) */ into b c
using (select /*+INDEX(A) USE_HASH(A) */ a.object_name, a.object_id
         from a
        where a.object_id in (select /*+ use_hash(b) index(b) */ object_id from b)) h
on (c.object_id = h.object_id)
when matched then
  update set c.object_name = h.object_name;
select * from table(dbms_xplan.display);

也可以写PL/SQL 
-------------------------PL/SQL版本---------------------------------
DECLARE
  CURSOR CUR_B IS
    SELECT a.object_id, a.object_name, b.ROWID ROW_ID
      FROM A, B
     WHERE A.object_id = B.object_id
     ORDER BY B.ROWID;
  V_COUNTER NUMBER;
BEGIN
  V_COUNTER := 0;
  FOR ROW_B IN CUR_B LOOP
    UPDATE b SET object_name = ROW_B.object_name WHERE ROWID = ROW_B.ROW_ID;
    V_COUNTER := V_COUNTER + 1;
    IF (V_COUNTER >= 10000) THEN
      COMMIT;
      dbms_output.put_line('Updated: ' ||V_COUNTER || ' lines.');
      V_COUNTER := 0;
    END IF;
  END LOOP;
  COMMIT;
END;
/

下面这个版本是批量处理的版本
------------------------批量处理版本--------------------------------
declare
  maxrows      number default 100000;
  row_id_table dbms_sql.urowid_table;
  --currcount_table dbms_sql.number_Table;
  object_name_table dbms_sql.varchar2_Table;
  cursor cur_b is
    SELECT /*+ index(a) use_hash(a,b) index(b) */
     a.object_name, b.ROWID ROW_ID
      FROM A, B
     WHERE A.object_id = B.object_id
     ORDER BY B.ROWID;
  v_counter number;
begin
  v_counter := 0;
  open cur_b;
  loop
    EXIT WHEN cur_b%NOTFOUND;
    FETCH cur_b bulk collect
      into object_name_table, row_id_table limit maxrows;
    forall i in 1 .. row_id_table.count
      update b
         set object_name = object_name_table(i)
       where rowid = row_id_table(i);
    commit;
  end loop;
end;
/

关于ORDER BY ROWID提升速度的验证

-------------------Buffer cache 不够大---------------------------------------
buffer cache 40Mb,B表有72Mb 所以不够存放下 B
keep pool 52Mb--keep idx_a,idx_b

SQL> alter system flush buffer_cache;

系统已更改。

已用时间:  00: 00: 00.00
SQL> merge into b c
  2  using (select a.object_name, a.object_id
  3           from a
  4          where a.object_id in (select object_id from b)) h
  5  on (c.object_id = h.object_id)
  6  when matched then
  7    update set c.object_name = h.object_name;

616851 行已合并。

已用时间:  00: 00: 12.51

SQL>  alter system flush buffer_cache;

系统已更改。

已用时间:  00: 00: 00.00
SQL> declare
  2    maxrows      number default 100000;
  3    row_id_table dbms_sql.urowid_table;
  4    --currcount_table dbms_sql.number_Table;
  5    object_name_table dbms_sql.varchar2_Table;
  6    cursor cur_b is
  7      SELECT /*+ index(a) use_hash(a,b) index(b) */
  8       a.object_name, b.ROWID ROW_ID
  9        FROM A, B
 10       WHERE A.object_id = B.object_id
 11       ORDER BY B.ROWID;   ------有ORDER BY ROWID
 12    v_counter number;
 13  begin
 14    v_counter := 0;
 15    open cur_b;
 16    loop
 17      EXIT WHEN cur_b%NOTFOUND;
 18      FETCH cur_b bulk collect
 19        into object_name_table, row_id_table limit maxrows;
 20      forall i in 1 .. row_id_table.count
 21        update b
 22           set object_name = object_name_table(i)
 23         where rowid = row_id_table(i);
 24      commit;
 25    end loop;
 26  end;
 27  /

PL/SQL 过程已成功完成。

已用时间:  00: 00: 31.71
SQL> alter system flush buffer_cache;

系统已更改。

已用时间:  00: 00: 01.87
SQL> declare
  2    maxrows      number default 100000;
  3    row_id_table dbms_sql.urowid_table;
  4    --currcount_table dbms_sql.number_Table;
  5    object_name_table dbms_sql.varchar2_Table;
  6    cursor cur_b is
  7      SELECT /*+ index(a) use_hash(a,b) index(b) */
  8       a.object_name, b.ROWID ROW_ID
  9        FROM A, B
 10       WHERE A.object_id = B.object_id;
 11    v_counter number;
 12  begin
 13    v_counter := 0;
 14    open cur_b;
 15    loop
 16      EXIT WHEN cur_b%NOTFOUND;
 17      FETCH cur_b bulk collect
 18        into object_name_table, row_id_table limit maxrows;
 19      forall i in 1 .. row_id_table.count
 20        update b
 21           set object_name = object_name_table(i)
 22         where rowid = row_id_table(i);
 23      commit;
 24    end loop;
 25  end;
 26  /

PL/SQL 过程已成功完成。

已用时间:  00: 01: 25.64

MERGE只需要13秒,ORDER BY ROWID 的PL/SQL 需要32秒,而没ORDER BY ROWID 的PL/SQL 需要1分26秒

如果buffer cache不够大(不能容纳下A,B),不order by rowid 要花1分25秒,order by rowid只花了32秒
可见,ORDER BY ROWID 在BUFFER CACHE不够大的情况下,对于速度的提升是非常明显的,因为buffer cache不够大,block可能经常被page out。
order by rowid 会连续更新临近的block,这样就确保读入的block尽可能的不被page out。

为什么Merge 比 用ROWID 去更新快呢?因为MERGE可以多块读,做MERGE的时候设置参数db_file_multiblock_read_count=128
                                   根据ROWID去更新,只能一次读一个block


---------------------BUFFER CACHE 足够大,多次执行,取运行最快的时间---------------------------------------
SQL> alter system set db_keep_cache_size=150m;

系统已更改。

SQL> alter table a storage(buffer_pool keep);

表已更改。

SQL> alter table b storage(buffer_pool keep);

表已更改。

SQL> select /*+ full(a) */ count(*) from a;

  COUNT(*)
----------
     50526

已用时间:  00: 00: 00.16
SQL> select /*+ full(b) */ count(*) from b;

  COUNT(*)
----------
    616864

已用时间:  00: 00: 01.08

SQL> SELECT o.OWNER,o.object_type,o.OBJECT_NAME, COUNT(*) NUMBER_OF_BLOCKS
  2       FROM DBA_OBJECTS o, V$BH bh
  3      WHERE o.DATA_OBJECT_ID = bh.OBJD
  4        AND o.owner='SCOTT'
  5      GROUP BY o.owner,o.object_type, o.OBJECT_NAME
  6      ORDER BY COUNT(*)desc ,2 ;

OWNER                          OBJECT_TYPE         OBJECT_NAME                    NUMBER_OF_BLOCKS
------------------------------ ------------------- ------------------------------ ----------------
SCOTT                          TABLE               B                                          8467
SCOTT                          INDEX               IDX_B                                      1394
SCOTT                          TABLE               A                                           695
SCOTT                          INDEX               IDX_A                                       288

SQL> declare
  2    maxrows      number default 100000;
  3    row_id_table dbms_sql.urowid_table;
  4    --currcount_table dbms_sql.number_Table;
  5    object_name_table dbms_sql.varchar2_Table;
  6    cursor cur_b is
  7      SELECT /*+ index(a) use_hash(a,b) index(b) */
  8       a.object_name, b.ROWID ROW_ID
  9        FROM A, B
 10       WHERE A.object_id = B.object_id
 11       ORDER BY B.ROWID;
 12    v_counter number;
 13  begin
 14    v_counter := 0;
 15    open cur_b;
 16    loop
 17      EXIT WHEN cur_b%NOTFOUND;
 18      FETCH cur_b bulk collect
 19        into object_name_table, row_id_table limit maxrows;
 20      forall i in 1 .. row_id_table.count
 21        update b
 22           set object_name = object_name_table(i)
 23         where rowid = row_id_table(i);
 24      commit;
 25    end loop;
 26  end;
 27  /

PL/SQL 过程已成功完成。

已用时间:  00: 00: 11.83
SQL> declare
  2    maxrows      number default 100000;
  3    row_id_table dbms_sql.urowid_table;
  4    --currcount_table dbms_sql.number_Table;
  5    object_name_table dbms_sql.varchar2_Table;
  6    cursor cur_b is
  7      SELECT /*+ index(a) use_hash(a,b) index(b) */
  8       a.object_name, b.ROWID ROW_ID
  9        FROM A, B
 10       WHERE A.object_id = B.object_id;
 11    v_counter number;
 12  begin
 13    v_counter := 0;
 14    open cur_b;
 15    loop
 16      EXIT WHEN cur_b%NOTFOUND;
 17      FETCH cur_b bulk collect
 18        into object_name_table, row_id_table limit maxrows;
 19      forall i in 1 .. row_id_table.count
 20        update b
 21           set object_name = object_name_table(i)
 22         where rowid = row_id_table(i);
 23      commit;
 24    end loop;
 25  end;
 26  /

PL/SQL 过程已成功完成。

已用时间:  00: 00: 09.71

SQL> merge into b c
  2  using (select a.object_name, a.object_id
  3           from a
  4          where a.object_id in (select object_id from b)) h
  5  on (c.object_id = h.object_id)
  6  when matched then
  7    update set c.object_name = h.object_name;

616851 行已合并。

已用时间:  00: 00: 08.54

ORDER BY ROWID 的PL/SQL 用了11秒,没有ORDER BY ROWID的PL/SQL用了9秒,而Merge最快,只花了8秒多。
(反复测试,以最快时间为准,添加了3个logfile group 每组500Mb,减少logfile对测试的影响)。
由此可见,如果buffer cache够大,不order by rowid 反比order by rowid更快(因为少了排序)



总结:
要非常快 更新 用哪个???

merge into (加hint)  +   并行

alter table 要更新的表 nologging


merge into 要么是用来实现功能,要么是用来进行UPDATE 优化。。只能用于UPDATE





你可能感兴趣的:(oracle)