小青蛙oracle跟踪,Oracle 存储过程:游标

一、认识游标

什么是游标?游标是数据库的一种数据类型,它用来管理从数据源(表,视图等)获取到的数据结果集,可以想象成一个游动的光标(指针),指向一个结果集,通过游标的移动逐行提取每一行的记录,就像我们屏幕上的光标指示当前位置一样,“游标”由此得名。

游标分成静态游标和动态游标(也叫REF游标)。

静态游标:所谓静态游标,顾名思义,指的是数据已经固定的游标,在使用游标前,已经知道游标中的数据和类型。静态游标又可以细分成显式游标和隐式游标,显示游标指的是已经定义在变量区,并且已经指定结果集的游标;隐式游标则是不用定义,直接就可以用的游标,比如系统定义的隐式游标sql,比如使用for循环遍历某个SQL(这个SQL的结果集就是一个隐式游标)的自定义游标。

动态游标:与静态游标相反,在定义的时候并不知道其结果集,在使用时,再给它定义结果集(通俗来说,就是查询数据的SQL不是一成不变的)的游标。动态游标也可以细分成强类型和弱类型游标,强类型游标规定了其返回类型,弱类型游标则是不规定返回类型,可以获取任何结果集。

在使用游标时,通常需要借助游标的一些属性来做逻辑判断,比如说判断游标是否已经到了结果集的尾部,这个时候可以使用游标的found属性来做判断:if 游标%found then 。。以下是游标的一些属性具体说明:

1.%found :用于检验游标是否成功,通常在fetch语句前使用,当游标按照条件查询一条记录是,返回true。fetch语句(获取记录)执行情况True or False。

2.%notfound : 最后一条记录是否提取出true or false。 到了游标尾部,没有记录了,就返回true

3.%isopen : 游标是否打开true or false。

4.%rowcount :游标当前提取的行数 ,即获得影响的行数。

二、游标使用的语法

1.静态游标语法(显式):

a.声明游标:划分存储区域,注意此时并没有执行Select 语句:

CURSOR 游标名(参数 列表)   [返回值类型]   is  select 语句;

b.打开游标:执行select 语句,获得结果集存储到游标中,此时游标指向结果集头部,类似于java的迭代器,必须先执行.next(),游标才指向第一条记录。

open 游标名(参数 列表);

c.获取记录:移动游标取一条记录:

fetch  游标名 into  临时记录或属性类型变量;

d.关闭游标:将游标放入缓冲池中,没有完全释放资源。可重新打开。

close  游标名;

2.动态游标语法:

a.声明REF游标类型:这个声明相当于自定义一个游标类型,在声明REF游标类型时,可以一并确定REF 游标的分类:

⑴强类型REF游标:指定retrun type,REF 游标变量的类型必须和return type一致。

语法:type   REF游标名   is   ref cursor return  结果集返回记录类型;

⑵弱类型REF游标:不指定return type,能和任何类型的CURSOR变量匹配,用于获取任何结果集。

语法:type   REF游标名 is   ref cursor;

b.声明REF游标类型变量:

语法:变量名  已声明Ref游标类型;

c.打开REF游标,关联结果集:

语法:open   REF 游标类型变量   for   查询语句返回结果集;

d.获取记录,操作记录:

语法:fetch    REF游标名 into   临时记录类型变量或属性类型变量列表;

e.关闭游标,完全释放资源:

语法:close   REF游标名;

3.游标的遍历:

a.for循环游标:使用for循环遍历游标时,会自动打开游标,并且循环结束会自动关闭游标,所以在for循环之前和之后都不需要对游标进行open、close操作。另外,紧跟着for关键字的变量是不需要提前定义的。语法:

for 变量名 in 游标名

loop

处理逻辑;

end loop;

b.loop循环游标:   loop循环是不会自动打开或者关闭游标的,需要手动操作。退出循环语句必须在执行逻辑操作之前执行,原因是因为即使游标已经遍历完,已经记录游标变量的记录是不会清除的,如果先执行逻辑操作,会导致循环多走一次。语法:

open 游标名;

loop

fetch  游标名 into  临时记录或属性类型变量(多个以逗号隔开);

exit  when   游标名%notfound;

逻辑操作

end   loop;

close 游标名;

c.while循环游标:和loop有一点类似,语法:

open 游标名;

fetch  游标名 into 临时记录或属性类型变量(多个以逗号隔开);

while 游标名%found

