十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
你说的资料上说的尽量不使用游标,那就是使用动态sql+execute immediate的方式了。这主要是程序设计上的问题,比如说一些表名或者字段名,可以使用变量来代替,这样写程序的时候麻烦点,但是方便日后维护。取数据用游标没什么错误,看你的存储过程,重要的是还是优化sql。或者可以使用bulk collect 批量的方式取数据。
创新互联建站从2013年成立,是专业互联网技术服务公司,拥有项目网站设计、做网站网站策划,项目实施与项目整合能力。我们以让每一个梦想脱颖而出为使命,1280元临淄做网站,已为上家服务,为临淄各地企业和个人服务,联系电话:13518219792
你说的资料上说的尽量不使用游标,那就是使用动态sql+execute immediate的方式了。这主要是程序设计上的问题,比如说一些表名或者字段名,可以使用变量来代替,这样写程序的时候麻烦点,但是方便日后维护。取数据用游标没什么错误,看你的存储过程,重要的是还是优化sql。或者可以使用bulk collect 批量的方式取数据。
游标(CURSOR)也叫光标,在关系数据库中经常使用,在PL/SQL程序中可以用CURSOR与SELECT一起对表或者视图中的数据进行查询并逐行读取。
Oracle游标分为显示游标和隐式游标。
显示游标(Explicit Cursor):在PL/SQL程序中定义的、用于查询的游标称作显示游标。
隐式游标(Implicit Cursor):是指非PL/SQL程序中定义的、而且是在PL/SQL中使用UPDATE/DELETE语句时,Oracle系统自动分配的游标。
一.显示游标
1.使用步骤
(1)定义 (2)打开 (3)使用 (4)关闭
2.使用演示
首先创建测试用表STUDENT,脚本如下:
CREATE TABLE "STUDENT" (
"STUNAME" VARCHAR2(10 BYTE),
"STUNO" VARCHAR2(4 BYTE),
"AGE" NUMBER,
"GENDER" VARCHAR2(2 CHAR)
)
(1).使用WHILE循环处理游标
create or replace PROCEDURE PROC_STU1 AS
BEGIN
--显示游标使用,使用while循环
declare
--1.定义游标,名称为cur_stu
cursor cur_stu is
select stuno,stuname from student order by stuno;
--定义变量,存放游标取出的数据
v_stuno varchar(4);
v_stuname varchar(20);
begin
--2.打开游标cur_stu
open cur_stu;
--3.将游标的当前行取出存放到变量中
fetch cur_stu into v_stuno,v_stuname;
while cur_stu%found --游标所指还有数据行,则继续循环
loop
--打印结果
dbms_output.PUT_LINE(v_stuno||'-'||v_stuname);
--继续将游标所指的当前行取出放到变量中
fetch cur_stu into v_stuno,v_stuname;
end loop;
close cur_stu; --4.关闭游标
end;
END PROC_STU1;
(2).使用IF..ELSE代替WHILE循环处理游标
create or replace PROCEDURE PROC_STU2 AS
BEGIN
--显示游标使用,使用if判断
declare
--1.定义游标,名称为cur_stu
cursor cur_stu is
select stuno,stuname from student order by stuno;
--定义变量,存放游标取出的数据
v_stuno varchar(4);
v_stuname varchar(20);
begin
--2.打开游标cur_stu
open cur_stu;
--3.将游标的当前行取出存放到变量中
fetch cur_stu into v_stuno,v_stuname;
loop
if cur_stu%found then --如果游标cur_stu所指还有数据行
--打印结果
dbms_output.PUT_LINE(v_stuno||'-'||v_stuname);
--继续将游标所指的当前行取出放到变量中
fetch cur_stu into v_stuno,v_stuname;
else
exit;
end if;
end loop;
close cur_stu; --4.关闭游标
end;
END PROC_STU2;
(3).使用FOR循环处理游标
create or replace PROCEDURE PROC_STU3 AS
BEGIN
--显示游标使用,使用for循环
declare
--定义游标,名称为cur_stu
cursor cur_stu is
select stuno,stuname from student order by stuno;
begin
for stu in cur_stu
loop
dbms_output.PUT_LINE(stu.stuno||'-'||stu.stuname);
--循环做隐含检查 %notfound
end loop;
--自动关闭游标
end;
END PROC_STU3;
(4).常用的使用EXIT WHEN处理游标
create or replace
PROCEDURE PROC_STU1_1 AS
BEGIN
--显示游标使用,使用exit when循环
declare
--1.定义游标,名称为cur_stu
cursor cur_stu is
select stuno,stuname from student order by stuno;
--定义变量,存放游标取出的数据
v_stuno varchar(4);
v_stuname varchar(20);
begin
--2.打开游标cur_stu
open cur_stu;
loop
--3.将游标的当前行取出存放到变量中
fetch cur_stu into v_stuno,v_stuname;
exit when cur_stu%notfound; --游标所指还有数据行,则继续循环
--打印结果
dbms_output.PUT_LINE(v_stuno||'-'||v_stuname);
end loop;
close cur_stu; --4.关闭游标
end;
END PROC_STU1_1;
二.隐式游标
1.使用演示
create or replace PROCEDURE PROC_STU4 AS
BEGIN
--隐式游标使用
update student set stuname='张燕广' where stuno='1104';
--如果更新没有匹配则插入一条新记录
if SQL%NOTFOUND then
insert into student(STUNO,STUNAME,AGE,GENDER)
values('1104','张燕广',18,'男');
end if;
END PROC_STU4;
2.说明
所有的SQL语句在上下文区内部都是可执行的,因为都有一个游标指向上下文区,此游标就是
SQL游标,与现实游标不同的是,SQL游标在PL/SQL中不需要打开和关闭,而是在执行UPDATE、
DELETE是自动打开和关闭。
上面例子中就是通过SQL%NOTFOUND游标属性判断UPDATE语句的执行结果决定是否需要插入新记录。
问题总结:
1.index by表不能存储在数据库中的type中,故选择嵌套表。
2.ibatis不支持oracle的复合数据类型的返回。(个人理解)
3.替代方案:用返回oracle游标来代替复合数据类型。ibatis能接受oracle游标类型。
注意此处是ibatis2.3
部分代码:
1.java
复制代码
1 private MapString,Object userStateResult(Users users)throws Exception{
2 MapString,Object param = new HashMapString,Object();
3 param.put("PRM_USERID", users.getUserid().toString());
4 param.put("PRM_OBJECTS", null);
5 param.put("PRM_TAGS", null);
6 param.put("PRM_APPCODE", null);
7 param.put("PRM_ERRMSG", null);
8 getDao().queryForList("user.prc_user_index",param);
9 if(Constant.SUCCESS.equals(param.get("PRM_APPCODE"))){
10 return param;
11 }else{
12 return null;
13 }
14 }
复制代码
返回值(包括游标的返回值)都在param这个map中
2.ibatis代码:
复制代码
1 parameterMap class="java.util.Map" id="UserIndexParam"
2 parameter property="PRM_USERID" javaType="java.lang.String"
3 jdbcType="VARCHAR" mode="IN" /
4 parameter property="PRM_OBJECTS" javaType="java.sql.ResultSet"
5 jdbcType="ORACLECURSOR" mode="OUT" resultMap="ref_object" /
6 parameter property="PRM_TAGS" javaType="java.sql.ResultSet"
7 jdbcType="ORACLECURSOR" mode="OUT" resultMap="ref_tag" /
8 parameter property="PRM_APPCODE" javaType="java.lang.String"
9 jdbcType="VARCHAR" mode="OUT" /
10 parameter property="PRM_ERRMSG" javaType="java.lang.String"
11 jdbcType="VARCHAR" mode="OUT" /
12 /parameterMap
13 ---------------------------------------------------------------------------------
14 resultMap id="ref_tag" class="com.diy.tag.entity.Tag"
15 result column="tagid" jdbcType="VARCHAR" property="tagid" /
16 result column="tagname" jdbcType="VARCHAR" property="name" /
17 /resultMap
18
19 resultMap class="com.diy.comm.cursorHandler.ObjectHandler" id="ref_object"
20 result column="OBJECTID" jdbcType="DECIMAL" property="objectid" /
21 result column="OWNERID" jdbcType="DECIMAL" property="ownerid" /
22 result column="DBUSID" jdbcType="DECIMAL" property="dbusid" /
23 result column="DUSERSID" jdbcType="DECIMAL" property="dusersid" /
24 result column="TAGID" jdbcType="VARCHAR" property="tagid" /
25 result column="USERNAME" jdbcType="VARCHAR" property="username" /
26 result column="OBJNAME" jdbcType="VARCHAR" property="objname" /
27 result column="LOVENUM" jdbcType="DECIMAL" property="lovenum" /
28 result column="INRUDUCTION" jdbcType="VARCHAR" property="inruduction" /
29 result column="CATAGROY" jdbcType="DECIMAL" property="catagroy" /
30 result column="IMAGEPATH" jdbcType="VARCHAR" property="imagepath" /
31 /resultMap
32 ---------------------------------------------------------------------------
33 procedure id="prc_user_index" parameterMap="UserIndexParam"
34 {call
35 PKG_USER.PRC_USER_INDEXVIEW(?,?,?,?,?)}
36 /procedure
复制代码
有一篇文章写的很好:大家可以参考一下
但是对于本问题没有用ibatis的TypeHandler。
因为存储过程调试可以返回游标数据,但是ibatis接受的到全部是null。不知道原因,有知道的朋友可以留言一下。
我个人猜测可能是ibatis版本问题。
3.存储过程代码(部分):
复制代码
--对象类型
CREATE OR REPLACE TYPE TAGS_INFO IS object
(
TAGID number,
TAGNAME varchar2(200)
)
--嵌套表
CREATE OR REPLACE TYPE table_tag IS TABLE OF TAGS_INFO
注意对象和嵌套表都要放在全局的。不能定义在包体中
--兴趣游标
TYPE TAGCURSOR IS REF CURSOR;
--东西游标
TYPE OBJECTCURSOR IS REF CURSOR;--这个定义在包体中
------------------------------------------------------------------------
PROCEDURE PRC_USER_INDEXVIEW(PRM_USERID IN VARCHAR2,
PRM_OBJECTS OUT OBJECTCURSOR,
PRM_TAGS OUT TAGCURSOR,
PRM_APPCODE OUT VARCHAR2,
PRM_ERRMSG OUT VARCHAR2) IS
N_FLAG NUMBER;
VAR_FIRSTTAG VARCHAR2(100);
VAR_DUSERID VARCHAR2(100);
INDEX_TAGS TABLE_TAG;
--用户兴趣标签
CURSOR CUR_USERTAG IS
SELECT C.TAGID, C.NAME
FROM USERSDETIAL A, TAGRELATION B, TAG C
WHERE A.DUSERSID = B.DUSERSID
AND B.TAGID = C.TAGID
AND A.DUSERSID = VAR_DUSERID;
--公共兴趣标签
CURSOR CUR_USERPUB IS
SELECT T.*
FROM (SELECT ROWNUM AS RNUM,
COUNT(A.DUSERSID) AS CNUM,
B.TAGID,
B.NAME
FROM TAGRELATION A, TAG B
WHERE A.TAGID = B.TAGID
GROUP BY A.DUSERSID, B.TAGID, B.NAME, ROWNUM) T
WHERE RNUM = 8
ORDER BY T.CNUM DESC;
--object
/*CURSOR CUR_OBJ(VAR_TAGID VARCHAR2) IS
SELECT ROWNUM AS RN, A.*
FROM OBJECT A
WHERE trim(A.TAGID) = VAR_TAGID
AND ROWNUM 30;*/
REC_USERTAG CUR_USERTAG%ROWTYPE;
REC_USERPUB CUR_USERPUB%ROWTYPE;
--REC_OBJ OBJECT%ROWTYPE;
BEGIN
PRM_APPCODE := PKG_COMM.DEF_OK;
PRM_ERRMSG := '';
IF PRM_USERID IS NULL THEN
PRM_APPCODE := PKG_COMM.DEF_ERR;
PRM_ERRMSG := '参数未定义';
RETURN;
END IF;
--用户详细ID是否存在
SELECT B.DUSERSID
INTO VAR_DUSERID
FROM USERS A, USERSDETIAL B
WHERE A.USERID = B.USERSID
AND A.USERID = PRM_USERID;
IF VAR_DUSERID IS NULL THEN
PRM_APPCODE := PKG_COMM.DEF_ERR;
PRM_ERRMSG := '参数无效';
RETURN;
END IF;
--1.判断是否为有效用户
SELECT NVL(A.FLAG, 1)
INTO N_FLAG
FROM USERS A, USERSDETIAL B
WHERE A.USERID = B.USERSID
AND B.DUSERSID = VAR_DUSERID;
IF N_FLAG = 1 THEN
PRM_APPCODE := PKG_COMM.DEF_ERR;
PRM_ERRMSG := '用户已被禁止登录';
RETURN;
END IF;
--2.判断用户是否有兴趣tag
FOR REC_USERTAG IN CUR_USERTAG LOOP
INDEX_TAGS := TABLE_TAG();
IF CUR_USERTAG%ROWCOUNT = 0 THEN
--获取公共兴趣游标
FOR REC_USERPUB IN CUR_USERPUB LOOP
INDEX_TAGS.EXTEND;
IF CUR_USERPUB%ROWCOUNT = 1 THEN
VAR_FIRSTTAG := REC_USERPUB.TAGID;
END IF;
INDEX_TAGS(CUR_USERPUB%ROWCOUNT) := TAGS_INFO(REC_USERPUB.TAGID,
REC_USERPUB.NAME);
END LOOP;
ELSIF CUR_USERTAG%ROWCOUNT = 1 THEN
VAR_FIRSTTAG := REC_USERTAG.TAGID;
END IF;
INDEX_TAGS.EXTEND;
INDEX_TAGS(CUR_USERTAG%ROWCOUNT) := TAGS_INFO(REC_USERTAG.TAGID,
REC_USERTAG.NAME);
--index_tags(CUR_USERTAG%ROWCOUNT).TAGNAME := REC_USERTAG.NAME;
END LOOP;
IF INDEX_TAGS.COUNT 0 THEN
/* --3. 取出object
FOR REC_OBJ IN CUR_OBJ(VAR_FIRSTTAG) LOOP
PRM_OBJECTS(CUR_OBJ%ROWCOUNT).OWNERID := REC_OBJ.OWNERID;
PRM_OBJECTS(CUR_OBJ%ROWCOUNT).OBJECTID := REC_OBJ.OBJECTID;
PRM_OBJECTS(CUR_OBJ%ROWCOUNT).DBUSID := REC_OBJ.DBUSID;
PRM_OBJECTS(CUR_OBJ%ROWCOUNT).DUSERSID := REC_OBJ.DUSERSID;
PRM_OBJECTS(CUR_OBJ%ROWCOUNT).TAGID := REC_OBJ.TAGID;
PRM_OBJECTS(CUR_OBJ%ROWCOUNT).LOVENUM := REC_OBJ.LOVENUM;
PRM_OBJECTS(CUR_OBJ%ROWCOUNT).INRUDUCTION := REC_OBJ.INRUDUCTION;
PRM_OBJECTS(CUR_OBJ%ROWCOUNT).CATAGROY := REC_OBJ.CATAGROY;
PRM_OBJECTS(CUR_OBJ%ROWCOUNT).Imagepath := REC_OBJ.Imagepath;
END LOOP;*/
--返回东西游标
OPEN PRM_OBJECTS FOR
SELECT ROWNUM AS RN, A.*, B.USERNAME
FROM OBJECT A, USERSDETIAL B
WHERE A.OWNERID = B.DUSERSID
AND TRIM(A.TAGID) = VAR_FIRSTTAG
AND ROWNUM 30;
END IF;
--tag游标
OPEN PRM_TAGS FOR
SELECT * FROM TABLE(CAST(INDEX_TAGS AS TABLE_TAG));
EXCEPTION
WHEN OTHERS THEN
PRM_APPCODE := PKG_COMM.DEF_ERR;
PRM_ERRMSG := '获取主界面数据失败' || '错误原因:' || PRM_ERRMSG || '-' || SQLERRM ||
'错误行数:' || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE();
END;
---注意:a.返回游标的用open for 方法,不用关心游标的关闭。它是自动关闭的。原因:调试把游标返回值点几下,你就回发现。
b.如果报错CURSOR IS CLOESD的话,说明游标里面没有数据。所以open for 必须保证有select中有数据
c.嵌套表这里要用类似与java的构造方法写,如上
如果写成类似于java中new对象后,用set方法给嵌套表赋值的,会报错未能未能初始化的结果集。
1. 用open打开的,用close关闭
declare
cursor mycursor is
select * from emp for update;
myrecord emp%rowtype;
begin
open mycursor;
loop
fetch mycursor into myrecord;
exit when mycursor%notfound;
if (myrecord.sal=2000) then
update emp
set sal=2001
where current of mycursor;
end if;
end loop;
close mycursor;
commit;
end;
2. 用for 循环的,循环完了就自己关了
declare
cursor mycursor is
select * from emp;
begin
for i in mycursor
loop
dbms_output.put_line(i.job);
end loop;
end;