PL/SQL


1. PL/SQL是什么

1.1 扩展

  1. 过程、函数、触发器用pl/sql语言编写
  2. 过程、函数、触发器存储、运行在oracle数据库上
  3. pl/sql是非常强大的数据库过程语言
  4. 过程、函数可以在java程序中调用

2. 程序结构

2.1 PL/SQL程序结构

PL/SQL程序包括:

  1. 声明部分:DECLARE和BEGIN之间的语句
  2. 执行部分:BEGIN和EXCEPTION或者BEGIN和END之间的语句
  3. 异常处理部分:EXCEPTION和END之间的语句

其中声明和异常处理部分为非必须

例如:

DECLARE
  v_AccountID NUMBER(5) := 1001;
  v_RealName VARCHAE(20);
BEGIN
  SELECT real_name INTO v_RealName
  FROM account WHERE id = v_AccountID;
EXCEPION
  WHEN NO_DATA_FOUND THEN
       INSERT INTO Fee_LOG(descr)
       VALUES('Account 1001 does not exist!');
       COMMIT;
END

2.2 Hello World

BEGIN
  dbms_output.put_line('Hello World')
END;

说明:
dbms_output – package包 包里包含过程、函数、过程
put_line(‘Hello World’)
HelloWorld执行过程

2.3 注释

3. 数据类型

PL/SQL中的数据类型分为两种:

  1. 标量类型,包括数字型、字符型、日期型、布尔型
  2. 复合类型,包括recode、associative array、nested table、varray

3.1 标量类型

3.1.1 数字类型

3.1.2 字符型

3.1.3 日期型

3.1.4 布尔型( 只能用于PL/SQL )

3.2 复合类型

3.2.1 记录(RECORD)

DECLARE
  TYPE t_rec IS RECORD  -- t_rec是记录类型
  (realname account.real_name%TYPE,  -- real_name是成员
   idcardno account.idcard_no%TYPE,
   birthdate date
  );
  v_account t_rec;  -- v_account是记录类型的变量
declare
  type t_account is record(
     real_name account.real_name%type,
     idcard_no account.idcard_no%type,
     birthdate date
  );
  v_account t_account;
  v_id account.id%type := 1011;
begin
  select real_name,idcard_no,birthdate
  into v_account
  from account
  where id = v_id;
  dbms_output.put_line(v_account.real_name);
  dbms_output.put_line(v_account.idcard_no);
  dbms_output.put_line(v_account.birthdate);
end;
DECLARE
  v_account account%ROWTYPE;
BEGIN
  insert into account values v_account;
  update account set row = v_account;
  commit;
END;

3.2.2 关联数组(Associative array)

#####声明

DECLARE
  TYPE type_name IS TABLE OF element_type [NOT NULL]
  INDEX BY [BINARY_INTEGER|PLS_INTEGER|VARCHAR2(size_limit)]

说明:

####Associative array的方法

####Associative array的异常

DECLARE
  TYPE numlist IS TABLE OF number 
  INDEX BY binary_integer;
  nums numlist; --自动初始化为null
BEGIN
  nums(null) := 3; --触发VALUE_ERROR
  IF nums(1) = 1 THEN --触发NO_DATA_FOUND
  ...
  END IF;
END;

例子1 简单用法

declare
  type t_indtab is table of number
  index by binary_integer;
  v_indtab t_indtab;
begin
  v_indtab(1) := 1;
  v_indtab(2) :=2;
  v_indtab(10) := 10;
  v_indtab(5) := 5;
  dbms_output.put_line(v_indtab(1));
  dbms_output.put_line(v_indtab(10));
  dbms_output.put_line(v_indtab(5));
  dbms_output.put_line(v_indtab(2));
  -- 返回联合数组的元素个数
  dbms_output.put_line(v_indtab.count);
  -- 返回最小下标
  dbms_output.put_line(v_indtab.first);
  -- 返回最大标
  dbms_output.put_line(v_indtab.last);
end;

