重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
游标操作的优化:
创新互联建站主要从事成都网站建设、网站制作、网页设计、企业做网站、公司建网站等业务。立足成都服务涿鹿,十年网站建设经验,价格优惠、服务专业,欢迎来电咨询建站服务:18980820575
-- 有一个表格,存储的是用户的名字,将 emp 表中所有的用户名转换成小写,再写入这个表格
create table ename_emp(
ename varchar2(50)
);
-- 下面这个游标的操作,需要 43s时间才能完成
declare
begin
for i in (select ename from emp_liebiao) loop
insert into ename_emp values(lower(i.ename));
commit;
end loop;
end;
-- 因为游标是以行为单位进行数据的操作的,所有游标的效率是比较慢的,而且游标需要消耗的内存也是比较多的,我们需要将游标以行进行操作的方式,修改成一次性操作所有数据的批量的方式。
批量操作在游标里面 叫做 bulk collect
第一步,创建一个表类型
type 表类型的名字 is table of 表名.列名 %type;
变量名 表类型的名字;
第三步,创建一个游标,读取某个查询的结果
cursor 游标名字 is select 查询语句 ;
第四步,打开游标
open 游标名字;
第五步,捕获游标的数据,将内容给到表类型的变量进行保存
fetch 游标名字 bulk collect into 变量名字 ;
第六步,使用 forall 语句,对数据进行批量的操作
forall i in 变量 .first .. 变量 .last DML 语句操作 ;
第七步,关闭游标
close 游标名字 ;
declare
-- 创建表类型
type biao is table of emp_liebiao.ename%type;
b biao;
-- 创建游标
cursor m is select ename from emp_liebiao;
begin
-- 打开游标
open m;
-- 将游标的内容批量的给到变量
fetch m bulk collect into b;
-- 使用forall批量修改数据
forall i in b.first .. b.last insert into ename_emp values(lower(b(i)));
commit;
-- 关闭游标
close m;
end;
练习:批量修改用户的编号,让编号+工资等级+部门,形成一个新的编号,存入下面的表格中,使用 bulk collect 来实现。
2000以下是 C ,2000-3000是 B ,3000以上是 A ,
例如 SMITH , 新编号应该是 7369_C_20
create table empno_emp(
empno varchar2(50)
);
declare
type biao is table of emp_liebiao%rowtype;
b biao;
cursor m is select * from emp_liebiao;
begin
open m;
fetch m bulk collect into b;
forall i in b.first..b.last
insert into empno_emp values(
b(i).empno||'_'||decode(sign(b(i).sal-2000)+sign(b(i).sal-3000),-2,'C',2,'A','B')||'_'||b(i).deptno
);
commit;
close m;
end;
--创建存储
程
CREATE
OR
REPLACE
PROCEDURE
xxxxxxxxxxx_p
(
--参数IN表示输入参数
OUT表示输入参数
类型
使用任意Oracle
合
类型
is_ym
IN
CHAR
)
AS
--定义变量
vs_msg
VARCHAR2(4000);
--错误信息变量
vs_ym_beg
CHAR(6);
--起始月份
vs_ym_end
CHAR(6);
--终止月份
vs_ym_sn_beg
CHAR(6);
--同期起始月份
vs_ym_sn_end
CHAR(6);
--同期终止月份
--定义游标(简单
说
遍历
结
集)
CURSOR
cur_1
IS
SELECT
area_code,CMCODE,SUM(rmb_amt)/10000
rmb_amt_sn,SUM(usd_amt)/10000
usd_amt_sn
FROM
BGD_AREA_CM_M_BASE_T
WHERE
ym
=
vs_ym_sn_beg
AND
ym
=
vs_ym_sn_end
GROUP
BY
area_code,CMCODE;BEGIN
--用输入参数给变量赋初值
用
Oralce
SUBSTR
TO_CHAR
ADD_MONTHS
TO_DATE
等
用
函数
vs_ym_beg
:=
SUBSTR(is_ym,1,6);
vs_ym_end
:=
SUBSTR(is_ym,7,6);
vs_ym_sn_beg
:=
TO_CHAR(ADD_MONTHS(TO_DATE(vs_ym_beg,'yyyymm'),
-12),'yyyymm');
vs_ym_sn_end
:=
TO_CHAR(ADD_MONTHS(TO_DATE(vs_ym_end,'yyyymm'),
-12),'yyyymm');
--先删除表
特定条件
数据
DELETE
FROM
xxxxxxxxxxx_T
WHERE
ym
=
is_ym;
--
用内置
DBMS_OUTPUT
象
put_line
打印
影响
记录行数
其
用
系统变量SQL%rowcount
DBMS_OUTPUT.put_line('del
月记录='||SQL%rowcount||'条');
INSERT
INTO
xxxxxxxxxxx_T(area_code,ym,CMCODE,rmb_amt,usd_amt)
SELECT
area_code,is_ym,CMCODE,SUM(rmb_amt)/10000,SUM(usd_amt)/10000
FROM
BGD_AREA_CM_M_BASE_T
WHERE
ym
=
vs_ym_beg
AND
ym
=
vs_ym_end
GROUP
BY
area_code,CMCODE;
DBMS_OUTPUT.put_line('ins
月记录='||SQL%rowcount||'条');
--遍历游标处理
更新
表
遍历游标
几种
用for语句
其
比较直观
种
FOR
rec
IN
cur_1
LOOP
UPDATE
xxxxxxxxxxx_T
SET
rmb_amt_sn
=
rec.rmb_amt_sn,usd_amt_sn
=
rec.usd_amt_sn
WHERE
area_code
=
rec.area_code
AND
CMCODE
=
rec.CMCODE
AND
ym
=
is_ym;
END
LOOP;
COMMIT;
--错误处理部
OTHERS表示除
声明外
任意错误
SQLERRM
系统内置变量保存
前错误
详细信息
EXCEPTION
WHEN
OTHERS
THEN
vs_msg
:=
'ERROR
IN
xxxxxxxxxxx_p('||is_ym||'):'||SUBSTR(SQLERRM,1,500);
ROLLBACK;
--
前错误记录进
志表
INSERT
INTO
LOG_INFO(proc_name,error_info,op_date)
VALUES('xxxxxxxxxxx_p',vs_msg,SYSDATE);
COMMIT;
RETURN;
END;
1.游标定义:
cursor XXXA is
SELECT 语句;
XXXB cursorName%rowtype;
XXXA: 游标名
XXXB: 游标行数据定义
2. 打开游标:
-- 打开之前最好先关一下,防止上次发生异常没有关掉而引发不必要的异常
IF XXXA%ISOPEN THEN
CLOSE XXXA;
END IF;
Open XXXA ;
Loop
Fetch XXXA into XXXB;
exit when XXXA%NOTFOUND;
... ... 处理逻辑
end loop;
close XXXA;
Oracle中的游标分为显示游标和隐式游标。
显示游标:
显示游标是用cursor...is命令定义的游标,它可以对查询语句(select)返回的多条记录进行处理;显示游标的操作:打开游标、操作游标、关闭游标;
隐式游标:
隐式游标是在执行插入(insert)、删除(delete)、修改(update)和返回单条记录的查询(select)语句时由PL/SQL自动定义的。PL/SQL隐式地打开SQL游标,并在它内部处理SQL语句,然后关闭它。
游标能够根据查询条件从数据表中提取一组记录,将其作为一个临时表置于数据缓冲区中,利用指针逐行对记录数据进行操作。
Oracle中的游标分为显示游标和隐式游标 。
在执行SQL语句时,Oracle会自动创建隐式游标,该游标是内存中处理该语句的数据缓冲区,存储了执行SQL语句的结果。通过隐式游标属性可获知SQL语句的执行状态信息。
%found:布尔型属性,如果sql语句至少影响到一行数据,值为true,否则为false。
%notfound:布尔型属性,与%found相反。
%rowcount:数字型属性,返回受sql影响的行数。
%isopen:布尔型属性,当游标已经打开时返回true,游标关闭时则为false。
用户可以显式定义游标。使用显式游标处理数据要4个步骤:定义游标、打开游标、提取游标数据和关闭游标。
游标由游标名称和游标对应的select结果集组成。定义游标应该放在pl/sql程序块的声明部分。
语法格式:cursor 游标名称(参数) is 查询语句
打开游标时,游标会将符合条件的记录送入数据缓冲区,并将指针指向第一条记录。
语法格式:open 游标名称(参数);
将游标中的当前行数据赋给指定的变量或记录变量。
语法格式:fetch 游标名称 into 变量名;
游标一旦使用完毕,就应将其关闭,释放与游标相关联的资源。
语法格式:close 游标名称;
declare
cursor c1 is select sno,cno,grade from sc;
v_sno sc.sno%type;
v_cno sc.cno%type;
v_grade sc.grade%type;
begin
open c1;
loop
fetch c1 into v_sno,v_cno,v_grade;
exit when c1%notfound;--紧跟fetch之后
if c1%found then
dbms_output.put_line(to_char(c1%rowcount)||v_cno);
end if;
end loop;
close c1;
end;
declare
cursor c1 is select sno,cno,grade from sc;
v_sno sc.sno%type;
v_cno sc.cno%type;
v_grade sc.grade%type;
begin
open c1;
fetch c1 into v_sno,v_cno,v_grade;
while c1%found loop
dbms_output.put_line(v_sno||v_cno||v_grade);
fetch c1 into v_sno,v_cno,v_grade;
end loop;
close c1;
end;
第三种:for
declare
cursor c1 is select sno,cno,grade from sc;
begin
for item in c1 loop
dbms_output.put_line(rpad(item.sno,'10',' ')||rpad(item.cno,'10',' ')||rpad(item.grade,'10',' '));
end loop;
end;
你尝试一下, 使用 函数 来处理, 应该就可以避免掉 存储过程参数没法写的问题。
创建返回结果集的函数
SQL create or replace package pkg_HelloWorld as
2 -- 定义ref cursor类型
3 type myrctype is ref cursor;
4 --函数申明
5 function getHelloWorld return myrctype;
6 end pkg_HelloWorld;
7 /
程序包已创建。
SQL CREATE OR REPLACE package body pkg_HelloWorld as
2 function getHelloWorld return myrctype
3 IS
4 return_cursor myrctype;
5 BEGIN
6 OPEN return_cursor FOR
7 SELECT 'Hello 1' AS a, 'World 1' AS B FROM dual
8 UNION ALL
9 SELECT 'Hello 2' AS a, 'World 2' AS B FROM dual;
10 return return_cursor;
11 END getHelloWorld;
12 end pkg_HelloWorld;
13 /
程序包体已创建。
注:Oracle 这里的函数,是一个返回游标类型的函数, 不是像 SQL Server 的那种叫 “表值函数” 的东西。
因此下面的写法会报错。
SQL SELECT * FROM pkg_HelloWorld.getHelloWorld();
SELECT * FROM pkg_HelloWorld.getHelloWorld()
*
第 1 行出现错误:
ORA-00933: SQL 命令未正确结束
SQL SELECT pkg_HelloWorld.getHelloWorld() FROM dual;
PKG_HELLOWORLD.GETHE
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
A B
------- -------
Hello 1 World 1
Hello 2 World 2
C# 如何调用上面的 返回结果集的例子:
/// summary
/// 测试 调用 Oracle 返回结果集的函数.
/// /summary
private void CallFuncWithTable(OracleConnection conn)
{
// 创建一个 Command.
OracleCommand testCommand = conn.CreateCommand();
// 定义需要执行的SQL语句. testCommand.CommandText = "pkg_HelloWorld.getHelloWorld";
// 定义好,本次执行的类型,是存储过程. testCommand.CommandType = CommandType.StoredProcedure;
// 定义好,我这个参数,是 游标 + 返回值.
OracleParameter para = new OracleParameter("c", OracleType.Cursor);
para.Direction = ParameterDirection.ReturnValue;
testCommand.Parameters.Add(para);
// 执行SQL命令,结果存储到Reader中.
OracleDataReader testReader = testCommand.ExecuteReader();
// 处理检索出来的每一条数据.
while (testReader.Read())
{
// 将检索出来的数据,输出到屏幕上.
Console.WriteLine("调用函数:{0}; 返回:{1} - {2}",
testCommand.CommandText, testReader[0], testReader[1]
);
}
// 关闭Reader.
testReader.Close();
}