概述

本实验参考DevGym中的实验指南。

创建环境

创建表和过程,其中我加了注释:

-- 创建PL/SQL包,用于计时。
create or replace package timing_pkg as  start_time pls_integer; time_taken pls_integer; procedure set_start_time; procedure calc_runtime (  operation varchar2 ); end;
/create or replace package body timing_pkg as  procedure set_start_time as begin start_time := dbms_utility.get_time; end; procedure calc_runtime (  operation varchar2 ) as begin time_taken :=  ( dbms_utility.get_time - start_time ); dbms_output.put_line ( operation || ' ' || time_taken || ' hundredths of a second' ); end; end;
/-- 测试表bricks
create table bricks ( brick_id integer  not null  primary key, colour   varchar2(10),  shape    varchar2(10), weight   integer
)
;-- 下面这段代码不知何用。执不执行都无所谓。
begin insert into bricks values ( 1, 'red', 'cylinder', 1 );  insert into bricks values ( 2, 'blue', 'cube', 1 );  insert into bricks values ( 3, 'green', 'cube', 1 );  delete bricks; commit;
end;
/
truncate table bricks;-- 此过程用于插入指定行数的记录到bricks表,插入前会清空表
create or replace procedure ins_rows ( num_rows int ) as
beginexecute immediate 'truncate table bricks';dbms_random.seed ( 0 );insert into bricks  with rws as ( select level x from dual connect by level <= num_rows ) select rownum, case mod ( rownum, 3 )  when 0 then 'red' when 1 then 'blue' when 2 then 'green' end, case mod ( rownum, 2 )  when 0 then 'cube' when 1 then 'cylinder' end, round ( dbms_random.value ( 2, 10 ) ) from   rws;
end ins_rows;
/

查看数据,目前表为空:

SQL> select * from bricks;no rows selected

获取DML的执行计划

DML语句其实也是包括SELECT的,无论插入、删除还是更新,都是使用DBMS_XPlan获取执行计划:

-- 插入单条,不会用到索引
insert /*+ gather_plan_statistics */ into bricks values ( 0, 'red', 'cylinder', 1 ); select * from   table(dbms_xplan.display_cursor(format => 'IOSTATS LAST'));PLAN_TABLE_OUTPUT
________________________________________________________________________________________________
SQL_ID  81b27pds9qjrw, child number 0
-------------------------------------
insert /*+ gather_plan_statistics */ into bricks values ( 0, 'red',
'cylinder', 1 )---------------------------------------------------------------------------------------------
| Id  | Operation                | Name   | Starts | A-Rows |   A-Time   | Buffers | Reads  |
---------------------------------------------------------------------------------------------
|   0 | INSERT STATEMENT         |        |      1 |      0 |00:00:00.01 |      41 |      8 |
|   1 |  LOAD TABLE CONVENTIONAL | BRICKS |      1 |      0 |00:00:00.01 |      41 |      8 |
---------------------------------------------------------------------------------------------Note
------ cpu costing is off (consider enabling it)17 rows selected.-- CTAS,批量插入,不会用到索引
insert /*+ gather_plan_statistics */ into bricks select level, 'red', 'cylinder', 1 from   dualconnect by level <= 100;select * from   table(dbms_xplan.display_cursor(format => 'IOSTATS LAST'));PLAN_TABLE_OUTPUT
_____________________________________________________________________________________________________
SQL_ID  fpmz0m5yrx4kj, child number 0
-------------------------------------
insert /*+ gather_plan_statistics */ into bricks    select level,
'red', 'cylinder', 1    from   dual   connect by level <= 100Plan hash value: 1236776825--------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name   | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------
|   0 | INSERT STATEMENT              |        |      1 |        |      0 |00:00:00.01 |       5 |
|   1 |  LOAD TABLE CONVENTIONAL      | BRICKS |      1 |        |      0 |00:00:00.01 |       5 |
|   2 |   CONNECT BY WITHOUT FILTERING|        |      1 |        |    100 |00:00:00.01 |       0 |
|   3 |    FAST DUAL                  |        |      1 |      1 |      1 |00:00:00.01 |       0 |
--------------------------------------------------------------------------------------------------16 rows selected.-- 更新单条,用到了索引。因为表有主键
update /*+ gather_plan_statistics */ bricks
set    shape = 'cube'
where  brick_id = 1;select * from   table(dbms_xplan.display_cursor(format => 'IOSTATS LAST'));PLAN_TABLE_OUTPUT
_______________________________________________________________________________________________
SQL_ID  0zbg60ms26www, child number 0
-------------------------------------
update /*+ gather_plan_statistics */ bricks  set    shape = 'cube'
where  brick_id = 1Plan hash value: 1792883719--------------------------------------------------------------------------------------------
| Id  | Operation          | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT   |             |      1 |        |      0 |00:00:00.01 |       2 |
|   1 |  UPDATE            | BRICKS      |      1 |        |      0 |00:00:00.01 |       2 |
|*  2 |   INDEX UNIQUE SCAN| SYS_C008657 |      1 |      1 |      1 |00:00:00.01 |       1 |
--------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - access("BRICK_ID"=1)20 rows selected.-- 批量删除,用到了索引。因为表有主键
delete /*+ gather_plan_statistics */ bricks where  brick_id <= 10;select * from   table(dbms_xplan.display_cursor(format => 'IOSTATS LAST'));PLAN_TABLE_OUTPUT
______________________________________________________________________________________________
SQL_ID  2sdbmwz2j55y0, child number 0
-------------------------------------
delete /*+ gather_plan_statistics */ bricks where  brick_id <= 10Plan hash value: 1389320603-------------------------------------------------------------------------------------------
| Id  | Operation         | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------
|   0 | DELETE STATEMENT  |             |      1 |        |      0 |00:00:00.01 |      14 |
|   1 |  DELETE           | BRICKS      |      1 |        |      0 |00:00:00.01 |      14 |
|*  2 |   INDEX RANGE SCAN| SYS_C008657 |      1 |     11 |     11 |00:00:00.01 |       1 |
-------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - access("BRICK_ID"<=10)Note
------ dynamic statistics used: dynamic sampling (level=2)23 rows selected.

