LightDB PLSQL介绍(第一篇)

文章目录

  • 概述
  • 开发工具
    • ltsql
  • PL/SQL基础
    • 程序结构
      • 匿名块
      • 存储过程
      • 函数
    • 数据类型
  • 控制语句
    • 条件语句
      • IF 条件语句
      • CASE条件语句
    • 循环语句
      • 简单LOOP
      • FOR LOOP
      • WHILE循环
      • 循环退出
      • 循环继续
    • 循序控制
  • 静态语句
  • 动态语句
  • 游标
    • 隐式游标
      • 隐式游标属性
      • 隐式游标的异常处理
    • 显式游标
      • 声明显式游标
      • 打开显式游标
      • 使用显式游标获取数据
      • 关闭显式游标
      • 显式游标属性
    • 游标变量
      • 创建游标变量

概述

LightDB 数据库系统的PL/oraSQL(兼容Oracle PL/SQL) 是一个可加载的过程化语言,它用于与 Oracle PL/SQL 应用程序兼容。 后面章节PL/oraSQL都简称为PL/SQL

开发工具

ltsql

ltsql是一个LightDB的基于终端的前端。它能让你交互式地键入查询,把它们发送给LightDB,并且查看执行结果;也可以让输入来自于一个文件或者命令行参数。ltsql随着lightdb服务器的安装而自动安装。PL/SQL应用程序可以在ltsql终端下进行命令的执行。

ltsql --help 可以查看ltsql的参数说明,后续讲解的PL/SQL功能都将基于ltsql进行内容介绍和效果展示。

PL/SQL基础

程序结构

PL/SQL程序主要三种类型的程序,分别是匿名块,存储过程和存储函数。匿名块无需提前定义,可以直接运行,存储过程和存储函数需要提前创建,然后再进行调用。

匿名块

一个完整的匿名块由三个部分组成。

DECLARE     声明部分
BEGIN       执行部分
EXCEPTION   异常处理
END;
  • 声明部分:以关键字DECLARE开始,以BEGIN结束。该 部分主要用于声明变量、常量、数据类型、游标、异常处理名 称和本地(局部)子程序定义等。
  • 执行部分:是PLoraSQLs的功能实现部分,以关键字 BEGIN开始,以EXCEPTION或END结束。该部分通过变量赋值、流 程控制、数据查询、数据操纵、数据定义、事务控制、游标处理等操作实现块的功能。
  • 异常部分:以关键字EXCEPTION开始,以END结束。当执行部分发生异常时,会进入异常部分进行执行,否则不会进入异常部分进行。

存储过程

结构同匿名块,DECLARE是可选项,如下示例:

CREATE OR REPLACE PROCEDURE remove_emp (employee_id NUMBER) AS声明部分
BEGIN       执行部分
EXCEPTION   异常处理
END;
/

函数

结构同匿名块和存储过程,相比于存储过程,需要声明一个返回值类型,如下示例:

CREATE OR REPLACE FUNCTION func_name (arg int) RETURN NUMBER AS声明部分
BEGIN       执行部分
EXCEPTION   异常处理
END;
/

数据类型

在PL/SQL中声明变量、常量、参数、函数返回值时都需要指定数据类型,以确定数据的存储格式、有效取值以及可以进行操作的类型。

  • 内建数据类型
  • PL/SQL拓展数据类型
  1. PLS_INTEGER
    PLS_INTEGER 和 INT4 是完全相同的。PLS_INTEGER 存储有符号整数,范围介于 -2,147,483,648 到 2,147,483,647,占 32 个比特位。
  2. STRING
  • 用户自定义数据类型

控制语句

控制结构可能是PL/SQL中最有用(也是最重要)的部分。使用PL/SQL的控制结构,可以以非常灵活和强大的方式操纵LightDB数据。

条件语句

IF 条件语句

  • IF THEN 语句

语法结构

IF condition THENstatements
END IF;

示例

CREATE OR REPLACE PROCEDURE p_test(val int) AS
BEGINIF val < 0 THENDBMS_OUTPUT.PUT_LINE('val < 0');END IF;
END;
/
  • IF THEN ELSE 语句

语法结构

IF condition THENstatements
ELSEelse_statements
END IF;

示例

CREATE OR REPLACE PROCEDURE p_test(val int) AS
BEGINIF val < 0 THENDBMS_OUTPUT.PUT_LINE('val < 0');ELSEDBMS_OUTPUT.PUT_LINE('val >= 0');END IF;
END;
/
  • IF THEN ELSIF 语句

语法结构

IF condition_1 THENstatements_1
ELSIF condition_2 THENstatements_2
[ ELSIF condition_3 THENstatements_3
]...
[ ELSEelse_statements
]
END IF;

示例

CREATE OR REPLACE PROCEDURE p_test(val int) AS
BEGINIF val < 0 THENDBMS_OUTPUT.PUT_LINE('val < 0');ELSEIF val > 0 THENDBMS_OUTPUT.PUT_LINE('val > 0');ELSEDBMS_OUTPUT.PUT_LINE('val = 0');END IF;
END;
/