例子2 遍历关联数组(for)

declare
  type t_indtab is table of number
  index by binary_integer;
  v_indtab t_indtab;
begin
  v_indtab(1) := 1;
  v_indtab(2) :=2;
  v_indtab(10) := 10;
  v_indtab(5) := 5;
  for i in v_indtab.first .. v_indtab.last loop
    if v_indtab.exists(i) then
      dbms_output.put_line(v_indtab(i));
    end if;
  end loop;
end;

例子3 遍历关联数组(while),效率大于for

declare
  type t_indtab is table of number
  index by binary_integer;
  v_indtab t_indtab;
  v_index binary_integer;
begin
  v_indtab(1) := 1;
  v_indtab(2) :=2;
  v_indtab(10) := 10;
  v_indtab(5) := 5;
  v_index := v_indtab.first;
  while v_index <= v_indtab.last loop
    dbms_output.put_line(v_indtab(v_index));
    v_index := v_indtab.next(v_index);
  end loop;
end;

案例1

declare
  cursor c_account is
    select id,real_name,idcard_no from account;
  type t_rec is record(
    real_name varchar2(20),
    idcard_no char(18)
  );
  type t_account is table of t_rec
  index by binary_integer;
  v_account t_account;
  v_rec t_rec;
  v_index account.id%type;
begin
  for i in c_account loop
    v_rec.real_name := i.real_name;
    v_rec.idcard_no := i.idcard_no;
    v_account(i.id) := v_rec;
  end loop;
  v_index := v_account.first;
  while v_index <= v_account.last loop
    dbms_output.put_line(v_index||'--'||v_account(v_index).real_name||'--'||v_account(v_
index).idcard_no);
    v_index := v_account.next(v_index);
  end loop;
end;

3.3 %TYPE方式

DECLARE v_RealName account.real_name%TYPE;
DECLARE 
  v_TempVar number(7,3) not null := 12.3;
  v_anotherVar v_tempvar%TYPE := 12.3;

3.4 %ROWTYPE

DECLARE
  v_account accout%ROWTYPE

4. 变量声明

4.1 语法

var_name type [constant] [not null] [:=vale];
DECLARE
  v_RealName varchar2(20);
  v_BaseCost number(7,2) := 5.9;
  v_Count binary_integer := 0;

5. PL/SQL流程控制语句

5.1 分支

5.1.1 if

IF boolean_expre1 THEN
... ...
ELSIF boolean_expre2 THEN
... ...
ELSE
... ...
END IF;

5.1.2 case

CASE WHEN condition THEN return_expr
    [WHEN condition THEN return_expr]
     ... ...
     ELSE else_expr
END

5.2 循环

5.2.1 LOOP

LOOP
  statement1;
  statement2;
  EXIT WHEN 
END LOOP;
</pre>

#### 5.2.2 WHILE
WHILE  LOOP
  statement1;
  statement2;
  [EXIT WHEN ]
END LOOP;
</pre>

#### 5.2.3 FOR
- 语法
FOR 循环计时器 IN 下限..上限 LOOP
  statement1;
  statement2;
END LOOP;
- 说明: - 每个循环一次,循环变量自动加1;使用关键字`REVERSE`,循环变量自动减1 - 跟在`IN REVERSE`后面的数字必须是从小到大的顺序,但不一定是整数,可以是能够转换成整数的变量或表达式 - 可以使用`EXIT`或者`EXIT WHEN`子句退出循环 ## 6. 变量 - 在匿名块或存储过程中定义的变量为局部变量,即作用域在整个匿名块或存储过程中。上述程序运行结束,该变量也不存在了。 - 绑定变量[宿主变量] - 在PL/SQL的SQL中直接使用绑定变量 :bv_name ,不需要定义 - 绑定变量的定义
SQL>variable i number
Begin
  for i in 1 .. 1000 loop
    execute immediate 
      'insert into test values(:i)' useing i;
      :i = i; -- 绑定变量的赋值
  end loop;
    dbms_output.put_line(:i);
  commit;