检查副作用

影响写的因素包括:

  • 触发器
  • 阻塞锁

触发器最好少用,但不是说不能用。否则发明它干嘛。

查看定义的触发器:

select * from user_triggers;

与 SELECTS 不同,其他会话中未提交的更改可以阻止写入完成。常见原因有:

  • 插入的主键或唯一键发生冲突
  • 另一个会话更新或删除相同的行

很难完全避免这个问题。例如,如果两个用户需要同时更新相同的行,则第二个用户必须等到第一个用户结束他们的事务。为尽量减少其影响,请确保尽快提交或回滚事务(也就是小事务)。如果这些问题仍然存在,您可能需要重新设计处理数据更改的方式。

单行插入与批量插入

以下为单行插入或逐行插入,是应该尽量避免的:

begindelete bricks;for i in 1 .. 10000 loop insert into bricks  values (  i,  case mod ( i, 3 )  when 0 then 'red' when 1 then 'blue' when 2 then 'green' end, case mod ( i, 2 )  when 0 then 'cube' when 1 then 'cylinder' end, round ( dbms_random.value ( 2, 10 ) ) ); end loop;
end;
/

尽管单个插入的执行速度都很快,但这些加起使整个过程变慢。如果 SQL 来自中间层应用程序,这尤其成问题,因为每一行往返数据库的时间很长。

如果在上面循环中加上提交(逐行提交),整个过程会更慢!

可以使用批量处理FOR ALL加快此过程。例如:

delete bricks;
select count(*) from bricks;declaretype bricks_rec is record (brick_id integer, colour varchar2(10),shape    varchar2(10), weight integer);type bricks_array is table of bricks_recindex by pls_integer;brick_values bricks_array;
begin for i in 1 .. 10000 loopbrick_values ( i ) := bricks_rec (brick_id => i + 20000, colour => case mod ( i, 3 )  when 0 then 'red' when 1 then 'blue' when 2 then 'green' end, shape => case mod ( i, 2 )  when 0 then 'cube' when 1 then 'cylinder' end, weight => round ( dbms_random.value ( 2, 10 ) ) );end loop;forall rws in 1 .. brick_values.countinsert into bricks  values brick_values ( rws );end;
/-- 下面的commit是我加的
commit;select count(*) from bricks;

FORALL 看起来和 FOR 循环类似。但与循环不同的是,它只处理内部的 DML 语句一次(一次性插入一个数组)。

单行插入与批量插入性能比较

原文代码如下:

declarenum_rows pls_integer := 100000;type bricks_rec is record (brick_id integer, colour varchar2(10),shape    varchar2(10), weight integer);type bricks_array is table of bricks_recindex by pls_integer;brick_values bricks_array;
begin delete bricks;commit;timing_pkg.set_start_time; for i in 1 .. num_rows loop insert into bricks  values (  i,  case mod ( i, 3 )  when 0 then 'red' when 1 then 'blue' when 2 then 'green' end, case mod ( i, 2 )  when 0 then 'cube' when 1 then 'cylinder' end, round ( dbms_random.value ( 2, 10 ) ) ); end loop; timing_pkg.calc_runtime ( 'Insert-loop' ); rollback; timing_pkg.set_start_time; for i in 1 .. num_rows loopbrick_values ( i ) := bricks_rec (brick_id => i, colour => case mod ( i, 3 )  when 0 then 'red' when 1 then 'blue' when 2 then 'green' end, shape => case mod ( i, 2 )  when 0 then 'cube' when 1 then 'cylinder' end, weight => round ( dbms_random.value ( 2, 10 ) ) );end loop;forall rws in 1 .. brick_values.countinsert into bricks  values brick_values ( rws );timing_pkg.calc_runtime ( 'Insert-forall' ); end;
/

要产生输出,需要设置:

set serveroutput on

比较结果如下,批量插入快了近10倍。其中hundredths of a second表示计时单位为1/100秒:

Insert-loop 231 hundredths of a second
Insert-forall 25 hundredths of a second

许多单行插入 vs. 一个多行插入

可以使用 INSERT 将行从一个表复制到另一个表。此代码使用游标将 10,000行从一个表加载到另一个表:

begindelete bricks;for rw in ( with rws as ( select level x from dual connect by level <= 10000) select rownum id, case mod ( rownum, 3 )  when 0 then 'red' when 1 then 'blue' when 2 then 'green' end colour, case mod ( rownum, 2 )  when 0 then 'cube' when 1 then 'cylinder' end shape, round ( dbms_random.value ( 2, 10 ) ) weightfrom   rws) loop insert into bricks  values (  rw.id,  rw.colour, rw.shape,rw.weight); end loop;
end;
/

以下为更快的INSERT-AS-SELECT方法,一次性添加所有 10,000 行:

delete bricks;
insert into bricks  with rws as ( select level x from dual connect by level <= 10000) select rownum, case mod ( rownum, 3 )  when 0 then 'red' when 1 then 'blue' when 2 then 'green' end, case mod ( rownum, 2 )  when 0 then 'cube' when 1 then 'cylinder' end, round ( dbms_random.value ( 2, 10 ) ) from   rws;

当然,之前的FORALL INSERT方法仍然可用:

declaretype bricks_rec is record (brick_id integer, colour varchar2(10),shape    varchar2(10), weight integer);type bricks_array is table of bricks_recindex by pls_integer;brick_values bricks_array;num_rows pls_integer := 10000;
begin delete bricks;with rws as ( select level x from dual connect by level <= num_rows ) select rownum, case mod ( rownum, 3 )  when 0 then 'red' when 1 then 'blue' when 2 then 'green' end, case mod ( rownum, 2 )  when 0 then 'cube' when 1 then 'cylinder' end, round ( dbms_random.value ( 2, 10 ) ) bulk collectinto   brick_valuesfrom   rws; forall rws in 1 .. brick_values.countinsert into bricks  values brick_values ( rws );end;
/

插入性能比较

也就是以上介绍的单行插入,INSERT-SELECT、FORALL INSERT3种方法的比较。