输出结果

lightdb@ora=# call p_test(-1);
val < 0
CALL
lightdb@ora=# call p_test(0);
val = 0
CALL
lightdb@ora=# call p_test(1);
val > 0
CALL

CASE条件语句

  • 简单CASE语句

语法结构

CASE search-expressionWHEN expression [, expression [ ... ]] THENstatements[ WHEN expression [, expression [ ... ]] THENstatements... ][ ELSEstatements ]
END CASE;

简单的CASE形式提供了基于操作数的相等性条件执行。 search-expression被评估(一次)并逐个与WHEN子句中的每个expression进行比较。 如果找到匹配项,则执行相应的statements,然后控制传递到 END CASE之后的下一条语句。(后续的WHEN表达式不会被评估。)如果找不到匹配项,则执行ELSE statements;但是如果没有ELSE,则会引发CASE_NOT_FOUND异常。

示例

CREATE OR REPLACE PROCEDURE print_grade(grade char) AS
BEGINCASE gradeWHEN 'A' THEN DBMS_OUTPUT.PUT_LINE('优秀');WHEN 'B' THEN DBMS_OUTPUT.PUT_LINE('良好');WHEN 'C' THEN DBMS_OUTPUT.PUT_LINE('及格');WHEN 'D' THEN DBMS_OUTPUT.PUT_LINE('不及格');ELSE DBMS_OUTPUT.PUT_LINE('没有名次');END CASE;
END;
/或CREATE OR REPLACE PROCEDURE print_grade(grade char) AS
BEGINCASE gradeWHEN 'A' THEN DBMS_OUTPUT.PUT_LINE('优秀');WHEN 'B' THEN DBMS_OUTPUT.PUT_LINE('良好');WHEN 'C' THEN DBMS_OUTPUT.PUT_LINE('及格');WHEN 'D' THEN DBMS_OUTPUT.PUT_LINE('不及格');END CASE;
EXCEPTIONWHEN CASE_NOT_FOUND THENDBMS_OUTPUT.PUT_LINE('没有名次');
END;
/

输出结果:

lightdb@ora=# CALL print_grade('A');
优秀
CALL
lightdb@ora=# CALL print_grade('b');
没有名次
CALL
lightdb@ora=# CALL print_grade('B');
良好
CALL
lightdb@ora=# CALL print_grade('C');
及格
CALL
lightdb@ora=# CALL print_grade('D');
不及格
CALL
lightdb@ora=# CALL print_grade('E');
没有名次
CALL
  • 搜索式CASE语句

语法结构

CASEWHEN boolean-expression THENstatements[ WHEN boolean-expression THENstatements... ][ ELSEstatements ]
END CASE;

CASE 的搜索形式根据布尔表达式的真值提供条件执行。 每个 WHEN 子句的 boolean-expression 会依次被评估, 直到找到一个产生 true 的表达式。然后执行相应的 statements, 然后控制权转移到 END CASE 后的下一条语句。 (后续的 WHEN 表达式不会被评估。) 如果找不到真实结果,则会执行 ELSE statements; 但是如果没有 ELSE,则会引发 CASE_NOT_FOUND 异常。

循环语句

简单LOOP

语法结构

[ <<label>> ]
LOOPstatements
END LOOP [ label ];

LOOP 定义了一个无条件循环,直到被 EXIT 或 RETURN 语句终止

示例

CREATE TABLE stduents(s_id integer, s_name varchar(64));
INSERT INTO stduents(s_id, s_name) VALUES(1, '张三');
INSERT INTO stduents(s_id, s_name) VALUES(2, '李四');DECLAREx NUMBER := 0;
BEGINLOOPDBMS_OUTPUT.PUT_LINE ('Inside loop:  x = ' || TO_CHAR(x));x := x + 1;IF x > 3 THENEXIT;END IF;END LOOP;-- After EXIT, control resumes hereDBMS_OUTPUT.PUT_LINE(' After loop:  x = ' || TO_CHAR(x));
END;
/

输出结果

Inside loop:  x = 0
Inside loop:  x = 1
Inside loop:  x = 2
Inside loop:  x = 3After loop:  x = 4
DO

FOR LOOP

语法结构

[ <<label>> ]
FOR name IN [ REVERSE ] expression .. expression [ BY expression ] LOOPstatements
END LOOP [ label ];

这种形式的FOR创建一个循环,遍历整数值的范围。 变量name会自动定义为integer类型, 仅在循环内存在(循环内会忽略任何现有的该变量的定义)。 指定范围的两个表达式在进入循环时只会计算一次。 如果没有指定BY子句,则迭代步长为1, 否则为BY子句中指定的值,该值也会在循环进入时计算一次。 如果指定了REVERSE,则在每次迭代后,步长值会被减去而不是加上。

示例

