PL/SQL 训练05--游标
--隐式游标
--通过一个简单的SELECT ...INTO 语句提取一行数据,并放在一个局部变量中,最简单获取数据的途径
--显示游标
--可以在声明单元明确的声明一个查询,这样可以在一个或多个程序中打开游标并且提取数据
--游标变量
--使用游标变量,可以把指向一个查询结果集的指针从一个程序传递到另一个程序,灵活度较高
--任何程序只要能访问这个变量,就可以打开这个游标,提取数据,关闭游标
--游标表达式
--CURSOR表达式可以把一个SELECT 语句转换成REF CURSOR结果集,可以用于
--表函数中提升应用程序的性能
--游标基础
--最简单的游标,可以当成一个指针,指针指向一个表或者多个表查询得到的记录集。比如
cursor cur_users is select * from ma_users ;
--声明了一个游标,可以打开游标
OPEN cur_users ;
--从游标中提取数据
fetch cur_users into user_rec;
--最后关闭游标
close cur_users;
--术语
--静态SQL:当一个SQL语句所在的代码块被编译时,这个语句就完全指定的或者是固定的。
--动态SQL:如果一个SQL语句直到运行时刻才被构造出来并执行,这个语句就是动态SQL语句
--结果集: sql语句所请求的行集的和,结果集中会被缓存在SGA以提升访问数据和修改数据的性能
--隐式游标:每一个SQL DML语句或者SELECT INTO PL/SQL都会声明和使用一个隐式游标
--显示游标:明确的把一个SELECT语句声明成一个游标
--游标变量:指向或者引用游标对象的变量
--游标属性:游标名或者游标变量%属性名
--SELECT FOR UPDATE:标准select的一个特殊变体,这个语句对查询出来的每一行数据预先加上一个行级锁
--批量处理 BULK COLLECT
--典型的查询操作
--解析:SQL语句处理过程的第一步分析,以确保这个语句是有效的并生成执行计划
--绑定:程序中的值替换SQL语句的占位符
--打开:当打开一个游标时,SQL语句的结果集就根据设置的绑定的值确定下来了,
-- 指向活跃行或者当前行的指针就被设置到第一行
--执行:在执行阶段,语句被SQL引擎执行
--提取:如果我们执行的是一个查询语句,FETCH命令会从结果集中取出下一行记录。
--- 每执行一次fetch操作,PL/SQL就把记录集的指针向前移动一下
--关闭 :关闭游标这一步会释放游标使用的全部内存资源,一旦游标关闭后,游标就不再有结果集了
--游标属性
--%found
--反映的是游标最后一次数据提取操作的操作状态。如果游标最后一次提取返回了一行,这个属性结果就是TRUE
--如果没有取到任何行,就返回FALSE
declare
cursor cur_users is
select * from ma_users;
begin --如果没有打开游标就使用这个属性,就会报invalid_cursor异常
/* if cur_users%found then
dbms_output.put_line('更新积分');
end if; */ for v in cur_users loop
update ma_users t
set t.user_point = 1000
where t.id_ma_users = v.id_ma_users;
if cur_users%found then
dbms_output.put_line(v.user_name || '更新了积分');
end if;
end loop;
end;
/
--%notfound
--跟%found属性相反,如果游标的最后一次提取数据操作没有取到任何记录,这个属性就返回TRUE
declare
cursor cur_users is
select * from ma_users; user_rec ma_users%rowtype;
begin --如果没有打开游标就使用这个属性,就会报invalid_cursor异常
/* if cur_users%notfound then
dbms_output.put_line('更新积分');
end if; */
open cur_users;
loop
fetch cur_users
into user_rec;
exit when cur_users%notfound;
update ma_users t
set t.user_point = 1000
where t.id_ma_users = user_rec.id_ma_users;
end loop;
close cur_users; open cur_users;
loop
fetch cur_users
into user_rec;
exit when not cur_users%found;
dbms_output.put_line(user_rec.user_name || '积分值是' ||
user_rec.user_point);
end loop;
close cur_users; end;
/ --%rowcount
--返回的是,在请求这个属性的那个时刻,已经从这个游标中取出的记录数量
begin update ma_users r
set r.user_point = r.user_point+100
where r.user_name ='test001';
dbms_output.put_line(sql%rowcount); end ;
/ declare
cursor cur_users is
select * from ma_users; user_rec ma_users%rowtype;
begin open cur_users;
loop
fetch cur_users
into user_rec;
exit when not cur_users%found;
dbms_output.put_line(cur_users%rowcount);
end loop;
dbms_output.put_line(cur_users%rowcount);
close cur_users;
-- dbms_output.put_line(cur_users%rowcount); end;
/ --%isopen
--如果一个游标已经打开,返回TURE,否则返回FALSE declare
cursor cur_users is
select * from ma_users; user_rec ma_users%rowtype;
begin begin open cur_users;
loop
fetch cur_users
into user_rec;
exit when not cur_users%found;
update ma_users t
set t.user_point = 1000
where t.id_ma_users = user_rec.id_ma_users;
end loop;
close cur_users;
EXCEPTION
WHEN OTHERS THEN
IF cur_users%ISOPEN THEN
close cur_users;
END IF; end; end;
/
--%bulk_rowcount
--%bulk_exceptions
--这两个属性是在FORALL语句中使用的,后续再介绍PL/SQL性能优化的时候再介绍
--注意以上这些属性不能使用在SQL语句中
--隐式游标OR显示游标?
--使用隐式游标
--前面已提及dml语句或者SELECT INTO 语句,PL/SQL都会声明一个隐式游标并管理这个游标
--数据库隐式或自动的执行了许多和游标相关的操作。如打开游标,提取数据等
--select语句的隐式游标
--SELECT 语句出现在代码块的执行单元,而不是在声明单元
--查询带有一个INTO语句,into语句是PL/SQL语言的一部分,这种用于把数据库的数据传送到本地PL/SQL数据结构中
--不用打开、提取或者关闭SELECT语句,这些操作由数据库完成
--隐式查询的结构 SELECT column_list [bulk collect] into PL/SQL variable_list from.. .where... declare
v_phone ma_users.user_phone%type;
begin select r.user_phone
into v_phone
from ma_users r
where r.user_name = 'test001';
dbms_output.put_line(v_phone);
dbms_output.put_line(sql%rowcount);
end;
/
--两种异常
-- no_data_found :什么都没有
-- too_many_rows :太多了 declare
v_phone ma_users.user_phone%type;
begin select r.user_phone
into v_phone
from ma_users r
where r.user_name = 'test0001';
dbms_output.put_line(v_phone);
exception
when no_data_found then
dbms_output.put_line(sql%rowcount);
end;
/
declare
v_phone ma_users.user_phone%type;
begin select r.user_phone
into v_phone
from ma_users r
where r.real_name = 'xinyuan';
dbms_output.put_line(v_phone);
exception
when no_data_found then
dbms_output.put_line(sql%rowcount);
when too_many_rows then
dbms_output.put_line(sql%rowcount);
end;
/
--sql%rowcount
--sql%found
--sql%notfound
--sql%isopen
--如果会话还没有执行过隐式游标,所有的隐式游标属性都会返回NULL
--否则返回的是最后一次执行SQL语句的属性值,而不管这个SQL语句在哪个代码块或程序中执行的
declare
v_phone ma_users.user_phone%type;
begin
select r.user_phone
into v_phone
from ma_users r
where r.user_name = 'test001';
dbms_output.put_line(sql%rowcount);
if sql%found then
dbms_output.put_line('found');
end if;
if sql%notfound then
dbms_output.put_line('notfound');
end if;
if sql%isopen then
dbms_output.put_line('isopen');
end if;
end;
/
--使用显示游标 --显示游标就是在声明单元明确定义的SELECT语句,并同时指定了一个名字
--dml语句没有显示游标一说
--声明显示游标 CURSOR CURSOR_NAME [(PARAM1,PARAM2)]
[RETURN return_specificaiton] --return子句
is select_statement [for update [of [column_list]]]; --不带有参数的游标
CURSOR CUR_USERS IS
SELECT * FROM MA_USERS ; --带有参数的游标
CURSOR CUR_USERS(I_USER_NAME IN VARCHAR2) IS
SELECT * FROM MA_USERS WHERE USER_NAME = I_USER_NAME; --带有RETURN语句的游标,游标的结构和底层表结构相同
CURSOR CUR_USERS RETURN MA_USERS%ROWTYPE IS
SELECT * FROM MA_USERS T WHERE T.REAL_NAME ='xinyuan'; --游标的名字非PL/SQL变量,不能赋值 --可以在包中声明显示游标
create or replace package user_info is
CURSOR CUR_USERS IS
SELECT * FROM MA_USERS; CURSOR CUR_USERS_P(I_USER_NAME IN VARCHAR2) return ma_users%rowtype IS
SELECT * FROM MA_USERS WHERE USER_NAME = I_USER_NAME;
end user_info; begin for v in user_info.CUR_USERS loop
dbms_output.put_line(v.user_name);
end loop; for v in user_info.CUR_USERS_P('test001') loop
dbms_output.put_line(v.user_phone);
end loop; end;
/
--return子句 类型
--基于某个数据库表定义的记录类型,使用ROWTYPE属性定义的
--基于另外一个之前定义好的游标定义的记录类型,使用%ROWTYPE定义的
--自定义的记录类型 --为什么要在包中定义游标呢?
create or replace package user_info_test is CURSOR CUR_USERS_P(I_USER_NAME IN VARCHAR2) return ma_users%rowtype;
end user_info_test;
/
create or replace package body user_info_test is CURSOR CUR_USERS_P(I_USER_NAME IN VARCHAR2) return ma_users%rowtype IS
SELECT * FROM MA_USERS WHERE USER_NAME = I_USER_NAME;
end user_info_test;
/
--隐藏信息
--最小化重编译 --打开显示游标 OPEN CURSOR_NAME[(PARAM1,PARAM2)]; --打开游标时,PLSQL会执行这个游标的查询语句,同时标识出活跃数据集,即符合where语句条件和连接
--条件的数据行,但不会提取任何一行数据 --无论什么情况下取数,ORACLE的读一致性模型会保证取数操作反映的是游标打开那一刻的数据状态 --不能打开一个已经打开的游标
--打开之前可以使用一个%isopen来判断 declare CURSOR CUR_USERS IS
SELECT * FROM MA_USERS;
user_rec cur_users%rowtype;
begin
if not CUR_USERS%isopen then
open cur_users;
end if;
loop
fetch cur_users
into user_rec;
exit when not cur_users%found;
dbms_output.put_line(user_rec.user_name);
end loop;
--open cur_users;
close cur_users; end;
/
--从显示游标中提取数据 fetch cursor_name into record_or_variable_list; declare CURSOR CUR_USERS IS
SELECT user_phone FROM MA_USERS; CURSOR CUR_USERS1 IS
SELECT user_name,user_phone FROM MA_USERS;
user_rec cur_users%rowtype;
v_phone ma_users.user_phone%type;
v_user_name ma_users.user_name%type;
begin
open cur_users;
loop
fetch cur_users
into user_rec;
exit when not cur_users%found;
dbms_output.put_line(user_rec.user_phone);
end loop;
close cur_users;
dbms_output.put_line('----分割线---');
open cur_users;
loop
fetch cur_users
into v_phone;
exit when not cur_users%found;
dbms_output.put_line(v_phone);
end loop;
close cur_users; dbms_output.put_line('----分割线---');
open cur_users1;
loop
fetch cur_users1
into v_user_name,v_phone;
exit when not cur_users1%found;
dbms_output.put_line(v_user_name||'手机号'||v_phone);
end loop;
close cur_users1; end;
/ --游标中可以使用列的别名
declare CURSOR CUR_USERS IS
SELECT t.user_name, sum(t.user_point) points
FROM MA_USERS t
group by t.user_name;
user_rec cur_users%rowtype;
begin
if not CUR_USERS%isopen then
open cur_users;
end if;
loop
fetch cur_users
into user_rec;
exit when not cur_users%found;
dbms_output.put_line(user_rec.user_name || user_rec.points);
end loop;
--open cur_users;
close cur_users; end;
/ --关闭游标 close cursor_name ; --如果打开了一个游标,要确保在程序结束时关闭这个游标,否则就会出现内存泄漏 --函数、块或过程块,结尾自动检测打开的游标,并隐式关闭。开销不少 ---包级别的游标,如果打开了,除非显示的关闭,否则一直保持打开直到会话结束 --只能关闭一个已经打开的游标,否则就会报INVALID_CURSOR declare CURSOR CUR_USERS IS
SELECT * FROM MA_USERS;
user_rec cur_users%rowtype;
EXP_TMP EXCEPTION;
begin open cur_users;
BEGIN
loop
fetch cur_users
into user_rec;
exit when not cur_users%found;
dbms_output.put_line(user_rec.user_name);
RAISE EXP_TMP;
end loop;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
fetch cur_users into user_rec;
dbms_output.put_line(user_rec.user_name);
/*FOR V IN CUR_USERS LOOP
dbms_output.put_line(V.user_name);
END LOOP;*/ end;
/
--显示游标属性
--%found
--%notfound
--%rowcount
--%isopen declare v_user_rec user_info.CUR_USERS%rowtype;
begin if user_info.CUR_USERS%isopen then
close user_info.CUR_USERS;
end if;
open user_info.CUR_USERS;
loop
fetch user_info.CUR_USERS
into v_user_rec;
exit when user_info.CUR_USERS%notfound or user_info.CUR_USERS%rowcount > 4;
dbms_output.put_line(v_user_rec.user_name);
end loop;
close user_info.CUR_USERS; end;
/
--游标参数化
--为什么要使用游标参数化?
--重用
--避免作用域的问题
--什么时候使用游标参数化?想想函数
--如果要在多个地方使用一个游标,每次只是WHERE语句中值不同,则建议创建一个带参数的游标 declare
v_user_info user_info.CUR_USERS_P%rowtype;
begin
open user_info.CUR_USERS_P('test001');
loop
fetch user_info.CUR_USERS_P
into v_user_info;
exit when user_info.CUR_USERS_P%notfound;
dbms_output.put_line(v_user_info.user_phone);
end loop;
close user_info.CUR_USERS_P;
end;
/
--游标的参数作用范围仅限于游标,不能在游标关联的SELECT语句之外引用游标参数
declare CURSOR CUR_USERS_P(I_USER_NAME IN VARCHAR2) IS
SELECT * FROM MA_USERS WHERE USER_NAME = I_USER_NAME; begin
I_USER_NAME := 'test';
open CUR_USERS_P(I_USER_NAME); end;
/ --游标参数的模式,只能是一个IN,不能使用OUT或者in out --游标参数可以有缺省值;比如
declare CURSOR CUR_USERS_P(I_USER_NAME IN VARCHAR2 := 'test001')
IS SELECT * FROM MA_USERS WHERE USER_NAME = I_USER_NAME;
v_user CUR_USERS_P%rowtype;
begin
open CUR_USERS_P('幸运小子1');
loop
fetch CUR_USERS_P
into v_user;
exit when CUR_USERS_P%notfound;
dbms_output.put_line(v_user.user_phone);
end loop;
close CUR_USERS_P;
end;
/ --select ... for update
--单纯使用SELECT从数据库查询数据,数据库不会对选择的行添加任何锁
--有时后,我们希望能在程序修改记录之前就锁住它们,可以使用
SELECT * from ma_users FOR UPDATE ; declare CURSOR CUR_USERS_P(I_USER_NAME IN VARCHAR2) IS
SELECT * FROM MA_USERS WHERE USER_NAME = I_USER_NAME FOR UPDATE;
v_user CUR_USERS_P%rowtype;
begin
open CUR_USERS_P('test001');
loop
fetch CUR_USERS_P
into v_user;
exit when CUR_USERS_P%notfound;
dbms_output.put_line(v_user.user_phone);
end loop;
close CUR_USERS_P;
end;
/
declare CURSOR CUR_USERS_P(I_USER_NAME IN VARCHAR2) IS
SELECT t.USER_PHONE, tt.trace_mark
FROM MA_USERS t, ma_user_log tt
WHERE t.USER_NAME = I_USER_NAME
and t.user_name = tt.user_name
FOR UPDATE OF t.USER_PHONE; --指定锁住某列
v_user CUR_USERS_P%rowtype;
begin
open CUR_USERS_P('乱世佳人');
loop
fetch CUR_USERS_P
into v_user;
exit when CUR_USERS_P%notfound;
dbms_output.put_line(v_user.user_phone || v_user.trace_mark);
end loop;
close CUR_USERS_P;
end;
/
--当遭遇COMMIT,或rollback,结果集所有行的锁就会被释放
--但也不能用FETCH从FOR UPDATE的游标中提取数据了 declare CURSOR CUR_USERS IS
SELECT * FROM MA_USERS for update;
user_rec cur_users%rowtype;
begin FOR V IN CUR_USERS LOOP
dbms_output.put_line(V.user_name);
commit;--ORA-01002: 提取违反顺序
END LOOP; end;
/
--使用 WHERE CURRENT OF ,代码冗余最小化
declare CURSOR CUR_USERS IS
SELECT * FROM MA_USERS for update;
user_rec cur_users%rowtype;
begin
FOR V IN CUR_USERS LOOP
dbms_output.put_line(V.USER_NAME || V.USER_POINT);
UPDATE MA_USERS T SET T.USER_POINT = 1000 WHERE CURRENT OF CUR_USERS;
END LOOP; end;
/
--游标变量和REF CURSOR
--游标变量是一个指向或者引用底层游标的变量
--游标变量可以用于打开任何一个查询,甚至在一个程序中执行多个不同的查询 --声明游标变量
--创建一个引用游标类型
--基于这个类型声明真正的游标变量
TYPE cursor_type_name is ref cursor [return return_type];--创建一个引用类型
--比如
type cur_user_typ is ref cursor return ma_users%rowtype; --强类型,提取数据必须用指定的类型
type cur_log_type is ref cursor ;--弱类型 --sys_refcusor
my_cursor sys_refcursor; --系统预定义弱类型 --声明游标变量
cursor_name cursor_type_name
declare type my_cur_type is ref cursor ;
v_cur my_cur_type;
begin end ;
/
--声明一个游标变量和创建一个真正的游标对象
--后者是通过游标的SQL语句查询出来的结果集,相当于常量,不过游标变量引用或者指向一个游标对象
--声明一个游标变量并不会创建一个游标对象。 --打开游标变量
open cursor_name for select_statement; --弱类型的和强类型的打开方式 declare type my_cur_type is ref cursor return ma_users%rowtype;
v_cur my_cur_type;
begin
dbms_output.put_line('test');
open v_cur for select * from ma_users;
open v_cur for select * from ma_user_log;
end ;
/
--从一个游标变量中获取数据
fetch cursor_variable_name into record_name;
fetch cursor_variable_name into variable_name1,variable_name2
--注意弱类型和强类型之分
--弱是强类型,into变量必须 与return的类型相匹配,
--兼容性检查,在运行时刻发生,如果类型不匹配就会报ROWTYPE_MISMATCH异常 create or replace procedure test_ref(i_type in varchar2,io_ref in out sys_refcursor )is begin
if i_type = 'user' then
open io_ref for select * from ma_users;
end if; if i_type = 'log' then
open io_ref for select * from ma_user_log;
end if;
end test_ref;
/ declare v_cur sys_refcursor;
v_type varchar2(10);
v_user_log ma_user_log%rowtype;
v_user ma_users%rowtype;
begin v_type := 'user';
test_ref(v_type,v_cur);
fetch v_cur into v_user_log ;
dbms_output.put_line(v_user_log.TRACE_MARK);
--onw row
exception
when rowtype_mismatch then
fetch v_cur into v_user ;
dbms_output.put_line(v_user.user_name);
end ;
/ declare v_cur sys_refcursor;
v_type varchar2(10);
v_user_log ma_user_log%rowtype;
v_user ma_users%rowtype;
begin v_type := 'log';
test_ref(v_type,v_cur);
loop
fetch v_cur into v_user_log ;
dbms_output.put_line('TRACE_MARK=='||v_user_log.TRACE_MARK);
exit when v_cur%notfound;
end loop;
--all rows
exception
when rowtype_mismatch then
fetch v_cur into v_user ;
dbms_output.put_line(v_user.user_name);
end ;
/
--游标变量的使用规则
--游标变量是对一个游标对象或者一个数据库中查询的引用,而不是游标变量本身
--如果游标变量为一个查询执行了OPEN语句
--用另外一个引用这个查询的游标变量对这个游标变量赋值
--其中之一为真,就可以说这个游标变量引用了一个查询 --编译时的行类型匹配规则 --1.如果以下条件为真,两个游标变量(包括过程的参数)在赋值操作和参数传递过程是兼容的
--两个变量或者参数都是强类型的REF CURSOR 且具有相同的ROWTYPE
--两个变量或者参数都是弱类型的REF CURSOR 不管名字是否相同
--一个变量或者参数是任意一种强类型的REF CURSOR 另一个是任意一种弱类型的REF CURSOR --2.一个强类型的游标变量或者参数,打开的查询语句返回的行结构要和声明的RETURN ROWTYPE 一致 --3.一个弱类型的游标变量或者参数,可以打开任意查询,获取的数据可以用于任何变量列或者记录类型 --运行时刻的行类型匹配规则
--一个弱类型的游标变量或者参数可以用来引用任何一种行类型的查询,而不管之前引用的是什么查询
--一个强类型的游标变量或者参数,只能引用结构上匹配RETURN的类型的查询
--如果两条记录或者变量列表能够满足一下条件,就可以认为是隐式转换后结构兼容的
--两个记录或者列表字段的数量相同
--每个字段的数据类型相同或者可以被隐式转换成另一个类型 --用在FETCH语句中的游标变量或者参数,和这个游标变量关联的查询必须在结构上能够和FETCH
--语句INTO 中的记录或者变量列相兼容
--游标变量别名 --如果把一个游标变量赋值给另外一个游标变量,它们就成为一个游标对象的别名,共享游标对象
declare
type cur_type is ref cursor; v_cur1 cur_type;
v_cur2 cur_type;
v_user ma_users%rowtype;
begin
open v_cur1 for
select * from ma_users;
v_cur2 := v_cur1;
fetch v_cur1
into v_user;
dbms_output.put_line(v_user.user_name);
fetch v_cur2
into v_user;
dbms_output.put_line(v_user.user_name);
close v_cur2;
fetch v_cur1
into v_user; end;
/ --游标对象的作用范围
declare
v_cur1 sys_refcursor;
v_user ma_users%rowtype;
begin
declare
v_cur2 sys_refcursor;
begin
open v_cur2 for
select * from ma_users;
v_cur1 := v_cur2;
end; fetch v_cur1
into v_user;
dbms_output.put_line(v_user.user_name);
close v_cur1;
end;
/ --游标量作为参数传递
--调用一个过程或者一个函数时,可以把游标变量作为一个参数传递
--使用前,必须指定参数的模式还有类型 DECLARE
TYPE CUR_TYPE IS REF CURSOR RETURN MA_USERS%ROWTYPE;
PROCEDURE OPEN_CUR(I_CUR OUT CUR_TYPE) IS BEGIN
OPEN I_CUR FOR
SELECT * FROM MA_USERS;
END OPEN_CUR;
BEGIN
DECLARE
V_CUR CUR_TYPE;
BEGIN
OPEN_CUR(V_CUR); END; END;
/
--或者
CREATE OR REPLACE PACKAGE USER_REF
IS
TYPE CUR_TYPE IS REF CURSOR RETURN MA_USERS%ROWTYPE;
END USER_REF; DECLARE
PROCEDURE OPEN_CUR(I_CUR OUT USER_REF.CUR_TYPE) IS
BEGIN
OPEN I_CUR FOR
SELECT * FROM MA_USERS;
END OPEN_CUR;
BEGIN
DECLARE
V_CUR USER_REF.CUR_TYPE;
BEGIN
OPEN_CUR(V_CUR);
END;
END;
/
--设置参数模式
-- in 参数对程序是只读的
--out 参数对于程序来说是只写的
-- in out 参数对程序可读可写 --注意游标变量和游标对象的区分
--怎样改变游标变量的值
---对于游标变量进行赋值
--使用OPEN FOR 语句 PROCEDURE org_CUR(I_CUR1 in USER_REF.CUR_TYPE,
I_CUR2 out USER_REF.CUR_TYPE) IS
BEGIN
I_CUR2 := I_CUR1; END org_CUR; --游标变量的约束
--嵌套表、关联数组、或者VARRAY的元素不能保存游标变量值,不能用REF CUROSR类型定义集合元素
--数据库的列不能保存游标变量值
--不能给一个游标变量赋值NULL
--不能用比较运算符去测试游标变量是否相等,不想等,不相等或者为空
--游标变量不能在包中声明 --游标表达式
--用CUROSR操作符表示,返回的是一个嵌套在查询语句中的游标
--这个嵌套游标结果集的每一行可以包含一个SQL查询通常允许的值范围。也可以包汗其它子查询创建的游标
--语法
CURSOR(SUBQUERY); --怎样使用游标表达式 --把子查询作为外层查询的一列
--把一个查询转换为一个结果集,这样可以把结果集在一个流或者转换函数中传递 declare cursor cur_users is
select r.user_name,
cursor (select rr.trace_mark
from ma_user_log rr
where rr.user_name = r.user_name) userLog
from ma_users r;
v_user_name ma_users.user_name%type;
v_log_cur sys_refcursor;
v_mark ma_user_log.trace_mark%type;
begin open cur_users;
loop
fetch cur_users
into v_user_name, v_log_cur;
exit when cur_users%notfound;
dbms_output.put_line(v_user_name);
loop
fetch v_log_cur
into v_mark;
exit when v_log_cur%notfound;
dbms_output.put_line('----' || v_mark);
end loop;
end loop; end;
/
--游标表达式的限制
--不能把游标表达式用于隐式游标,因为没有机制支持把嵌套游标的数据取到PL/SQL的数据结构中
--游标表达式只能出现在最外层的SELECT 列表中
--声明视图时不能使用游标表达式
--如果在动态SQL中使用游标表达式,不能对游标表达式进行BIND和EXECUTE操作
-------------------------------------------------
1. 请简要描述下游标变量和游标对象之间的关系
2。前几课的作业,有了用户模块,也有购物车,现在还缺订单模块,记录订单信息,一个订单可能含有多个商品,
不同的订单上的同种商品的价格可能还不一样,比如说折扣不同等,索引需要设计订单表来记录订单信息,
可能需要一个订单号,用户,联系方式,邮寄地址,总价格等等,还需要关联一个订单下商品表等。
一个用户可能有多个订单,一个订单可能有多个商品。请大家设计出所需要的表,并且练习使用游标表达式来展示一个用下的订单,及订单下的商品信息
1 游标变量:一个指向或者引用底层的游标的变量。可以用于打开任何一个查询,
一个程序中执行多个不同的查询,可以再不同的Pl/sql程序间传递结果集,在客户端跟服务器端使用游标变量来传递。
游标对象:游标变量的值是对游标对象的引用,只有2个操作可以改变游标变量的值,open for语句与变量赋值。
声明一个游标变量并不会创建一个游标对象
--定义游标变量
type cus_cur_type is ref cursor return chg_test_b%rowtype;
type cus_cur_type is ref cursor;
--声明游标变量
rowList cus_cur_type;
rowValue chg_test_b%rowtype;
--打开一个游标变量
open rowList for select * from ma_users; 2
1 相关表
create table goods
(create_on date default sysdate,
create_by varchar2(20) default 'system',
update_on date default sysdate,
update_by varchar2(20) default 'system',
goods_id varchar2(32) default sys_guid() primary key,--goods id ,uk
goods_name varchar2(100) not null,--goods name
goods_photo varchar2(100),--goods photo url
goods_number number(18,0),--goods total number
tag_price number(18,2) not null,--goods tag price
normal_price number(18,2),----goods normal price,default =tag price
goods_category varchar2(20),--goods category
goods_currency varchar2(20) default 'CNY',--default currency CNY
goods_type varchar2(20),--goods type
goods_subtype1 varchar2(20),--goods sub type 1
goods_subtype2 varchar2(20),-- goods sub type 2
goods_status number default 1 ,--0/inactive,1/active
goods_description varchar2(100),--goods description
is_selling number default 1,--0/not selling,1/selling
selling_date date default sysdate ,--default seling date
is_promote number default 0--0/inactive,1/active
); create index idx_goods_name on goods(goods_name); insert into goods(goods_name,goods_photo,goods_number,tag_price,normal_price,goods_category,goods_currency,goods_type)
values('iphone 6s 64g','null',1000,6000,6000,'mobile','CNY','mobile');
insert into goods(goods_name,goods_photo,goods_number,tag_price,normal_price,goods_category,goods_currency,goods_type)
values('iphone 6s 16g','null',1000,5000,5000,'mobile','CNY','mobile');
insert into goods(goods_name,goods_photo,goods_number,tag_price,normal_price,goods_category,goods_currency,goods_type)
values('iphone 6s plus 16g','null',1000,6500,6500,'mobile','CNY','mobile');
insert into goods(goods_name,goods_photo,goods_number,tag_price,normal_price,goods_category,goods_currency,goods_type)
values('iphone 6s plus 64g','null',1000,7000,7000,'mobile','CNY','mobile') ; create table order_goods
(create_on date default sysdate,
create_by varchar2(20) default 'system',
update_on date default sysdate,
update_by varchar2(20) default 'system',
id_order_goods varchar2(32) primary key,
order_id varchar2(32) not null ,--order_info's order id
goods_id varchar2(32) not null,--reference goods id
goods_name varchar2(100) not null,--goods name
goods_photo varchar2(100),--goods photo url
goods_number number(18,0),--goods number
goods_price number(18,2),--goods price
goods_category varchar2(20),--goods category
goods_currency varchar2(20) default 'CNY',--default currency CNY
order_status number default 1 ,--0/paying,1/paid
order_subtotal number(18,2) --A single order subtotal
); create index idx_og_goods_id on order_goods(goods_id);
create index idx_og_order_id on order_goods(order_id); create table order_info
(create_on date default sysdate,
create_by varchar2(20) default 'system',
update_on date default sysdate,
update_by varchar2(20) default 'system',
order_id varchar2(32) primary key,--order id
id_ma_user varchar2(32) not null,--reference ID_MA_USERS
session_id varchar2(32),--Mark multiple orders belong to the same time
order_status number ,--0/UNCONFIRMED,1/CONFIRMED
pay_status number ,--0/paying,1/paid
order_total number(18,2),--Mark multiple orders total
order_starttime date default sysdate,
order_endtime date,
comments varchar2(100)
); create index idx_oi_id_ma_user on order_info(id_ma_user); create table shopping_address
(create_on date default sysdate,
create_by varchar2(20) default 'system',
update_on date default sysdate,
update_by varchar2(20) default 'system',
shopping_address_id varchar2(32) default sys_guid() primary key,--shopping_address id
id_ma_user varchar2(32) not null,--reference ID_MA_USERS
address_name varchar2(100) not null,--address name
consignee varchar2(50),--consignee
email varchar2(100) ,--user email
country_name varchar2(30) ,--country_name
province_name varchar2(40) ,--province_name
city_name varchar2(40) ,--city_name
district_name varchar2(100) ,--district_name
address_detail varchar2(100) ,--address_detail
zipcode number,
tel_phone varchar2(20) ,
mobile_phone number,
delivery_date date); create index idx_sd_id_ma_user on shopping_address(id_ma_user); insert into shopping_address(id_ma_user,address_name,consignee,email,country_name,province_name,
city_name,district_name,address_detail,zipcode,tel_phone,mobile_phone,delivery_date)
values('2EAE6137499E6DC9E053BD02A8C0DDDF','home','乱世佳人','test@163.com','china','chengdu',
'wihou','','',86,'028-12345679',23333333,sysdate);
insert into shopping_address(id_ma_user,address_name,consignee,email,country_name,province_name,
city_name,district_name,address_detail,zipcode,tel_phone,mobile_phone,delivery_date)
values('2EAE613749AF6DC9E053BD02A8C0DDDF','home','幸运小子','test@163.com','china','chengdu',
'wihou','','',86,'028-12345679',223333322,sysdate);
insert into shopping_address(id_ma_user,address_name,consignee,email,country_name,province_name,
city_name,district_name,address_detail,zipcode,tel_phone,mobile_phone,delivery_date)
values('2EAE613749A06DC9E053BD02A8C0DDDF','home','乱世佳人2','test@163.com','china','chengdu',
'wihou','','',86,'028-12345679',2233333,sysdate); commit select d.user_name,d.id_ma_users,a.order_id,c.goods_id,c.goods_name,a.order_subtotal,e.order_total,b.mobile_phone,b.address_detail
from order_goods a,shopping_address b,goods c,ma_users d,order_info e
where a.order_id=e.order_id
and a.goods_id=c.goods_id
and a.goods_name=c.goods_name
and b.id_ma_user=d.id_ma_users
and d.user_name='乱世佳人' 2 实现 --相关设计细节,代码细节还需要优化
DECLARE
v_user_name ma_users.user_name%TYPE;
v_id_ma_users ma_users.id_ma_users%TYPE;
v_order_id order_goods.order_id%TYPE;
v_goods_id goods.goods_id%TYPE;
v_goods_name goods.goods_name%TYPE;
v_order_subtotal order_goods.order_subtotal%TYPE;
v_order_total order_info.order_total%TYPE;
v_mobile_phone shopping_address.mobile_phone%TYPE;
v_address_detail shopping_address.address_detail%TYPE;
v_order_starttime order_info.order_starttime%TYPE;
v_order_endtime order_info.order_endtime%TYPE;
v_log_cur SYS_REFCURSOR;
v_sysdate DATE DEFAULT SYSDATE;
CURSOR cur_order_goods
IS
SELECT d.user_name,
d.id_ma_users,
a.order_id,
c.goods_id,
c.goods_name,
a.order_subtotal,
e.order_total,
b.mobile_phone,
b.address_detail,
v_sysdate,
v_sysdate+2
FROM order_goods a,
shopping_address b,
goods c,
ma_users d,
order_info e
WHERE a.order_id = e.order_id
AND a.goods_id = c.goods_id
AND a.goods_name = c.goods_name
AND b.id_ma_user = d.id_ma_users
and d.user_name='乱世佳人';
BEGIN
OPEN cur_order_goods; LOOP
FETCH cur_order_goods
INTO v_user_name,
v_id_ma_users,
v_goods_id,
v_order_id,
v_goods_name,
v_order_subtotal,
v_order_total,
v_mobile_phone,
v_address_detail,
v_order_starttime,
v_order_endtime; EXIT WHEN cur_order_goods%NOTFOUND;
DBMS_OUTPUT.
put_line (
'username= '
|| v_user_name
|| ' orderid= '
|| v_order_id
|| ' goodsname= '
|| v_goods_name
|| ' ordersubtotal= '
|| v_order_subtotal
|| ' ordertotal= '
|| v_order_total
|| ' ordertime= '
|| TO_CHAR (v_order_starttime, 'yyyy-mm-dd hh24:mi:ss'));
END LOOP;
END;
PL/SQL 训练05--游标的更多相关文章
- PL/SQL 训练11--包
--所谓包,就是把一组PL/SQL的代码元素组织在一个命名空间下.--一种可以把程序或者其他的PL/SQL元素比如游标.类型.变量的组织结构在一起的结构(包括逻辑结构和物理结构)--包提供了非常重要的 ...
- PL/SQL -->隐式游标(SQL%FOUND)
PL/SQL -->隐式游标(SQL%FOUND) 分类: SQL/PLSQL 基础2010-12-22 16:23 4084人阅读 评论(0) 收藏 举报 sqlexceptionoracle ...
- PL/SQL — 显式游标
一.游标的相关概念及特性 1.定义 通过游标方式定位到结果集中某个特定的行,然后根据业务需求对该行进行相应特定的操作. 2.分类 显示游标: 用户自定义游标,用于处理select语句返回的多行数据. ...
- PL/SQL — 隐式游标
一.隐式游标的定义及其属性 定义 隐式游标由系统自动定义,非显示定义游标的DML语句即被赋予隐式游标属性.其过程由oracle控制,完全自动化.隐式游标的名称是SQL,不能对SQL游标显式地执行OPE ...
- 【Oracle】PL/SQL 显式游标、隐式游标、动态游标
在PL/SQL块中执行SELECT.INSERT.DELETE和UPDATE语句时,Oracle会在内存中为其分配上下文区(Context Area),即缓冲区.游标是指向该区的一个指针,或是命名一个 ...
- PL/SQL 训练13--plsql 优化
--数据缓存技术 --PGA和SGA---SGA:系统全局区域--PGA:Process Global Area是为每个连接到Oracle的用户进程保留的内存. ---PLSQL从PGA获取信息的速度 ...
- PL/SQL 训练12--动态sql和绑定变量
--什么是动态SQL?动态PL/SQL--动态SQL是指在运行时刻才构建执行的SQL语句--动态PL/SQL是指整个PL/SQL代码块都是动态构建,然后再编译执行 --动态SQL来可以用来干什么? - ...
- PL/SQL 训练04--事务
--pl/sql通过SQL和ORACLE数据库紧密的整合在一起--在pl/sql中可以执行任何操作语句(DML语句),包括INSERT,UPDATE,DELETE,MERGE,也包括查询语句--可否执 ...
- PL/SQL 训练01--基础介绍
--开始介绍变量之前,我们先看下怎么在PLSQL写程序,如下我们写了一个块 declare --声明部分,声明变量 v_name ) :='hello world'; begin --执行区域 dbm ...
随机推荐
- 关于C语言中结构体大小计算
结构体大小的计算,.网上说法一大堆还都不一样分什么对齐不对齐,偏移量什么的.. 在此稍微举例简单总结下: 对齐原则:每一成员的结束偏移量需对齐为后一成员类型的倍数 补齐原则:最终大小补齐为成员中最大 ...
- ssh登录CentOS服务器(Aliyun)
ssh登录 (一).在本机使用命令生成ssh密钥:(-C代表注释,如果有多个密钥,加个注释就会很好区分) ssh-keygen -t rsa -C "mingwei" (二).将本 ...
- 如何将Django部署到Apache服务器上
操作环境: Ubuntu 16.04 Apache 2.4 Django 1.9 Python 2.7 mod_wsgi 前言:本教程纯自己查阅资料后整理,望对大家有帮助! 1. 安装 mod_ws ...
- Chrome浏览器导出数字证书
1.F12打开开发者工具,选中"Security"面板-->找到"View certificate",点击 2.选中“详细信息”面板-->复制到文件
- 旧板与IO板之间的连接
COM3(底板)——COM3(IO板) COM4(底板)——COM2(IO板) 如果需要改东西就是执行下列命令 最后进行拍照测试和IO测试
- Ceph与OpenStack整合(仅为云主机提供云盘功能)
1. Ceph与OpenStack整合(仅为云主机提供云盘功能) 创建: linhaifeng,最新修改: 大约1分钟以前 ceph ceph osd pool create volumes 128 ...
- windows 改路径有小差异
https://jingyan.baidu.com/article/5552ef473e2df6518ffbc916.html cmd是windows下一个非常常用的工具,但是它默认的地址却是不变的. ...
- 《锋利的jQuery》读书笔记(DOM+事件)
前阵子买了一批书,就从锋利的jQuery看起吧,书中一些太过常规以及没有强记必要性的操作就不记录了. 1.DOM加载后执行JS $(document).ready(function(){ //.... ...
- git pull VS git fetch&merge
使用git fetch和git pull都可以更新远程仓库的代码到本地,但是它们之间还是有区别. git fetch git fetch origin master git log -p maste ...
- Wordpress在主题中自定义登陆页面并且禁用自带的登陆页面
在使用Wordpress制作主题之后,不想要他自带的登陆页面以及地址. 1.新建一个用户页面来接管与登陆相关的动作 //在主题根目录下新建page-login.php,通过action获取用户动作,然 ...