declarenum_rows pls_integer := 100000;type bricks_rec is record (brick_id integer, colour varchar2(10),shape    varchar2(10), weight integer);type bricks_array is table of bricks_recindex by pls_integer;brick_values bricks_array;
begin delete bricks;commit;timing_pkg.set_start_time; for rw in (with rws as ( select level x from dual connect by level <= num_rows ) select rownum id, case mod ( rownum, 3 )  when 0 then 'red' when 1 then 'blue' when 2 then 'green' end, case mod ( rownum, 2 )  when 0 then 'cube' when 1 then 'cylinder' end, round ( dbms_random.value ( 2, 10 ) ) from   rws) loop insert into bricks  values (  rw.id,  case mod ( rw.id, 3 )  when 0 then 'red' when 1 then 'blue' when 2 then 'green' end, case mod ( rw.id, 2 )  when 0 then 'cube' when 1 then 'cylinder' end, round ( dbms_random.value ( 2, 10 ) ) ); end loop; timing_pkg.calc_runtime ( 'Insert-loop' ); rollback; timing_pkg.set_start_time; insert into bricks  with rws as ( select level x from dual connect by level <= num_rows ) select rownum, case mod ( rownum, 3 )  when 0 then 'red' when 1 then 'blue' when 2 then 'green' end, case mod ( rownum, 2 )  when 0 then 'cube' when 1 then 'cylinder' end, round ( dbms_random.value ( 2, 10 ) ) from   rws; timing_pkg.calc_runtime ( 'Insert-select' ); rollback;timing_pkg.set_start_time; with rws as ( select level x from dual connect by level <= num_rows ) select rownum, case mod ( rownum, 3 )  when 0 then 'red' when 1 then 'blue' when 2 then 'green' end, case mod ( rownum, 2 )  when 0 then 'cube' when 1 then 'cylinder' end, round ( dbms_random.value ( 2, 10 ) ) bulk collectinto   brick_valuesfrom   rws; forall rws in 1 .. brick_values.countinsert into bricks  values brick_values ( rws );timing_pkg.calc_runtime ( 'Insert-forall' ); end;
/

比较结果如下,Insert-select最快,Insert-forall性能稍逊,但代码复杂得多:

Insert-loop 253 hundredths of a second
Insert-select 31 hundredths of a second
Insert-forall 38 hundredths of a second

多个单行更新 vs. 一个多行更新

以下为多个单行更新示例:

declarenum_rows pls_integer := 10000;
begin ins_rows ( num_rows );for rws in (select * from bricks where colour = 'red') loop update bricks bset    weight = 1where  b.brick_id = rws.brick_id; end loop; end;
/

其实以下一个语句就可以实现,而且更高效:

update bricks
set    weight = 1
where  colour = 'red' ;

当然,FORALL方法仍可以用:

declarenum_rows pls_integer := 10000;rws dbms_sql.number_table;begin ins_rows ( num_rows );select brick_idbulk collect into rwsfrom  brickswhere colour = 'red';forall i in 1 .. rws.count update bricks set    weight = 1where  brick_id = rws (i);
end;
/

虽然性能不是最好,但有时也可能有用,比如需要对每一行做非SQL处理。

更新性能比较

也就是以上3种更新方法的比较:

declarenum_rows pls_integer := 100000;rws dbms_sql.number_table;begin ins_rows ( num_rows );timing_pkg.set_start_time; for i in 1 .. num_rows loop update bricks set    colour = 'pink',  shape = 'cone' where  brick_id = i; end loop; timing_pkg.calc_runtime ( 'Update-loop' ); ins_rows ( num_rows );timing_pkg.set_start_time; update bricks set    colour = 'pink',  shape = 'cone'; timing_pkg.calc_runtime ( 'Update-all' ); ins_rows ( num_rows );for i in 1 .. num_rows looprws (i) := i;end loop;timing_pkg.set_start_time; forall i in 1 .. rws.count update bricks set    colour = 'pink',  shape = 'cone'where  brick_id = rws (i); timing_pkg.calc_runtime ( 'Update-forall' );
end;
/

结果如下:

Update-loop 110 hundredths of a second
Update-all 30 hundredths of a second
Update-forall 44 hundredths of a second

多个单行删除 vs. 一个多行删除

单行删除:

declarenum_rows pls_integer := 10000;
begin ins_rows ( num_rows );for rw in 1 .. 1000 loop delete bricks bwhere  b.brick_id = rw; end loop; end;
/

其实单个语句更简单和高效:

exec ins_rows ( 10000 );delete bricks b
where  b.brick_id between 1 and 1000;

FOR ALL方法仍可以用:

exec   ins_rows ( 10000 );select count(*) from bricks;declarerws dbms_sql.number_table;begin for i in 1 .. 1000 looprws (i) := i;end loop;forall i in 1 .. rws.count delete bricks where  brick_id = rws (i); end;
/select count(*) from bricks;

删除性能比较