BEGINDBMS_OUTPUT.PUT_LINE ('lower_bound < upper_bound');FOR i IN 1..3 LOOPDBMS_OUTPUT.PUT_LINE (i);END LOOP;DBMS_OUTPUT.PUT_LINE ('lower_bound = upper_bound');FOR i IN 2..2 LOOPDBMS_OUTPUT.PUT_LINE (i);END LOOP;DBMS_OUTPUT.PUT_LINE ('lower_bound > upper_bound');FOR i IN REVERSE 3..1 LOOPDBMS_OUTPUT.PUT_LINE (i);END LOOP;
END;
/

输出结果

lower_bound < upper_bound
1
2
3
lower_bound = upper_bound
2
lower_bound > upper_bound
3
2
1
DO

WHILE循环

[ <<label>> ]
WHILE boolean-expression LOOPstatements
END LOOP [ label ];

WHILE 语句会重复执行一系列语句,只要 boolean-expression 评估为 true。该表达式在每次进入循环体之前进行检查。

示例

DECLAREdone  BOOLEAN := TRUE;
BEGINWHILE done LOOPDBMS_OUTPUT.PUT_LINE ('进入循环1');done := FALSE;  -- This assignment is not made.END LOOP;WHILE NOT done LOOPDBMS_OUTPUT.PUT_LINE ('进入循环2');done := TRUE;END LOOP;DBMS_OUTPUT.PUT_LINE('退出了循环');
END;
/

输出结果

进入循环1
进入循环2
退出了循环
DO

循环退出

  • EXIT
    EXIT语句无条件退出循环的当前迭代,并将控制权转移到当前循环或封闭标记循环的末尾。
  • EXIT WHEN
    当WHEN子句中的条件为true时,EXIT WHEN语句退出循环的当前迭代,并将控制权转移到当前循环或封闭标记循环的末尾。

循环继续

  • CONTINUE
    CONTINUE语句无条件退出循环的当前迭代,并将控制权转移到当前循环或封闭标记循环的下一次迭代。
  • CONTINUE WHEN
    当WHEN子句中的条件为true时,CONTINUE WHEN语句退出循环的当前迭代,并将控制权转移到当前循环或封闭标记循环的下一次迭代。

循序控制

  • GOTO语句
    GOTO语句无条件地将控制权转移到标签。标签在其作用域中必须是唯一的,并且必须位于可执行语句或PL/SQL块之前。运行时,GOTO语句将控制权转移到标记的语句或块。

  • NULL语句
    NULL语句只将控制权传递给下一个语句。有些语言将这样的指令称为no-op(无操作)。

示例 GOTO到NULL

DECLAREdone  BOOLEAN;
BEGINFOR i IN 1..50 LOOPDBMS_OUTPUT.PUT_LINE('i=' || i);IF i = 3 THENGOTO end_loop;END IF;END LOOP;<<end_loop>>NULL;
END;
/

利用GOTO 到NULL语句的标签来提前结束循环。

静态语句

静态SQL是一种PL/SQL功能,它允许在PL/SQL语句中直接使用SQL语法。

  • SELECT
    查询语句
  • INSERT
    插入语句
  • UPDATE
    更新语句
  • DELETE
    删除语句
  • MERGE
    MERGE语句
  • WITH
    WITH语句
  • COMMIT
    提交语句
  • ROLLBACK
    回滚语句
  • SAVEPOINT
    保存点语句
  • SET TRANSACTION
    设置事务隔离级别语句

示例1 - 增删改查

CREATE TABLE stduents(sid integer, sname varchar2(64), age integer);DECLAREv_id integer;v_name varchar2(64);v_age integer;v_count integer;
BEGIN-- 插入数据INSERT INTO stduents(sid, sname, age) VALUES(1, '张三', 18);INSERT INTO stduents(sid, sname, age) VALUES(2, '李四', 19);-- 查询数据SELECT COUNT(*) INTO v_count FROM stduents;DBMS_OUTPUT.PUT_LINE('总记录数:' || v_count);FOR i IN 1 .. 2 LOOPSELECT sname INTO v_name FROM stduents WHERE sid = i;DBMS_OUTPUT.PUT_LINE('sid=' || i || ' sname=' || v_name);UPDATE stduents SET age = age + i WHERE sid = i;END LOOP;FOR i IN 1 .. 2 LOOPSELECT age INTO v_age FROM stduents WHERE sid = i;DBMS_OUTPUT.PUT_LINE('sid=' || i || ' age=' || v_age);UPDATE stduents SET age = age + i WHERE sid = i;END LOOP;-- 删除数据DELETE FROM stduents;SELECT COUNT(*) INTO v_count FROM stduents;DBMS_OUTPUT.PUT_LINE('总记录数:' || v_count);END;
/

输出结果

总记录数:2
sid=1 sname=张三
sid=2 sname=李四
sid=1 age=19
sid=2 age=21
总记录数:0
DO

示例2 - MERGE使用

当使用MERGE语句时,你通常想将源表(people_source)中的数据与目标表(people_target)进行同步,如下用例:

CREATE TABLE people_source ( person_id  INTEGER NOT NULL PRIMARY KEY, name VARCHAR2(20) NOT NULL
);CREATE TABLE people_target ( person_id  INTEGER NOT NULL PRIMARY KEY, name VARCHAR2(20) NOT NULL
);INSERT INTO people_target VALUES (1, 'John Smith');
INSERT INTO people_target VALUES (2, 'alice jones');
INSERT INTO people_source VALUES (2, '张三');
INSERT INTO people_source VALUES (3, '李四');
INSERT INTO people_source VALUES (4, '王五');

匿名块更新

BEGINMERGE INTO people_target pt  USING (SELECT person_id, name FROM people_source) ps  ON (pt.person_id = ps.person_id)  WHEN MATCHED THEN  UPDATE SET pt.name = ps.name  WHEN NOT MATCHED THEN  INSERT (pt.person_id, pt.name) VALUES (ps.person_id, ps.name);
END;
/

查询更新结果

select * from people_source ;person_id | name 
-----------+------2 | 张三3 | 李四4 | 王五
(3 rows)select * from people_target;person_id |    name    
-----------+------------1 | John Smith2 | 张三4 | 王五3 | 李四
(4 rows)

动态语句

动态SQL是一种用于在运行时生成和运行SQL语句的编程方法。
当编写通用且灵活的程序(如特设查询系统)、编写必须运行数据库定义语言(DDL)语句的程序,或者在编译时不知道SQL语句的全文或其输入和输出变量的数量或数据类型时,它非常有用。

  • EXECUTE IMMEDIATE
    如果动态语句没有绑定变量的占位符那么EXECUTE IMMEDIATE不需要从句。

示例1 动态语句不包含占位符

DECLARE  table_name VARCHAR2(50) := 'my_table';  sql_stmt VARCHAR2(4000);  v_retrieved_name VARCHAR2(100);  
BEGIN  -- 构建创建表的SQL语句  sql_stmt := 'CREATE TABLE ' || table_name || ' ( ' ||  'ID NUMBER PRIMARY KEY, ' ||  'NAME VARCHAR2(100), ' ||  'CREATED_DATE DATE ' ||  ')';  -- 执行创建表的SQL语句  EXECUTE IMMEDIATE sql_stmt;  -- 构建插入数据的SQL语句(硬编码值)  sql_stmt := 'INSERT INTO ' || table_name || ' (ID, NAME, CREATED_DATE) VALUES (1, ''张三'', SYSDATE)';  -- 执行插入数据的SQL语句  EXECUTE IMMEDIATE sql_stmt;  -- 输出插入成功信息  DBMS_OUTPUT.PUT_LINE('数据插入成功。');  -- 构建查询数据的SQL语句(硬编码ID)  sql_stmt := 'SELECT NAME FROM ' || table_name || ' WHERE ID = 1';  -- 执行查询数据的SQL语句,并将结果存储在变量中  EXECUTE IMMEDIATE sql_stmt INTO v_retrieved_name;  -- 输出查询到的数据  DBMS_OUTPUT.PUT_LINE('从表中检索到的名字是: ' || v_retrieved_name);  EXCEPTION  WHEN OTHERS THEN  -- 处理异常  DBMS_OUTPUT.PUT_LINE('发生错误: ' || SQLERRM);  
END;  
/

示例2 动态语句包含占位符

CREATE TABLE employees (  emp_id NUMBER PRIMARY KEY,  first_name VARCHAR2(50),  last_name VARCHAR2(50),  hire_date DATE  
);DECLARE  v_emp_id NUMBER := 1001;  v_first_name VARCHAR2(50) := 'John';  v_last_name VARCHAR2(50) := 'Doe';  v_hire_date DATE := SYSDATE;  
BEGIN  EXECUTE IMMEDIATE 'INSERT INTO employees (emp_id, first_name, last_name, hire_date) VALUES (:1, :2, :3, :4)'  USING v_emp_id, v_first_name, v_last_name, v_hire_date;  
END; 
/

输出结果

select * from employees;emp_id | first_name | last_name |      hire_date      
--------+------------+-----------+---------------------1001 | John       | Doe       | 2024-05-31 00:00:00

在这个示例中,:1、:2、:3和:4是占位符,它们分别被USING子句中的v_emp_id、v_first_name、v_last_name和v_hire_date变量替换。这种方法允许你在动态SQL中安全地插入数据,因为它可以防止SQL注入攻击。

注意:在实际应用中,确保你的变量和占位符的数量和类型匹配,否则你会遇到运行时错误。

示例3 动态语句检索值