end;
SQL>print i;
SQL>exec :i := 10000
SQL>print i
--绑定变量
SQL>variable v_realname varchar2(20)
SQL>variable v_age number
SQL>exec nova_account_select(1011, :v_realname, :v_age);
SQL>print v_realname;
SQL>print v_age;
## 7. 异常(Exception) ### 7.1 Oracle错误 - PL/SQL错误 - 编译时错误 - 运行时错误 - 运行时的错误 - oracle错误(ORA-xxxx) - PL/SQL运行错误 - 用户定义的条件 ### 7.2 Oracle错误处理机制 - 在程序运行期间的错误对应一个异常(EXCEPTION) - 一个错误对应一个异常,当错误产生时抛出相应的异常,并被异常处理器捕获,程序控制权传递给异常处理器,由异常处理器来处理运行时错误。 ![plsql_exception_处理](/images/2013-05-18-pl-sql/05-plsql-exception.jpg 'plsql_exception_处理') ### 7.3 异常的类型 - 隐式触发 - ORACLE预定义异常 - 非ORACLE预定义异常 - 显式触发 - 用户自定义异常 ### 7.4 PL/SQL中的异常处理 #### 7.4.1 预定义异常 例子1
declare
  type t_rec_acc is record(
  real_name varchar2(20),
  idcard_no number(20)
  );
  type t_account is table of t_rec_acc
  index by binary_integer;
  v_account t_account;
begin
  select real_name,idcard_no
  bulk collect into v_account
  from account
  where 1 = 2;
  for i in v_account.first .. v_account.last loop
    dbms_output.put_line(v_account(i).real_name
                    ||'--'||v_account(i).idcard_no);
  end loop;
exception
  when value_error then
    dbms_output.put_line('v_account is empty');
end;
#### 7.4.2 非预定义异常 例子
declare
  type t_rec_acc is record(
  real_name varchar2(20),
  idcard_no number(20)
  );
  type t_account is table of t_rec_acc
  index by binary_integer;
  v_account t_account;
  e_integrity exception;
  pragma exception_init(e_integrity,-2291);
begin
  insert into nova_service_detail(id,service_id)
  values (11111,22222);
  select real_name,idcard_no
  bulk collect into v_account
  from account
  where 1 = 2;
  for i in v_account.first .. v_account.last loop
    dbms_output.put_line(v_account(i).real_name
                    ||'--'||v_account(i).idcard_no);
  end loop;
exception
  when value_error then
    dbms_output.put_line('v_account is empty');
  when e_integrity then
    dbms_output.put_line('no service');
end;
#### 7.4.3 自定义异常 例子
declare
  v_first binary_integer;
  e_notnull exception;
  v_sqlerrm varchar2(1000);
begin
  insert into nova_service_detail(id,service_id)
  values (11111,22222);
  if v_first is null then
    raise e_notnull;
  end if;
exception
  when e_notnull then
    dbms_output.put_line('v_forst is null');
  when others then
    v_sqlerrm := sqlerrm;
    dbms_output.put_line('erro message:'||v_sqlerrm);
end;
## 8. PL/SQL中的SQL DDL语句不能在PL/SQL中直接写
BEGIN
  execute immediate 'create table test(c1 number)';
END;
PL/SQL中的SQL分类 - 静态SQL 在PL/SQL块中使用的SQL语句在编译时是明确的,执行的是确定对象,即SQL语句是在PL/SQL编译阶段编译的。 - 动态SQL 在PL/SQL块编译时SQL语句是不确定的,如根据用户输入的参数的不同而执行不同的操作。编译程序动态语句部分不进行处理,只是在程序运行时动态地创建语句、对语句进行语法分析并执行该语句。 ### 8.1 静态SQL DML和TCL可以直接中PL/SQL中直接写
BEGIN
 insert into host(id) values('10.0.0.11');
 commit;