declarenum_rows pls_integer := 100000;rws dbms_sql.number_table;begin ins_rows ( num_rows );timing_pkg.set_start_time; for i in 1 .. num_rows loop delete bricks where  brick_id = i; end loop; timing_pkg.calc_runtime ( 'Delete-loop' ); ins_rows ( num_rows );timing_pkg.set_start_time; delete bricks; timing_pkg.calc_runtime ( 'Delete-all' ); ins_rows ( num_rows );for i in 1 .. num_rows looprws (i) := i;end loop;timing_pkg.set_start_time; forall i in 1 .. rws.count delete bricks where  brick_id = rws (i); timing_pkg.calc_runtime ( 'Delete-forall' );
end;
/

结果如下,还是一次删除多行最快:

Delete-loop 185 hundredths of a second
Delete-all 74 hundredths of a second
Delete-forall 132 hundredths of a second

使用 DDL 调优更新

带有 where 子句的 UPDATE 比遍历行并为每一行运行 UPDATE 快得多。但单次更新仍可能需要很长时间才能完成。并且它从 UPDATE 开始直到事务完成会锁定所有受影响的行。

对于大表,如果要更改表中的大部分或全部行,您可以通过DDL方法更快地执行更新:

  • 使用 CTAS 创建一个临时表。在此 SELECT语句中提供新的列值
  • 切换原表和临时表

很少需要使用这个技巧。可用于一次性迁移或其它速度至关重要的情况。

以下利用CTAS将更新缓存到临时表中:

exec ins_rows ( 100000 );create table bricks_update ( brick_id primary key, colour, shape, weight
) as  select brick_id,  cast ( 'yellow'as varchar2(10) ) colour, cast ( 'prism' as varchar2(10) ) shape,  weight from   bricks;

在和原表互换之前,还需要将权限,约束,索引,触发器等迁移到临时表。

然后可以进行互换了:

drop table bricks purge; rename bricks_update to bricks;

使用 DDL 调优更新性能比较

declarenum_rows pls_integer := 100000;
begin ins_rows ( num_rows );timing_pkg.set_start_time; update bricks set    colour = 'yellow',  shape = 'prism'; timing_pkg.calc_runtime ( 'Update-all' ); ins_rows ( num_rows );timing_pkg.set_start_time; execute immediate q'! create table bricks_update ( brick_id primary key, colour, shape, weight ) as  select brick_id,  cast ( 'yellow'as varchar2(10) ) colour, cast ( 'prism' as varchar2(10) ) shape,  weight from   bricks!'; execute immediate 'drop table bricks purge'; execute immediate 'rename bricks_update to bricks'; timing_pkg.calc_runtime ( 'Update-ctas' );
end;
/

结果如下:

Update-all 67 hundredths of a second
Update-ctas 24 hundredths of a second

使用 DDL 调优删除:删除所有行

同样可以使用将 DML 更改为 DDL 的思路来加快删除过程。极端情况为删除所有行:

declarenum_rows pls_integer := 100000;
begin ins_rows ( num_rows );timing_pkg.set_start_time; delete bricks; timing_pkg.calc_runtime ( 'Delete-all' ); ins_rows ( num_rows );timing_pkg.set_start_time; execute immediate 'truncate table bricks';timing_pkg.calc_runtime ( 'Truncate' );
end;
/

结果如下:

Delete-all 53 hundredths of a second
Truncate 3 hundredths of a second

但是truncate不能回退,而且删除所有行比较少见。

将删除调整为 DDL:删除大部分行

与 UPDATE 一样,您可以使用 DDL 技巧来“删除”数据。有以下方法:

  1. CTAS 保存行,然后切换行(清空原表,并将保存的行插入)
  2. CTAS 保存行,然后切换表 (重命名表)
  3. 过滤表移动(filtered table move)

这些方法在删除表中的大部分行(大于50%)时最有效。

第一种方法:

exec ins_rows ( 10000 );select count (*) from bricks;create table bricks_keep ( brick_id primary key, colour, shape, weight ) as select * from bricks where  brick_id > 9500;select count (*) from bricks_keep;truncate table bricks; insert into bricksselect * from bricks_keep;

第3种方法:

exec ins_rows ( 10000 );select count(*) from bricks;alter table bricks move  including rows where brick_id > 9500;select count(*) from bricks;

将删除调整为 DDL:删除大部分行的性能比较