DECLAREv_emp_id NUMBER;  v_first_name VARCHAR2(50);  v_last_name VARCHAR2(50);  v_hire_date DATE;      v_sql VARCHAR2(1024);v_emp employees%rowtype;
BEGINv_sql := 'select emp_id, first_name, last_name, hire_date from employees where emp_id = :1';execute immediate v_sql into v_emp_id, v_first_name, v_last_name, v_hire_date using 1001;dbms_output.put_line('emp_id=' || v_emp_id || ' first_name=' || v_first_name || ' last_name=' || v_last_name || ' hire_date=' || v_hire_date);execute immediate v_sql into v_emp using 1001;dbms_output.put_line('emp_id=' || v_emp.emp_id || ' first_name=' || v_emp.first_name || ' last_name=' || v_emp.last_name || ' hire_date=' || v_emp.hire_date);execute immediate v_sql into v_emp_id, v_first_name, v_last_name, v_hire_date using 1002;dbms_output.put_line('emp_id=' || v_emp_id || ' first_name=' || v_first_name || ' last_name=' || v_last_name || ' hire_date=' || v_hire_date);EXCEPTIONWHEN NO_DATA_FOUND THENDBMS_OUTPUT.PUT_LINE('未找到数据, errcode=' || sqlcode || ' errmsg=' || sqlerrm );
END;
/

游标

隐式游标

隐式游标是由PL/SQL构建和管理的会话游标。每次运行SELECT或DML语句时,PL/SQL都会打开一个隐式游标。您无法控制隐式游标,但可以从其属性获取信息。

隐式游标属性

变量名说明
SQL%ISOPEN总是返回FALSE,因为一个隐式游标在关联的语句执行完后总是会关闭
SQL%FOUND如果没有执行SELECT或者DML语句则为NULL,如果最近执行的SELECT或者DML语句返回了行记录则为TRUE,如果最近执行的SELECT或者DML语句没有返回行记录则为FALSE
SQL%NOTFOUND如果没有执行SELECT或者DML语句则为NULL,如果最近执行的SELECT或者DML语句返回了行记录则为FALSE,如果最近执行的SELECT或者DML语句没有返回行记录则为TRUE
SQL%ROWCOUNT如果没有执行SELECT或者DML语句)则为NULL,如果最近执行的SELECT或者DML语句返回了行记录则为获取的行记录数

示例

BEGINIF SQL%ISOPEN THENDBMS_OUTPUT.PUT_LINE('isopen is true');ELSEDBMS_OUTPUT.PUT_LINE('isopen is false');END IF;
END;
/BEGINIF SQL%FOUND THENDBMS_OUTPUT.PUT_LINE('found is true');ELSIF SQL%FOUND IS NULL THENDBMS_OUTPUT.PUT_LINE('found is null');ELSEDBMS_OUTPUT.PUT_LINE('found is false');END IF;
END;
/BEGINIF SQL%NOTFOUND THENDBMS_OUTPUT.PUT_LINE('notfound is true');ELSIF SQL%NOTFOUND IS NULL THENDBMS_OUTPUT.PUT_LINE('notfound is null');ELSEDBMS_OUTPUT.PUT_LINE('notfound is false');END IF;
END;
/DECLAREv_num integer;
BEGINv_num := SQL%ROWCOUNT;DBMS_OUTPUT.PUT_LINE('rowcount is ' || v_num);
END;
/DECLAREv_num integer;v_emp employees%rowtype;
BEGINv_num := SQL%ROWCOUNT;DBMS_OUTPUT.PUT_LINE('select 之前 rowcount 值是 ' || v_num);SELECT * INTO v_emp FROM employees WHERE emp_id = 1001;v_num := SQL%ROWCOUNT;DBMS_OUTPUT.PUT_LINE('select 之后 rowcount 值是 ' || v_num);
END;
/

注意: 使用隐式游标属性和SELECT或DML语句之间不要插入DBMS_OUTPUT.PUT_LINE指令,这个指令在LightDB中会影响隐式游标属性值。

隐式游标的异常处理

查询语句中找不到与我们条件相匹配的行,在这里情况下,数据库将引发名为NO_DATA_FOUND的异常

查询语句返回多行情况下,数据库将引发为TO_MANY_ROWS的异常

示例1 NO_DATA_FOUND的异常

select * from employees;emp_id | first_name | last_name |      hire_date      
--------+------------+-----------+---------------------1002 | John2      | Doe2      | 2024-06-01 04:31:301001 | John       | Doe       | 2024-06-01 00:00:00DECLAREv_emp employees%rowtype;
BEGINSELECT * INTO v_emp FROM employees WHERE emp_id = 1003;
EXCEPTIONWHEN NO_DATA_FOUND THENDBMS_OUTPUT.PUT_LINE('不能找到数据');WHEN TOO_MANY_ROWS THENDBMS_OUTPUT.PUT_LINE('找到的记录数大多');WHEN OTHERS THENDBMS_OUTPUT.PUT_LINE('未知错误');
END;
/

输出结果

不能找到数据

示例2 TO_MANY_ROWS的异常

DECLAREv_emp employees%rowtype;
BEGINSELECT * INTO v_emp FROM employees;
EXCEPTIONWHEN NO_DATA_FOUND THENDBMS_OUTPUT.PUT_LINE('不能找到数据');WHEN TOO_MANY_ROWS THENDBMS_OUTPUT.PUT_LINE('找到的记录数大多');WHEN OTHERS THENDBMS_OUTPUT.PUT_LINE('未知错误');
END;
/