END;
### 8.2 本地动态SQL 本地动态SQL使用`EXECUTE IMMEDIATE`语句来实现。 DDL只能使用动态SQL执行。
BEGIN
  execute immediate
    'insert into host(id) values('10.0.0.11')';
  execute immediate
    'commit';
END;
## 9. PL/SQL中的SELECT 根据select语句返回的记录数,将select语句的实现分为两类: - 当且仅当返回一条记录 用 `select .. into ..` 语句实现 - 返回0条或多条记录 用 `cursor` 实现 用 `select .. bulk collect into ..`实现 ### 9.1 `select .. into ..`
DECLARE
  v_id number(10) := 1011;
  v_realname account.real_name%type;
  v_idcardno account.idcard_no%type;
BEGIN
  select real_name,idcard_no
  into v_realname,v_idcardno
  from account
  where id = v_id;
  dbms_output.put_line(v_realname);
  dbms_output.put_line(v_idcardno);
END;
### 9.2 `CURSOR` ####概念 - 根据select语句返回的记录数,若返回记录数是0条或多条用`CURSOR`实现 - Oracle使用专有SQL工作区(private sql workarea)来执行sql语句,存储处理信息。这个工作区称为`CURSOR`。 - Oracle所执行的每一个sql语句都有唯一的`CURSOR`与之相对应。 - 程序员用PL/SQL的`CURSOR`定义所需执行的select语句 ####`CURSOR`的分类 - 隐式`CURSOR` - `select .. into ..` - DML语句 - 显示`CURSOR` - 返回多条记录的select语句用显示`CURSOR`实现 ####显式CURSOR的处理 ![显示CURSOR的处理过程](/images/2013-05-18-pl-sql/02-cursor-process.jpg '显示CURSOR的处理过程') ####显式CURSOR的属性 ![显式CURSOR的属性](/images/2013-05-18-pl-sql/03-show-cursor-atts.jpg '显式CURSOR的属性') ####隐式CURSOR的属性 ![隐式CURSOR的属性](/images/2013-05-18-pl-sql/04-cursor-atts.jpg '隐式CURSOR的属性') ####CURSOR的声明 - 在游标声明中使用标准的select语句 - 如果需要按指定的次序处理行,可在查询中使用order by子句 - 可以在查询中引用变量,但必须在CURSOR语句之前声明这些变量
DECLARE
  cursor c_account_id(p_account_id number) is
    select real_name,idcard_no from account
    where id = p_account_id;
####OPEN CURSOR - 通过open cursor来执行select语句并标识结果集 - select语句如果没有返回记录,不会出现异常
open c_account(1011);
####FETCH CURSOR - 检索当前行,把值赋给变量 - 变量可以是RECORD类型或简单变量 - 如果是简单变量 - 包含相同数量的变量 - 把每一个变量和相应的列进行位置匹配 - 通过循环检测CURSOR中是否包含数据行
FETCH cursor_name INOT [variable1,variable2,...|record_name];
####结果集提取的控制 - 使用循环处理显式CURSOR结果集中的多行数据 - 每次FETCH一行,反复进行 - 使用%NOTFOUNFD属性检测一次不成功的提取操作 - 使用显式CURSOR的属性检测每一次提取是否成功,避免出现无限循环 ####CLOSE CURSOR - 处理完结果集中的数据后,应该关闭CURSOR - 如果需要,可以再次打开该CURSOR - CURSOR一旦关闭,所有和该CURSOR相关的资源都会被释放,不可再从关闭的CURSOR中提取数据 - 任何对关闭的CURSOR的操作都会引发 `INVALID_CURSOR`错误 - 每个session能打开的CURSOR数量由`OPEN_CURSOR`参数决定
close c_account;
例子1 loop 循环的写法
declare
  cursor c_account is
    select real_name,idcard_no from account;
  v_account c_account%rowtype;
begin
  open c_account;
  loop
    fetch c_account into v_account;
    exit when c_account%notfound;
    dbms_output.put_line(v_account.real_name||' '||v_account.idcard_no);
  end loop;
  close c_account;