drop table bricks_keep purge;declarenum_rows pls_integer := 100000;
begin ins_rows ( num_rows );timing_pkg.set_start_time; delete bricks where  brick_id <= num_rows * 0.9; timing_pkg.calc_runtime ( 'Delete-where' ); ins_rows ( num_rows );timing_pkg.set_start_time; execute immediate ' create table bricks_keep ( brick_id primary key, colour, shape, weight ) as select * from bricks where  brick_id > ' || ( num_rows * 0.9 ); execute immediate 'truncate table bricks'; execute immediate 'insert into bricksselect * from bricks_keep';timing_pkg.calc_runtime ( 'Delete-ctas-swap-rows' ); execute immediate 'drop table bricks_keep purge';ins_rows ( num_rows );timing_pkg.set_start_time; execute immediate ' create table bricks_keep ( brick_id primary key, colour, shape, weight ) as select * from bricks where  brick_id > ' || ( num_rows * 0.9 ); execute immediate 'drop table bricks purge';execute immediate 'rename bricks_keep to bricks'; timing_pkg.calc_runtime ( 'Delete-ctas-swap-table' ); ins_rows ( num_rows );timing_pkg.set_start_time; execute immediate ' alter table bricks move  including rows where brick_id > ' || ( num_rows * 0.9 ); timing_pkg.calc_runtime ( 'Delete-move' ); end;
/

结果如下:

Delete-where 47 hundredths of a second
Delete-ctas-swap-rows 7 hundredths of a second
Delete-ctas-swap-table 5 hundredths of a second
Delete-move 4 hundredths of a second

若删除的行较少,可能常规的删除更快。特别对于归档应用中删除最老数据,分区可能最有效。

使用 DDL 调整删除:删除多行

注意,这里的场景是多行(many),而非大部分(most)行。

对表进行分区会将其拆分为更小的子表。可以单独对分区执行操作(删除分区),其他分区不受影响。

以下DDL将表更改为分区表:

alter table bricks modify partition by range ( brick_id ) interval ( 10000 ) ( partition p0 values less than ( 10001 ),partition p1 values less than ( 20001 ) ) online;

单独清空分区:

exec ins_rows ( 30000 );select count(*) from bricks;alter table bricks truncate partition p1;select count(*) from bricks;COUNT(*)
___________30000COUNT(*)
___________20000

按插入日期对表进行分区是一种常用的策略。这样可以快速删除在某个日期之前添加的所有行。

对表进行分区会影响针对它运行的所有语句。与非分区表相比,读取多个分区的操作可能会更慢。

使用 DDL 调整删除:删除多行性能比较

declarenum_rows pls_integer := 100000;
beginins_rows ( num_rows );timing_pkg.set_start_time; delete brickswhere brick_id <= 20000;timing_pkg.calc_runtime ( 'Delete-where' ); timing_pkg.set_start_time; execute immediate ' alter table brickstruncate partition p0, p1'; timing_pkg.calc_runtime ( 'Truncate-partition' );
end;
/

结果为:

Delete-where 8 hundredths of a second
Truncate-partition 3 hundredths of a second

挑战

需要优化的SQL如下:

declarenum_rows pls_integer := 50000;
beginins_rows ( num_rows );timing_pkg.set_start_time; for rws in ( select level id from dual connect by level <= 20000) loopinsert into bricks ( brick_id, colour, shape, weight )values ( rws.id + num_rows, 'red', 'cube', 1 );end loop;for rws in ( select brick_id from bricks where colour = 'blue') loopupdate bricks bset    weight = 2where  b.brick_id = rws.brick_id;end loop;for rws in ( select brick_id from bricks where colour = 'green') loopdelete bricks bwhere  b.brick_id = rws.brick_id;end loop;timing_pkg.calc_runtime ( 'Looping' );end;
/

其运行时间为:

Looping 104 hundredths of a second

优化后的语句如下:

declarenum_rows pls_integer := 50000;
beginins_rows ( num_rows );timing_pkg.set_start_time; insert into bricks with rws as ( select level id from dual connect by level <= 20000 ) select rws.id + num_rows, 'red', 'cube', 1 from rws;update bricks b set weight = 2 where colour = 'blue';delete bricks b where colour = 'green';timing_pkg.calc_runtime ( 'Optimized' );end;
/commit;

结果为:

Optimized 12 hundredths of a second

清理环境

drop table bricks;