输出结果

找到的记录数大多

显式游标

显式游标是一种会话游标,由您构造和管理。 您必须声明和定义一个显式游标,给它起一个名称并将其与一个查询关联(通常,查询会返回多行)。 然后,您可以通过以下两种方式之一处理查询结果集:

  • open 显式游标,从结果集中 fetch 行,并 close 显式游标。
  • 在游标 FOR LOOP 语句中使用显式游标。

声明显式游标

您可以先声明显式游标,然后在同一块、子程序或包中稍后定义它,也可以同时声明和定义它。
显式游标声明,仅声明游标,其语法如下:

               CURSOR cursor_name parameter_list RETURN return_type;

要实际使用显式游标,可以直接定义它而不声明它。
在下面的示例中,声明并定义了三个显式游标:

DECLARECURSOR c1 RETURN employees%ROWTYPE;    -- 声明 c1CURSOR c2 IS                             -- 声明并定义 c2SELECT emp_id,first_name, last_name, hire_date s FROM employeesWHERE emp_id > 2000; CURSOR c1 RETURN employees%ROWTYPE IS  -- 定义 c1,SELECT emp_id,first_name, last_name, hire_date s FROM employeesWHERE emp_id = 110;CURSOR c3 RETURN employees%ROWTYPE;      -- 声明 c3CURSOR c3 IS                             -- 定义 c3,SELECT emp_id,first_name, last_name, hire_date s FROM employeesWHERE emp_id = 110;
BEGINNULL;
END;
/

打开显式游标

声明并定义了显式游标后,可以使用OPEN语句打开它。游标名忽略大小写。 定义的游标不带参数,打开时也可以使用游标名加()的形式。
下面是打开游标的几种方式:

open C_tmp;
open c_tmp;
open c_tmp();

使用显式游标获取数据

打开显式游标后,可以使用FETCH语句获取查询结果集的行。返回一行的FETCH语句的基本语法如下:

FETCH cursor_name INTO into_clause;

into_clause 是变量列表或单个记录变量。 对于查询返回的每一列,变量列表或记录必须有一个相应的类型兼容变量或字段。 在声明用于FETCH语句的变量和记录时,%TYPE和%ROWTYPE属性非常有用。

FETCH语句检索结果集的当前行,将该行的列值存储到变量或记录中,并将游标移动到下一行。

通常,您在LOOP语句中使用FETCH语句,当 FETCH语句运行完所有行时退出循环。 要检测此退出条件,请使用游标属性%NOTFOUND。当FETCH语句返回没有行时,PL/SQL不会引发异常。

关闭显式游标

使用CLOSE语句关闭打开的显式游标,从而允许重用其资源。关闭游标后,无法从其结果集中获取记录或引用其属性。

close c_tmp;

显式游标属性

同隐式游标属性,游标名%isopen游标名%found游标名%notfound游标名%rowcount

示例1 无参数显式游标

DECLAREv_emp employees%rowtype;CURSOR cur IS select emp_id,first_name,last_name,hire_date from employees order by emp_id asc;
BEGINIF NOT cur%isOPEN THENOPEN cur;END IF;LOOPFETCH cur INTO v_emp;EXIT WHEN cur%NOTFOUND;DBMS_OUTPUT.PUT_LINE('emp_id=' || v_emp.emp_id || ' first_name=' || v_emp.first_name || ' last_name=' || v_emp.last_name || ' hire_date' || v_emp.hire_date);END LOOP;CLOSE cur;DBMS_OUTPUT.PUT_LINE('查询结束');
END;
/

输出结果

emp_id=1001 first_name=John last_name=Doe hire_date2024-06-01 00:00:00
emp_id=1002 first_name=John2 last_name=Doe2 hire_date2024-06-01 04:31:30
查询结束

示例2 带参数的显示游标

DECLAREv_emp employees%rowtype;CURSOR cur(v_id number) IS select emp_id,first_name,last_name,hire_date from employees where emp_id = v_id order by emp_id asc;
BEGINIF NOT cur%isOPEN THENOPEN cur(1001);END IF;LOOPFETCH cur INTO v_emp;EXIT WHEN cur%NOTFOUND;DBMS_OUTPUT.PUT_LINE('emp_id=' || v_emp.emp_id || ' first_name=' || v_emp.first_name || ' last_name=' || v_emp.last_name || ' hire_date' || v_emp.hire_date);END LOOP;CLOSE cur;DBMS_OUTPUT.PUT_LINE('查询结束');
END;
/

输出结果

emp_id=1001 first_name=John last_name=Doe hire_date2024-06-01 00:00:00
查询结束
DO

游标变量

游标变量类似于显式游标,但具有以下特点:
它不限于一个查询。 您可以为一个查询打开一个游标变量,处理结果集,然后将游标变量用于另一个查询。

  • 您可以为它赋值。
  • 您可以在表达式中使用它。
  • 它可以是子程序参数。 您可以使用游标变量在子程序之间传递查询结果集。
  • 它可以是宿主变量。
  • 您可以使用游标变量在PL/SQL存储的子程序和它们的客户端之间传递查询结果集。
  • 它无法接受参数。 您不能向游标变量传递参数,但可以将整个查询传递给它。 查询可以包括变量。