end;
例子2 for 循环的写法
declare
  cursor c_account is
    select real_name,idcard_no from account;
begin
  for i in c_account loop
    dbms_output.put_line(i.real_name||' '||i.idcard_no);
  end loop;
end;
例子3 隐式CURSOR的处理
begin
  insert into test_cjp values (111);
  dbms_output.put_line(sql%rowcount||' rows inserted');
  delete from test_cjp where c1 <= 10;
  dbms_output.put_line(sql%rowcount||' rows deleted');
end;
说明: `sql%rowcount`中的`sql`表示离他最近的sql语句 ### 9.3 `select .. bulk collect into ...` 例子1 bulk collect
DECLARE
  type t_rec is record(
  real_name account.real_name%type,
  idcard_no account.idcard_no%type
  );
  type t_account is table of t_rec
  index by binary_integer;
  v_account t_account;
BEGIN
  select real_name,idcard_no
  bulk collect into v_account
  from account
  where 1 = 1;
  for i in v_account.first .. v_account.last loop
    dbms_output.put_line(v_account(i).real_name||'--'||v_account(i).idcard_no);
  end loop;
END;
例子2
DECLARE
  type t_rec is record(
  real_name account.real_name%type,
  age number(3)
  );
  type t_account is table of t_rec
  index by binary_integer;
  v_account t_account;
  v_age_sum number(6) := 0;
BEGIN
  select real_name,round((sysdate-birthdate)/365)
  bulk collect into v_account
  from account
  where 1 = 1;
  if v_account.count <> 0 then
    for i in v_account.first .. v_account.last loop
      v_age_sum := v_age_sum + v_account(i).age;
      dbms_output.put_line(v_account(i).real_name||'--'||v_account(i).age
                           ||'--'||v_age_sum);
    end loop;
  else
    dbms_output.put_line('v_account is empty');
  end if;
END;
## 10. 子程序 ### 10.1 有名子程序 - 命名的PL/SQL块,编译并存储在数据库中,可以在任何需要的地方调用 - 子程序的组成部分: - 子程序头 - 声明部分 - 可执行部分 - 异常处理部分(可选) ### 10.2 匿名子程序和有名子程序 - 匿名子程序 - 匿名块不存在数据库中 - 每次使用时都会进行编译 - 不能供其他块调用 - 有名子程序 - 模块化:将程序分解为逻辑模块 - 可重用性:可以被任意数目的程序调用 - 可维护性:简化维护操作 - 安全性:通过设置权限,使数据更安全 ### 10.3 有名子程序的分类 - procedure:过程 - function:函数 - package:包 - trigger:触发器 ### 10.4 过程(procedure) #### 10.4.1 什么是procedure? #### 10.4.2 语法
CREATE [OR REPLACE] PROCEDURE proc_name
[(arg_name [ {IN|OUT|IN OUT}] type, ...)]
{IS|AS}
  
BEGIN
  
[EXCEPTION
  ]
END;
</pre>

例子
--创建procedure
create or replace procedure nova_proc1
  (p_in varchar2,
   p_out out varchar2,
   p_inout in out varchar2)
is
  v_in varchar2(10);
begin
  --p_in := p_in || 'd';
  v_in := p_in || 'd';
  p_out := p_out || 'd';
  p_inout := p_inout || 'd';
end;
--调用procedure
declare
  v_out varchar2(10) := 'abc';
  v_inout varchar2(10) := 'abc';
begin
  nova_proc1('abc',v_out,v_inout);
  dbms_output.put_line(v_out);
  dbms_output.put_line(v_inout);
end;
####调用存储过程 1. 用匿名块调用
  begin
    proc_name;
  end;
2. 在SQL工作表中直接调用
  exec account_number;