loop

逻辑处理;

fetch  游标名 into 临时记录或属性类型变量(多个以逗号隔开);

end loop;

close 游标名;

三、示例

以下写了两个存储过程,分别记录了静态游标和动态游标的基础用法,可以用作参考:

静态游标相关:

create or replace procedure test_static_cursor is

--无参数静态显式游标

-- return test_user%rowtype 这里的返回值可以要 也可以不要,因为后面的SQL已经指定了返回值

CURSOR static_cursor return test_user%rowtype is

select * /**u.id, u.username, u.password*/

from test_user u;

--带参数的显示游标 (参数名 参数类型 [default 默认值])

CURSOR static_cursor1(p_name test_user.id%type default '123') is

select * from test_user u where u.id = p_name;

--定义变量 这里的变量类型的意思是保持和test_user的id列的类型一致.

--在定义变量以获取游标的数据时,建议使用这种方式

v_id test_user.id%type;

v_username test_user.username%type;

v_password varchar2(32);

--定义记录(记录的意思是游标的一条记录)变量

v_record static_cursor1%rowtype;

v_num number;

begin

--初始化一些数据

delete from test_user;

commit;

select count(1) into v_num from test_user;

if v_num = 0 then

insert into test_user

(id, username, password)

values

('123', 'shaoyu', 'shaoyu');

--系统定义的隐式游标:SQL

--注意一句sql语句只会影响一个隐式游标,多个sql语句执行会覆盖隐式游标sql

if sql%found then

dbms_output.put_line('成功插入' || sql%rowcount || '条数据');

end if;

insert into test_user

(id, username, password)

values

('456', 'admin', 'admin');

insert into test_user

(id, username, password)

values

('789', 'system', 'system');

commit;

end if;

--打开游标,此时会执行定义游标时的SQL

open static_cursor;

--读取游标数据

fetch static_cursor

into v_id, v_username, v_password;

--验证

dbms_output.put_line(v_id || '-' || v_username || '-' || v_password);

--关闭游标

close static_cursor;

--打开游标

open static_cursor1('456');

--读取游标数据存入单个变量

fetch static_cursor1

into v_id, v_username, v_password;

--验证

dbms_output.put_line(v_id || '-' || v_username || '-' || v_password);

close static_cursor1;

open static_cursor1('789');

--读取游标数据存入记录变量

fetch static_cursor1 into v_record ;

--验证

dbms_output.put_line(v_record.id || '-' || v_record.username || '-' || v_record.password);

close static_cursor1;

--游标的遍历:

--1.for循环(不需要打开游标)

dbms_output.put_line('for循环');

if static_cursor%isopen then

dbms_output.put_line('游标已打开');

else

dbms_output.put_line('游标未打开');

end if;

--data不需要提前定义

for data in static_cursor loop

if static_cursor%isopen then

dbms_output.put_line('游标已打开');

else

dbms_output.put_line('游标未打开');

end if;

dbms_output.put_line(data.id || '-' || data.username || '-' ||

data.password);

end loop;

if static_cursor%isopen then

dbms_output.put_line('游标已打开');

else

dbms_output.put_line('游标未打开');

end if;

--2.loop循环

dbms_output.put_line('loop循环');

open static_cursor;

loop

fetch static_cursor

into v_id, v_username, v_password;

exit when static_cursor%notfound;

dbms_output.put_line(v_id || '-' || v_username || '-' || v_password);

end loop;

close static_cursor;

--3.while循环

dbms_output.put_line('while循环');

open static_cursor;

fetch static_cursor

into v_id, v_username, v_password;

while static_cursor%found loop

dbms_output.put_line(v_id || '-' || v_username || '-' || v_password);

fetch static_cursor

into v_id, v_username, v_password;

end loop;

close static_cursor;

end test_static_cursor;

动态游标:

create or replace procedure test_dynamic_cursor is

--定义强类型REF游标类型

type dynamic_cursor_type1 is ref cursor return test_user%rowtype;

--定义弱类型REF游标

type dynamic_cursor_type2 is ref cursor;

--定义强类型REF自定义返回记录类型游标类型 先定义自定义返回记录类型 再定义游标类型

type dynamic_cursor_type3_rec is record(

user_id test_user.id%type,

username test_user.username%type);

type dynamic_cursor_type3 is ref cursor return dynamic_cursor_type3_rec;

--定义之前定义好的游标类型

dynamic_cursor1 dynamic_cursor_type1;