Oracle开发者性能课第8课(如何更快地进行插入、更新和删除)实验相关推荐

  1. Oracle开发者性能课第3课(我的查询做了多少工作)实验

    概述 本实验参考DevGym中的实验指南. 创建环境 首先创建表: -- 此参数默认值即为auto alter session set WORKAREA_SIZE_POLICY = auto;crea ...

  2. clickhouse hbase性能对比_如何让 HBase 更快、更稳、更省钱

    概述 Apache HBase 是 Apache Hadoop 生态体系中的大规模.可扩展.分布式的数据存储服务.同时它还是 NoSQL 数据库.它的设计初衷是为包含了数百万列的数十亿行记录提供随机的 ...

  3. Android性能优化:如何让App更快、更稳、更省(含内存、布局优化等)

    前言 在 Android开发中,性能优化策略十分重要 因为其决定了应用程序的开发质量:可用性.流畅性.稳定性等,是提高用户留存率的关键 本文全面讲解性能优化中的所有知识,献上一份 Android性能优 ...

  4. Oracle开发者性能课第9课(如何查找慢 SQL)实验

    概述 本实验参考DevGym中的实验指南. 创建环境 创建表和过程,其中我加了注释: create table schools (school_id integer not null primary ...

  5. Oracle开发者性能课第6课(如何创建物化视图)实验

    概述 本实验参考DevGym中的实验指南. 创建环境 创建表bricks和索引: exec dbms_random.seed ( 0 ); create table bricks ( brick_id ...

  6. Oracle开发者性能课第5课(为什么我的查询不使用索引)实验

    概述 本实验参考DevGym中的实验指南. 创建环境 创建表bricks和索引,及全局临时表bricks_temp,并在最后搜集统计信息: exec dbms_random.seed ( 0 ); c ...

  7. 自主品牌语音交互性能测评,荣威RX5反应更快,博越变暖男

    文:小伟哥 图:小伟哥.网络 说起语音控制,我们第一时间想到的是iphone上的SiRi,你可以通过语音打电话.发信息,还可以查询天气,有时候还可以陪你聊天.如今许多汽车品牌都把自己的语音控制系统作为 ...

  8. oracle 日志 性能,Oracle日志的性能介绍及原理剖析-Oracle

    Oracle日志的性能介绍及原理剖析 一)一致性和性能 日志是所有数据库的一个很核心的内容很重要 它关系到数据库的数据的一致性 目前大家在使用的我们可看到的有几个数据库 有oracle.sqlserv ...

  9. IntelliJ IDEA 2019.3 发布,启动更快,性能更好(新特性解读)

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 原文地址:https://www.jetbrains.com/id ...

最新文章

  1. android 好看的输入法,Android 手机上最好的输入法是哪种
  2. 使用LoRa技术进行智慧城市转型
  3. Java自定义Annotation方法
  4. PDM系统服务器管理,PDM产品数据管理系统
  5. 云台山风景区,感受人生最美的风景
  6. 离散帕斯瓦尔定理、帕斯瓦尔定理的证明
  7. 有哪些常用的搜索引擎指令?
  8. 彩虹代刷网免授权+精美WAP端源码
  9. 阿里云和腾讯云服务器10M带宽能支持多少人在线?
  10. body 没有被撑开_div层为什么没有被自动撑开
  11. 从字节中取出1bit数据
  12. python游戏编程书_Python游戏编程快速上手 第4版 (斯维加特著) 中文pdf扫描版[41MB]...
  13. mac浏览器没有网络,通讯软件(QQ、微信、飞书等)正常
  14. 论文翻译-ASTER: An Attentional Scene Text Recognizer with Flexible Rectification
  15. 有限长信号自相关函数的估计
  16. 中国宠物用品品牌“Touchdog它它”完成数千万元Pre-A 轮融资
  17. AI坦克对战(实现人机)
  18. Android各厂商自启动管理开发
  19. 中断系统的简单了解以及C51(STC89C52)单片机中断系统的详解
  20. git高级特性之 - cherry-pick

热门文章

  1. 1-Wire搜索算法详解(1)
  2. 【C】杨辉三角生成式
  3. keep-alive的作用以及使用方法
  4. 数据仓库Kimball Or Inmon?
  5. STM32L0芯片FLASH编程示例及提醒
  6. JavaScript_牛客网_编程初学者入门训练(131-140题解)
  7. 【08月23日】北上资金持股比例排名
  8. 热点显示连接不上服务器,为什么手机热点连不上_手机开热点别人连不上的解决方法...
  9. 函数的使用(定义)(无参无返回值)
  10. vue 调用腾讯地图 https://apis.map.qq.com/ws/place/v1/suggestion/