####存储过程中的参数 #####形参和实参 - 在创建过程语句中的参数为形参 - 在调用过程时括号内的参数为实参 #####形参的种类 - in,参数的缺省模式 - 在调用过程的时候,实际参数的值被传递给该过程;在过程内部,形参只__可读__的。 - out - 在调用过程时,任何的实参将被忽略;在过程内部,形参是只__可写__的。 - in out - 是in与out的组合。在调用过程的时候,实参的值可以被传递给该过程;在过程内部,形参也可以被读出也可以被写入;过程结束时,形式参数的内容将赋给调用时的实际参数。 #####对实际参数的要求 - 模式为IN的形参对应的实际参数可以是常量或变量 - 模式为IN OUT或OUT的形参对应的实际参数必须是变量,用于存储返回的值,所以不能是常量或表达式。 #####形参的限制 - 在调用过程当中,实际参数在将值传递给过程时,也传递了对变量的限制 - 形式参数不能声明长度,但可使用%TYPE来进行限制 #####带参数的过程调用 - 位置表示法 - 调用时添入所有参数,实参与形参按顺序一一对应 - 名字表示法 - 调用时给出形参名字,并给出实参 - procname(12,p_outparm=>v_var1,p_inout=>10); - 两种方法可以混用 - 混用时,第一个参数必须通过位置来指定 - 名字表示对于参数很多时,可提高程序的可读性 #####使用缺省参数 - 形参可以指明缺省值 parm_name [mode] type {:=|default} init_type - 位置标示法时,所有的缺省值都放在最后面 - 使用名字标示法则无所谓 - 声明时,如果有缺省值,尽量将缺省值放在参数表的末尾 ####调用过程所需的权限 - 在过程中执行DDL操作,所需的权限必须通过直接授予的方式,不能通过角色授予。 - 调用过程时,所有角色都是disable的,即角色中包含的所有权限都不能生效。 - 调用其他用户的过程,必须由过程的属主授予执行权限 - grant execute on procname to username; > 匿名块的编译和执行在一起 > 有名块的编译和执行分开,role在两个阶段可以状态不一致(enable disable). > oracle解决办法,所有通过角色授予的系统权限不能用。在存储过程中需要的系统权限,必须通过直接授权的方式。 ### 10.5 函数(FUNCTION) ####什么是function? ####语法
create [or replace] function fun_name
[(arg_name [{in|out|in out}] type,...)]
return type
is
  
begin
  
  return value;
[exception
  ]
end;
</pre>

####例子
--声明函数
create or replace
function fun1
(p_in number,p_out out number)
return number
is 
begin 
     p_out :=2;
     return p_in;
end;
---------------------------------------------------
--调用函数
declare
  v_in number := 123;
  v_out number(5);
  v_result number(5);
begin
  v_result := fun1(v_in,v_out);
  dbms_output.put_line('v_out  '||v_out);
  dbms_output.put_line('v_result  '||v_result);
end;
### 10.6 包(package) ####什么是package - package是一个可以将相关对象存储在一起的PL/SQL结构。package包含了两个分离的组成部分:specification(package的声明,包声明)和body(声明中的程序实体,包体)。每个部分都单独被存储在数据字典中。包声明是一个操作接口,对应用来说是可见的; - 包体是黑盒,对应用来说隐藏列实现细节。 ####包的组成 - 将相关的若干程序单元组织到一块,用一个包来标识这个集合,包可以包含以下的程序单元: ![package可包含的内容](images/2013-05-18-pl-sql/06-package-content.jpg 'package可包含的内容') ####声明语法
create or replace package pkg_name
{is|as}
公共变量(variable)的定义|
公共类型(type)的定义|
公共异常(exception)的定义|
公共游标(cursor)的定义|
函数说明 |
过程说明
end;
####包体语法
--创建包声明
create or replace package body pkg_name
is
  ...
end;
例子
--创建包声明
create or replace package n_pkg1
is
  type t_rec is record
  (m1 number,
   m2 varchar2(10)
  );
  v_rec t_rec;
  procedure proc1;
  function fun1(p_in number) return number;