dynamic_cursor2 dynamic_cursor_type2;

dynamic_cursor3 dynamic_cursor_type3;

--定义返回类型变量

rec3 dynamic_cursor_type3_rec;

--定义变量

v_id test_user.id%type;

v_username test_user.username%type;

v_password varchar2(32);

v_num number;

begin

--初始化一些数据

delete from test_user;

commit;

select count(1) into v_num from test_user;

if v_num = 0 then

insert into test_user

(id, username, password)

values

('123', 'shaoyu', 'shaoyu');

insert into test_user

(id, username, password)

values

('456', 'admin', 'admin');

insert into test_user

(id, username, password)

values

('789', 'system', 'system');

commit;

end if;

dbms_output.put_line('强类型动态游标');

--给强类型动态游标关联结果集

open dynamic_cursor1 for select * from test_user;

--验证

loop

fetch dynamic_cursor1

into v_id, v_username, v_password;

exit when dynamic_cursor1%notfound;

dbms_output.put_line(v_id || '-' || v_username || '-' || v_password);

end loop;

close dynamic_cursor1;

--给弱类型动态游标关联结果集

dbms_output.put_line('弱类型动态游标');

open dynamic_cursor2 for select id,password from test_user;

--验证

loop

fetch dynamic_cursor2

into v_id, v_password;

exit when dynamic_cursor2%notfound;

dbms_output.put_line(v_id || '-' || v_password);

end loop;

close dynamic_cursor2;

--给自定义强类型动态游标关联结果集

dbms_output.put_line('自定义返回类型强类型动态游标');

open dynamic_cursor3 for select id,username from test_user;

--验证

loop

fetch dynamic_cursor3

into rec3;

exit when dynamic_cursor3%notfound;

dbms_output.put_line(rec3.user_id || '-' || rec3.username);

end loop;

close dynamic_cursor3;

end test_dynamic_cursor;

以上看起来游标好像就这么一些用法,那还有没有别的用法呢?有的,那就是在使用游标时,对游标的结果集对应的数据源进行操作。

四、更新、删除游标记录

在定义游标的时候,如果在定义结果集的语句后面加上for update或者for delete子串,那么在使用游标时,就可以对游标的结果集进行操作,不要担心数据源的状态,当使用for update、for delete子串打开一个游标时,所有返回集中的数据行都将处于行级(ROW-LEVEL)独占式锁定,其他对象只能查询这些数据行,不能进行update、delete或select...for update操作,保证了数据的正确性。

值得提醒的是,在多表查询中,使用of子句来锁定特定的表,如果忽略了of子句,那么所有表中选择的数据行都将被锁定。如果这些数据行已经被其他会话锁定,那么正常情况下oracle将等待,直到数据行解锁。

语法:

a.声明更新或删除显示游标:

cursor 游标名 is  select 语句   for update [ of  更新列列名];

cursor 游标名 is  select 语句   for delete [ of  更新列列名];

b.使用显示游标当前记录来更新或删除:

update 表名   set 更新语句  where   current  of   游标名;

delete from 表名  where   current  of   游标名;

这个就不写例子了,第三步的示例理解了之后,这个很容易编写。

五、使用游标作为存储过程出参

说了这么多,并没有将游标应用到实际中,其实web程序对数据库的调用多数情况下需要返回一个结果集,很显然,游标是非常适合的。在这种情况下,只需要将游标作为存储过程的出参就可以了。

1.包的概念

在上一篇中提到了包和存储过程,那什么是包呢?包(package)也是数据库的一种对象类型,它包含定义和包体(body)两个方面,【定义】类似于是java中的接口,【包体】类似于是java中对接口的实现类,包里面是可以包含【自定义类型】和【存储过程】的,可以认为是java接口中的全局变量(自定义类型)和方法(存储过程),就连使用方式也极其类似:包名.存储过程名(参数)。

有人觉得奇怪,不是要说游标做为存储过程出参吗?怎么又扯上包这个东西了?

在java中,所有的变量都有一个作用域,oracle数据库也不例外,假设我们单独定义一个存储过程,在参数那一列是要规定参数类型的,如果我们使用的是自定义的游标,那么这个游标类型在这个存储过程参数里是肯定没有定义的,所以我们需要借助包,在包中定义自定义的游标类型,然后再把这个自定义游标作为包中的存储过程的出入参,这样就保证了游标在存储过程中的作用域始终可用。