创建游标变量

要创建游标变量,请声明一个预定义类型 SYS_REFCURSOR 的变量, 或者定义一个 REF CURSOR 类型,然后声明一个该类型的变量。
REF CURSOR 类型定义的基本语法如下:

         TYPE type_name IS REF CURSOR RETURN return_type%TYPE

游标变量声明。例如:

DECLARETYPE empcurtyp IS REF CURSOR RETURN employees%ROWTYPE;cursor1  empcurtyp;
BEGINNULL;
END;
/

具有用户定义返回类型的游标变量。例如:

DECLARETYPE EmpRecTyp IS RECORD (employee_id NUMBER,last_name VARCHAR2(25),salary   NUMBER(8,2));TYPE EmpCurTyp IS REF CURSOR RETURN EmpRecTyp;emp_cv EmpCurTyp;
BEGINNULL;
END;
/

使用sys_refcursor作为参数变量。例如:

CREATE OR REPLACE PROCEDURE p_getemployees(cur OUT sys_refcursor) IS
BEGINOPEN cur FOR select emp_id,first_name,last_name,hire_date from employees order by emp_id asc;
END;
/DECLAREcur sys_refcursor;v_emp employees%rowtype;
BEGINp_getemployees(cur);LOOPFETCH cur INTO v_emp;EXIT WHEN cur%NOTFOUND;DBMS_OUTPUT.PUT_LINE('emp_id=' || v_emp.emp_id || ' first_name=' || v_emp.first_name || ' last_name=' || v_emp.last_name || ' hire_date' || v_emp.hire_date);END LOOP;CLOSE cur;DBMS_OUTPUT.PUT_LINE('查询结束');
END;
/

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

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

相关文章

【CesiumJS入门】(12)Vite+Vue3+Cesium 简易安装与配置

步骤 vite 创建项目&#xff1a;yarn create vite安装 Cesium&#xff1a;yarn add cesium安装 vite-plugin-static-copy&#xff1a;yarn add -D vite-plugin-static-copy 配置 vite.config.js &#xff1a; import { defineConfig } from "vite"; import vue fro…

制作自己的 ButterKnife(使用 AutoService 和 APT 注解处理器在编译期生成 Java 代码)

ButterKnife 开发过 Android 的肯定都知道曾经有这么一个库&#xff0c;它能够让你不用再写 findViewById 这样的代码&#xff0c;这就是大名鼎鼎的 ButterKnife&#xff08;https://github.com/JakeWharton/butterknife&#xff09;。虽然现在这个库已经不再维护&#xff0c;…

LabVIEW实现汽车逆变器功能测试系统

​介绍了如何利用LabVIEW开发汽车逆变器&#xff08;包括功率板和控制板&#xff09;的自动测试设备&#xff08;ATE&#xff09;&#xff0c;实现对额定800V电压、300A电流的逆变器进行功能测试。系统通过CAN2.0通讯协议&#xff0c;实现电机控制、温度传感器监测、电压校验和…

Java程序策——Java连接数据库保姆级教程(超详细步骤)

【Java程序策】——连接数据库 目录 ​编辑 一&#xff1a;在数据库中建立一个表&#xff08;student表&#xff09; 1.1&#xff1a;进入mysql 1.2&#xff1a;建立一个“数据库成员” 1.3&#xff1a;建立一个表&#xff08;student表&#xff09; 1.4&#xff1a;给表…

pytorch学习day5

一、权值初始化 在深度学习中&#xff0c;权值初始化是神经网络训练中关键的一步。正确的权值初始化可以帮助加速收敛速度&#xff0c;提高模型的稳定性和泛化能力。在PyTorch中&#xff0c;我们可以通过torch.nn.init模块中的函数来实现不同的权值初始化方法。 1.常规初始化 P…

算法刷题笔记 判断子序列(C++实现)

文章目录 题目描述基本思路实现代码 题目描述 给定一个长度为n的整数序列a1,a2,…,an以及一个长度为m的整数序列b1,b2,…,bm。请你判断a序列是否为b序列的子序列。子序列指序列的一部分项按原有次序排列而得的序列&#xff0c;例如序列{a1,a3,a5}是序列{a1,a2,a3,a4,a5}的一个…

MATLAB 函数 function

函数定义函数调用局部函数匿名函数函数句柄子函数函数文件的位置函数的文档函数的参数函数的返回值总结 在 MATLAB中&#xff0c;函数是一个执行特定任务的代码块&#xff0c;可以被重复调用。 MATLAB函数可以执行计算、数据操作、文件处理等任务&#xff0c;并且可以接收输入…

AI市场驱动HBM持续爆火