end;
--------------------------------------------
--创建包体
create or replace package body n_pkg1
is
  procedure proc1
  is
  begin
    dbms_output.put_line(v_rec.m1);
  end;
  function fun1(p_in number) return number
  is
  begin
    return p_in;
  end;
end;
--------------------------------------------
--调用
begin
  n_pkg1.v_rec.m1 := n_pkg1.fun1(10);
  n_pkg1.proc1;
####包中变量的作用域 sessioin ####包的优点 - 方便对存储过程和函数的组织 - 将相关的过程和函数组织在一起 - 在一个用户环境中解决命名的冲突问题 - 方便对存储过程和函数的管理 - 在不改变包的声明定义是可以改变包体的实现 - 限制过程、函数的依赖性 - 方便对存储过程和函数的安全性管理 - 整个包的访问权限只需一次性授权 - 区分公用过程和私有过程 - 改善性能 - 在包被首次调用时作为一个整体全部调入内存 - 减少多次调用时的磁盘I/O次数 ### 10.7 触发器(trigger) ####DML行级触发器
create [or replace] trigger trig_name
{before|after} insert [or update ...]
on tab_name
for each row [when restricting_condition]
PL/SQL block;
####:old和:new 在行级触发器中,在列名前加上:old标识符表示该列变化前的值,加上:new标识符表示变化后的值 ## 11. SQL的处理过程 ### 11.1 过程 - 语法检查(syntax check) - 检查此sql的拼写是否符合语法 - 语义检查(semantic check) - 诸如检查sql语句中的访问对象是否存在及该用户是否具备相应的权限 - 对sql语句进行解析(parse) - 利用内部算法对sql进行解析,生成解析树(parse tree)及执行计划(execution plan) - 执行sql,返回结果(execute and return) ### 11.2 软分析和硬分析 Oracle利用内部的hash算法来取得该sql的hash值,然后在library cache里查找是否存在该hash值 - 假设存在,则将此sql与cache中的进行比较;假设“相同”,就将利用已有的解析树与执行计划,而省略列优化器的相关工在。这就是软分析的过程。 - 如果上面两个假设中任有一个不成立,那么优化器都将进行创建解析树、生成执行计划的动作。这个过程叫硬分析。 - 分析语句 软分析、硬分析 - 绑定变量 - 赋值 - 执行语句 - 获取数据 ### 11.2 静态SQL/动态SQL的性能分析 - 静态SQL - 动态SQL 例子:
--静态SQL
create or replace procedure proc1
is
begin
  for i in 1 .. 1000 loop
    insert into test values(i);
  end loop;
  commit;
end; --编译存储过程时编译SQL语句
begin proc1 end;--1次硬分析、1次软分析、1000次执行
-------------------------------------------
--未使用绑定变量
create or replace procedure proc1
is
begin
  for i in 1 .. 1000 loop
    execute immediate 
      'insert into test values('||i||')';
  end loop;
  commit;
end;--编译存储过程时不编译SQL语句
begin proc1 end;--1000次硬分析、1000次软分析、1000次执行
-------------------------------------------
--使用绑定变量
create or replace procedure proc1
is
begin
  for i in 1 .. 1000 loop
    execute immediate 
      'insert into test values(:i)' using i;
  end loop;
  commit;
end;--编译存储过程时不编译SQL语句

begin proc1 end;--1次硬分析、1000次软分析、1000次执行
####小节 - 尽量使用静态SQL - 动态SQL中要使用绑定变量 ## 12. 附录 ####设置输出打开
set serveroutput on;
###显示创建对象时的错误信息
sql>show error
###查procedure
select object_name,object_type,status
from user_objects
where object_name='NOVA_ACCOUNT_NUMBER'
标题:PL/SQL
作者: Nova Woo 2013-05-18 
出处: http://novawoo.github.io/pl-sql
说明:转载、投稿、翻译类文章版权信息以正文标注为准
分类: Oracle & PL/SQL    标签:
 
 

无觅相关文章插件,快速提升流量
友荐云推荐