2.包的语法:

包定义:

create or replace package 包名 as

定义 自定义type

定义 全局变量

procedure 存储过程名; --没有存储过程具体实现

function 函数名;

end test_package;

包体定义:

create or replace package body test_package as

定义变量

procedure 存储过程名(参数) is ...存储过程具体实现

end test_package;

下面写个实例:

create or replace package test_package as

--定义游标类型

type o_cur is ref cursor;

--定义存储过程

procedure test_static_cursor(o_data out o_cur);

end test_package;

create or replace package body test_package as

--存储过程具体实现

procedure test_static_cursor(o_data out o_cur) is

v_num number;

begin

--初始化一些数据

delete from test_user;

commit;

select count(1) into v_num from test_user;

if v_num = 0 then

insert into test_user

(id, username, password)

values

('123', 'shaoyu', 'shaoyu');

insert into test_user

(id, username, password)

values

('456', 'admin', 'admin');

insert into test_user

(id, username, password)

values

('789', 'system', 'system');

commit;

end if;

--给出参关联结果集

open o_data for

select * from test_user;

end;

end test_package;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/528519.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux使用设备文件的目录,Linux系统下的/dev目录

dev是设备(device)的英文缩写。/dev这个目录对所有的用户都十分重要。下面学习啦小编给大家分享详解Linux系统下的/dev目录,欢迎阅读:详解Linux系统下的/dev目录:在这个目录中包含了所有linux系统中使用的外部设备。但是这里并不是放的外部设…

linux解锁文件.user.ini,Linux下解决网页服务器权限和.user.ini无法删除的问题

解决网页服务器权限问题在linux环境下搭建Nginx/apache网页服务器,在尝试访问页面时都会遇到这个问题,提示没有写入权限,或者必须访问网页服务器权限。这源自于linux对于权限的限制比较严格。所以网上无数的解决方法,无外乎一个提…

linux内核 lts长期演进,Linux Kernel 4.19 和 5.4 生命周期延长至 6 年

近日,Linux 内核开发及维护者 Greg Kroah-Hartman 宣布将 Linux 内核 4.19 和 5.4 版本的生命周期终止(EOL)支持从两年延长到六年,分别延长至 2024 和 2025 年。在这期间,它们能够继续向后移植 bug 并获得一些重要的安全修复程序。Linux 内核…

linux pipe 文件,Linux系统常用指令、管道(pipe)、文件查找(find)

//在3~4个目录层次中间查找passwd文件find / -mindepth 3 -maxdepth 4 -name passwd//查找文件名中含有”an”的文件find ./ -name “*an*” -type f//查找大于7K的文件find ./ -size 7k//查找小于100字节的文件,c表示字节find ./ -size -100c-name 文件名字 //直接…

stm32运行linux,新出的STM32F750成功运行Linux

本帖最后由 ICZOOM 于 2018-12-20 15:28 编辑前段时间ST推出了Value Line的STM32F750和H750两个系列,看了一下选型表,F750有LQFP144封装,正好适合我的STM32 linux板子。于是,双十一买了几片。同时,我对我的linux板做了…

linux如何运行qt源码,Linux/Ubuntu下编译Qt4.8.2源码

本人所用的系统为Ubuntu 14,所下载的QT源码版本为4.8.2。以下是编译QT源码的基本步骤:2.解压代码输入指令: tar zxvf qt-everywhere-opensource-src-4.8.2.tar.gz,解压出QT源代码。3.执行./config生成makef…

linux启动过程中内核拷贝,轻松识破linux内核启动过程中的“”套路“”

内核启动流程相关的内容让很多热爱linux的小伙伴既爱又恨,因为这是了解linux系统基本构造的良好过程同时由于其本身复杂且底层,脑子中的脉络不是很清晰,本文就总结了一些优秀博文,以自己的理解来解构一下。本文的环境是CentOS 6.8…

linux brctl命令,Linux中brctl命令起什么作用呢?

摘要:下文讲述Linux中brctl的功能说明,如下所示;brctl命令功能:用于对以太网的网桥进行配置brctl命令的语法格式:brctl [参数] -----常用参数说明------addbr:创建网桥delbr:删除网桥addif:将网卡接口接入网桥delif:删除网桥接入的网卡接口sh…

linux文件编辑命令详细整理,Linux文件编辑命令vi详细说明