随着人工智能市场的蓬勃发展&#xff0c;对AI处理器的需求激增&#xff0c;内存制造巨头正积极扩大高带宽内存&#xff08;HBM&#xff09;的产能&#xff0c;并努力提高其良率和竞争力。最新的动态是美光在日本广岛县计划建设新工厂&#xff0c;旨在最早于2027年开始生产芯片和…

关于 spring boot 的目录详解和配置文件

目录 配置文件 spring boot 的配置文件有两种格式&#xff0c;分别是 properties 和 yml&#xff08;yaml&#xff09;。这两种格式的配置文件是可以同时存在的&#xff0c;此时会以 properties 的文件为主&#xff0c;但一般都是使用同一种格式的。 格式 properties 语法格…

36. 【Java教程】输入输出流

本小节将会介绍基本输入输出的 Java 标准类&#xff0c;通过本小节的学习&#xff0c;你将了解到什么是输入和输入&#xff0c;什么是流&#xff1b;输入输出流的应用场景&#xff0c;File类的使用&#xff0c;什么是文件&#xff0c;Java 提供的输入输出流相关 API 等内容。 1…

C#之位运算符

位运算符 位运算符 主要是用于数值类型计算运算方式: 将数值转换为二进制 在进行位运算返回的是一个二进制转十进制的数值如果二进制数 不够位置 则在前面补零 位与 & 符号 &#xff1a;& 链接两个数值进行运算 将数值转换为二进制 对位运算 有1则1 用法: 先将a和…

逆天面试题-

扪心自问&#xff0c;不借助第三变量实现两个变量value的交换&#xff0c;你怎么做&#xff1f; 3 2 1 看两种思路&#xff1a; 一.联想运算 不多说&#xff0c;试着去感受它的奇妙 #include<iostream> using namespace std; int main(){int a 3;int b 5;a a …

eNSP学习——OSPF的DR与BDR

目录 相关命令 原理概述 实验内容 实验目的 实验拓扑 实验编址 实验步骤 1、基本配置 2、搭建基本的OSPF网络 3、查看默认情况下的DR/BDR状态 4、根据现网需求影响DR/BDR选举 需要eNSP各种配置命令的点击链接自取&#xff1a;华为&#xff45;NSP各种设备配置命令大…

小白级教程—安装Ubuntu 20.04 LTS服务器

下载 本教程将使用20.04版进行教学 由于官方速度可能有点慢&#xff0c;可以下方的使用清华镜像下载 https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/ 点击20.24版本 选择 ubuntu-20.04.6-live-server-amd64.iso 新建虚拟机 下载好后 我们使用 VMware 打开它 这里选…

数组的详细介绍

数组是一组相同类型元素的集合&#xff0c;也就是说&#xff1a;数组至少包含两个及以上的元素&#xff0c;且元素类型相同。 数组包括一维数组和多维数组&#xff0c;其中二维数组最常见。下面我们一一介绍。 一维数组&#xff1a; 格式&#xff1a;type name [常量值]&…

PHP中使用RabbitMQ实现异步发送电子邮件可以显著提升应用的表现,尤其是在需要大量发送邮件的场景下

前提条件 安装RabbitMQ服务。 安装PHP的RabbitMQ客户端库&#xff08;php-amqplib&#xff09;。 安装邮件发送库&#xff08;如PHPMailer&#xff09;。 安装php-amqplib和PHPMailer 首先&#xff0c;通过Composer安装这些库&#xff1a; composer require php-amqplib/…

2024开放式耳机怎么买才好?这里可以教你六招!

有不少人都在说“开放式蓝牙耳机无音质”&#xff0c;大多数的购买者往往既贪恋蓝牙耳机的便携性&#xff0c;又想要有线耳机的Hifi快感&#xff0c;对于我们来说最重要的就是确定预算和需求&#xff0c;这样才能定位到最适合自己的开放式蓝牙耳机。这么多年零零总总听下来的蓝…

前端面试题日常练-day51 【面试题】

题目 希望这些选择题能够帮助您进行前端面试的准备&#xff0c;答案在文末 1. 在PHP中&#xff0c;以下哪个函数用于向浏览器发送一个HTTP头部&#xff1f; a) header() b) send() c) echo() d) print() 2. 哪个PHP函数用于将字符串转换为整数&#xff1f; a) stringToInt…

【python】成功解决“ModuleNotFoundError: No module named ‘IPython’”错误的全面指南

成功解决“ModuleNotFoundError: No module named IPython’”错误的全面指南 一、引言 在Python编程中&#xff0c;ModuleNotFoundError是一种常见的错误类型&#xff0c;它通常表明Python解释器无法找到你试图导入的模块。特别是当你遇到“ModuleNotFoundError: No module…

求助!什么软件可以人声分离?手机上可以进行人声分离操作吗?

在数字时代&#xff0c;音频处理变得越来越重要&#xff0c;而人声分离技术则是其中的一项关键技术。很多人可能都有过这样的疑问&#xff1a;什么软件可以实现人声分离&#xff1f;手机上能否进行人声分离操作&#xff1f;今天&#xff0c;我们就来为大家解答这些问题&#xf…