目录1、引言2、vi命令与vim命令3、vi命令使用3.1一般模式3.2编辑模式(重点)3.3命令行模式1、引言相信有很多程序员朋友,在长时间不操作linux系统或者初学linux命令时,都有跟我有一样的疑惑。这个vim命令与vi命令有什么区别?不都是编辑器么&am…

linux wifi ip,Linux环境下使用WIFI模块:使用DHCP工具动态获得IP地址

使用DHCP工具动态获得IP地址实验版本及下载地址DHCP:dhcp-4.4.1.tar.gz链接: [https://www.isc.org/downloads/]编译安装DHCP配置编译选项解压完成后进入DHCP根文件目录,输入配置指令./configure --hostarm-linux ac_cv_file__dev_randomyes --with-rand…

linux 硬盘空间监控,Linux服务器硬盘空间监控

#*************************************************************************# Author : 清风# CreateDate : 2015/6/12 15:30:13# Description : this script is mointoring the linux disk capacity, if disk used more than 90%,then it wi…

linux nifi指定jdk,nifi在arm架构启动不了,怎么办?

问题描述nifi启动报错问题出现的平台版本及自己尝试过哪些方法arrch64、在服务器上编译过相关代码粘贴代码文本(请勿用截图)ERROR [main] org.apache.nifi.NiFi Failure to launch NiFi due to java.util.ServiceConfigurationError: org.apache.nifi.processor.Processor: Pro…

linux内核更新 2.6.38,Linux内核 2.6.38版本预计到四月初完成

【IT168 技术】Linux版本2.6.37发布整整14天之后,Linus Torvalds发布了内核2.6.38的第一测试版,正式版预计将在三月底或四月初完成。在此版本中,开发周期的合并窗口已经结束,Torvalds集 成这个版本的大多数变化到主要开发分支的源…

Linux指令lunch,linux命令 launch是什么命令?

lunch 吧?类似于choosecombo,选择编译编译参数的。执行source build/envsetup.sh才有。其实我也不是特别了解。我觉得应该是在envsetup.sh里定义的。不知道你用过choosecombo没有,跟那个是类似的。$ source build/envsetup.shincluding devic…

linux创建a1的硬链接a2,Linux命令-重定向和软硬链接

echo功能说明:显示文字. 语 法:echo [-ne][字符串] 或 echo [–help][–version]参数:   -n 不要在最后自动换行重定向>是定向输出到文件,如果文件不存在,就创建文件;如果文件存在,就将其…

linux shell 博客,【博客侠】Linux Shell脚本系列:开始上手(1)

看标题大家应该知道,这是一个关于 Linux Shell 脚本的系列文章。通过本系列文章希望帮助 Linux 初学用户快速开始上手编写 Shell 脚本,能够利索的对脚本进行测试和使用。在接下来的首篇文章中,我们将介绍什么是 Shell,有哪些 Linu…

怎么在linux中查询yum,linux - 如何使用YUM列出包的内容?

linux - 如何使用YUM列出包的内容?我知道如何使用rpm列出包的内容(rpm -qpil package.rpm)。 但是,这需要知道.rpm文件在文件系统上的位置。 更优雅的解决方案是使用包管理器,在我的例子中是YUM。 如何使用YUM实现这一目标?7个解决…

在linux安装光盘启动不了,linux光盘启动

〔以RedHat 7.3为例〕1.把安装盘的第一张放到光驱,然后重新启动机器,在BOIS中把系统用光驱来引导。2.等安装界面出来后,按〔F4〕键,也就是linux rescue模式。3.一系列键盘以及几项简单的配制,过后就〔继续〕了。。。这…

linux libusb应用实例,在Linux中使用libusb-1.0作为非root用户访问USB设备

我试图在RHEL5上作为非root用户与USB设备连接.该设备是一个GPIO接口(其文档可在http://www.xdimax.com/sub20/sub20.html找到),它使用libusb-1.0.使用其API打开设备的过程是:sub_device d;d sub_find_devices(0);sub_handle h sub_open(d);当我这样做时,sub_find_…

交叉调试 arm linux,搭建交叉调试环境Arm-Linux-Gdb与gdbserver

操作系统:Ubuntu9.04 开发板:博创2410s 交叉编译工具:arm-linux-gcc-4.1.1 gdbgdbserver 是调试目标板的常用方法.网络环境如下:HOST 192.168.1.123 Target: 192.168.1.21NFS共享目录: mount -t nfs -o intr,nolock,rsize1024,ws…