Oracle数据库中的动态SQL(Dynamic SQL)

devtools/2024/9/24 2:06:47/

Oracle数据库中的动态SQL是一种在运行时构建和执行SQL语句的技术。与传统的静态SQL(在编写程序时SQL语句就已经确定)不同,动态SQL允许开发者在程序执行过程中根据不同的条件或用户输入来构建SQL语句。这使得动态SQL在处理复杂查询、存储过程中灵活处理未知或变化的数据结构时非常有用。

1、 动态SQL的类型

Oracle中动态SQL主要有两种形式:

  1. 本地动态SQL(Native Dynamic SQL)

    • 使用EXECUTE IMMEDIATE语句来执行单个的SQL语句或PL/SQL匿名块。
    • 主要用于执行不需要返回结果的SQL语句,如INSERT、UPDATE、DELETE、DDL(数据定义语言)语句等。
    • 也可以使用INTO子句将查询结果存储在PL/SQL变量中。
  2. DBMS_SQL包

    • 提供了一套用于执行动态SQL语句的接口,允许执行更复杂的动态SQL,包括查询和DML操作。
    • 可以处理游标和绑定变量,使得处理查询结果集和参数化查询成为可能。
    • 使用步骤包括打开游标、绑定变量、执行SQL语句、处理结果集(如果有的话)、关闭游标。

2、 使用EXECUTE IMMEDIATE

EXECUTE IMMEDIATE语句非常适合执行简单的动态SQL语句,如:

2.1、从动态PL/SQL块调用子程序

Invoking Subprogram from Dynamic PL/SQL Block

In this example, the dynamic PL/SQL block is an anonymous PL/SQL block that invokes a subprogram created at schema level.

sql">create or replace procedure sp_insert_dept
( deptid in out number,dname in varchar2,mgrid in number,locid in number
) authid definer as
begindeptid := departments_seq.nextval;insert into departments (DEPARTMENT_ID,DEPARTMENT_NAME,MANAGER_ID, LOCATION_ID) values(deptid,dname,mgrid,locid);commit;
end;
/--  定义输入参数并执行
DECLAREplsql_block VARCHAR2(500);new_deptid  NUMBER(4);new_dname   VARCHAR2(30) := 'super';new_mgrid   NUMBER(6)    := 200;new_locid   NUMBER(4)    := 1700;
BEGIN-- Dynamic PL/SQL block invokes subprogram:plsql_block := 'BEGIN sp_insert_dept(:a, :b, :c, :d); END;';/* Specify bind variables in USING clause.Specify mode for first parameter.Modes of other parameters are correct by default. */EXECUTE IMMEDIATE plsql_block USING IN OUT new_deptid, new_dname, new_mgrid, new_locid;
END;
/
-- 执行结果  
deptid = 280 new_dname= dbms_output.put_line new_mgrid= 202 new_locid= 3200PL/SQL procedure successfully completed.-- 检查表数据
HR@192.168.80.190:1521/racdb> select * from departments;DEPARTMENT_ID DEPARTMENT_NAME                MANAGER_ID LOCATION_ID
------------- ------------------------------ ---------- -----------10 Administration                        200        170020 Marketing                             201        180030 Purchasing                            114        170040 Human Resources                       203        240050 Shipping                              121        1500。。。。。。。。中间省略    。。。。。。。。。。。。。280 super                                 200        1700

2.2、用BOOLEAN形式参数动态调用子程序

Dynamically Invoking Subprogram with BOOLEAN Formal Parameter

In this example, the dynamic PL/SQL block is an anonymous PL/SQL block that invokes a subprogram that has a formal parameter of the PL/SQL data type BOOLEAN.

sql">CREATE OR REPLACE PROCEDURE sp_test_boolean (x BOOLEAN) AUTHID DEFINER AS
BEGINIF x THENDBMS_OUTPUT.PUT_LINE('x is true');END IF;
END;
/DECLAREdyn_stmt VARCHAR2(200);b        BOOLEAN := TRUE;
BEGINdyn_stmt := 'BEGIN sp_test_boolean(:x); END;';EXECUTE IMMEDIATE dyn_stmt USING b;
END;
/
-- 执行结果
x is truePL/SQL procedure successfully completed.
-- 注意执行成功的数据库版本oracle19c
-- 测试如果是oracle11g的环境会报错
ERROR at line 6:
ORA-06550: line 6, column 36:
PLS-00457: expressions have to be of SQL types
ORA-06550: line 6, column 3:
PL/SQL: Statement ignored

2.3、用RECORD形式参数动态调用子程序

Dynamically Invoking Subprogram with RECORD Formal Parameter

In this example, the dynamic PL/SQL block is an anonymous PL/SQL block that invokes a subprogram that has a formal parameter of the PL/SQL (but not SQL) data type RECORD. The record type is declared in a package specification, and the subprogram is declared in the package specification and defined in the package body.

sql">CREATE OR REPLACE PACKAGE pkg_record_datatype 
AUTHID DEFINER 
ASTYPE rec IS RECORD (n1 NUMBER, n2 NUMBER);PROCEDURE sp_record_datatype (x OUT rec, y NUMBER, z NUMBER);
END pkg_record_datatype ;
/CREATE OR REPLACE PACKAGE BODY pkg_record_datatype 
ASPROCEDURE sp_record_datatype (x OUT rec, y NUMBER, z NUMBER) ASBEGINx.n1 := y;x.n2 := z;END sp_record_datatype ;
END pkg_record_datatype ;
/DECLAREr       pkg_record_datatype.rec;dyn_str VARCHAR2(3000);
BEGINdyn_str := 'BEGIN pkg_record_datatype.sp_record_datatype(:x, 100, 1008); END;';EXECUTE IMMEDIATE dyn_str USING OUT r;DBMS_OUTPUT.PUT_LINE('r.n1 = ' || r.n1);DBMS_OUTPUT.PUT_LINE('r.n2 = ' || r.n2);
END;
/

执行结果 – 注意(oracle19c版本),oracle11g依旧报错

sql">r.n1 = 100
r.n2 = 1008

3、 使用DBMS_SQL包

DBMS_SQL包用于执行更复杂的动态SQL,包括查询和需要处理结果集的DML操作。以下是使用DBMS_SQL包的基本步骤:

3.1、DBMS_SQL.RETURN_RESULT Procedure

In this example, the procedure p invokes DBMS_SQL.RETURN_RESULT without the optional to_client parameter (which is TRUE by default). Therefore, DBMS_SQL.RETURN_RESULT returns the query result to the subprogram client (the anonymous block that invokes p). After p returns a result to the anonymous block, only the anonymous block can access that result.

sql">CREATE OR REPLACE PROCEDURE sp_dbms_sql_test AUTHID DEFINER ASc1 SYS_REFCURSOR;c2 SYS_REFCURSOR;
BEGINOPEN c1 FORSELECT first_name, last_nameFROM employeesWHERE employee_id = 176;DBMS_SQL.RETURN_RESULT (c1);-- Now p cannot access the result.OPEN c2 FORSELECT city, state_provinceFROM locationsWHERE country_id = 'AU';DBMS_SQL.RETURN_RESULT (c2);-- Now p cannot access the result.
END;
/BEGINsp_dbms_sql_test ;
END;
/

执行结果

sql">ResultSet #1FIRST_NAME           LAST_NAME
-------------------- -------------------------
Jonathon             TaylorResultSet #2CITY                           STATE_PROVINCE
------------------------------ -------------------------
Sydney                         New South Wales

注意:Oracle12c中PL/SQL(DBMS_SQL)新特性之隐式语句结果,DBMS_SQL.RETURN_RESULT隐式返回查询结果,Oracle11g执行上面的示例会报错,不支持RETURN_RESULT。

动态SQL为Oracle数据库应用提供了极大的灵活性和功能,但使用时也需要注意SQL注入等安全问题。因此,在处理用户输入时,应该使用参数化查询或适当的输入验证来防止潜在的安全风险。

3.2、DBMS_SQL.GET_NEXT_RESULT

希望通过客户端应用来处理这些结果集,这可以通过DBMS_SQL包的 GET_NEXT_RESULT过程来解决

sql">DECLAREl_sql_cursor    PLS_INTEGER;l_ref_cursor    SYS_REFCURSOR;l_return        PLS_INTEGER;l_col_cnt       PLS_INTEGER;l_desc_tab      DBMS_SQL.desc_tab;l_count         NUMBER;l_EMPLOYEE_ID   EMPLOYEES.EMPLOYEE_ID%TYPE;l_FIRST_NAME    EMPLOYEES.FIRST_NAME%TYPE;l_LAST_NAME    EMPLOYEES.LAST_NAME%TYPE;
BEGIN-- 执行过程l_sql_cursor := DBMS_SQL.open_cursor(treat_as_client_for_results => TRUE);DBMS_SQL.parse(c             => l_sql_cursor,statement     => 'BEGIN get_result_emp(30); END;',language_flag => DBMS_SQL.native);l_return := DBMS_SQL.execute(l_sql_cursor);-- 循环遍历每个结果集LOOP-- 获取下个结果集BEGINDBMS_SQL.get_next_result(l_sql_cursor, l_ref_cursor);EXCEPTIONWHEN NO_DATA_FOUND THENEXIT;END;-- 检查结果集列数l_return := DBMS_SQL.to_cursor_number(l_ref_cursor);DBMS_SQL.describe_columns (l_return, l_col_cnt, l_desc_tab);l_ref_cursor := DBMS_SQL.to_refcursor(l_return);-- 根据列数处理结果集CASE l_col_cntWHEN 1 THENDBMS_OUTPUT.put_line('The column is COUNT:');FETCH l_ref_cursorINTO  l_count;DBMS_OUTPUT.put_line('l_count=' || l_count);CLOSE l_ref_cursor;WHEN 3 THENDBMS_OUTPUT.put_line('The columns are EMPLOYEE_ID and FIRST_NAME and l_LAST_NAME:');LOOPFETCH l_ref_cursorINTO  l_EMPLOYEE_ID, l_FIRST_NAME,l_LAST_NAME;EXIT WHEN l_ref_cursor%NOTFOUND;DBMS_OUTPUT.put_line('l_EMPLOYEE_ID=' || to_char(l_EMPLOYEE_ID) || CHR(9) || 'l_FIRST_NAME=' || l_FIRST_NAME || CHR(9)|| 'l_LAST_NAME=' || l_LAST_NAME);END LOOP;CLOSE l_ref_cursor;ELSEDBMS_OUTPUT.put_Line('I wasn''t expecting that!');END CASE;END LOOP;
END;
/

执行结果

sql">The columns are EMPLOYEE_ID and FIRST_NAME and l_LAST_NAME:
l_EMPLOYEE_ID=114       l_FIRST_NAME=Den        l_LAST_NAME=Raphaely
l_EMPLOYEE_ID=115       l_FIRST_NAME=Alexander  l_LAST_NAME=Khoo
l_EMPLOYEE_ID=116       l_FIRST_NAME=Shelli     l_LAST_NAME=Baida
l_EMPLOYEE_ID=117       l_FIRST_NAME=Sigal      l_LAST_NAME=Tobias
l_EMPLOYEE_ID=118       l_FIRST_NAME=Guy        l_LAST_NAME=Himuro
l_EMPLOYEE_ID=119       l_FIRST_NAME=Karen      l_LAST_NAME=Colmenares
The column is COUNT:
l_count=107PL/SQL procedure successfully completed.

http://www.ppmy.cn/devtools/113087.html

相关文章

【系统分析师】-安全体系

考点综述 (1)综合知识:包括加密密钥和公开密钥算法:计算机病毒及防治技术计算机犯罪基本概念与防范措施、入侵检测与防范、系统访问控制技术;信息删除、修改、插入和丢失;伪造与重放攻击的防止;SHA、MD5;私有信息保护。 (2)案例:根据具体案例,说明在网络与分布式环境…

从Prompt到创造:解锁AI的无限潜能

文章目录 🍊AI内容创作核心:提示词Prompt1 什么是提示词工程?1.1 提示词的原理是什么?1.2 提示词工程师:百万年薪的职业?1.3 谁都能成为提示词工程师吗? 2 提示词书写的基本技巧3 常见的提示词框架3.1 CO-…

Vue3实现打印功能

1、安装插件 npm i vue3-print-nb --save 2、main.js全局配置 import print from vue3-print-nb app.use(print) 3、设置打印区域 为打印区域设置 id 选择器 <div id"printData"><el-table border :data"tableData" style"width: 100%…

安装配置filebrowser

安装配置filebrowser ​ 这章就简单搞个工具用一下&#xff0c;这个工具就是一个像安卓软件一样的文件浏览器&#xff0c;可以设置用户权限啥的&#xff0c;挺好用的下面直接粘的安装步骤&#xff0c;注意一下配置别错了就行&#xff0c;json文件和命令配置要一样。访问效果放…

兴趣推送与相似推送逻辑设计

兴趣推送 这里的兴趣推送不涉及大数据&#xff0c;或者大模型的兴趣推送&#xff0c;而是从内容标签的角度去构建用户画像/模型达到的兴趣推送。 内容标签 如果要根据某个用户的模型去选择内容标签&#xff0c;再到对应的内容标签集合中推送具体的内容id&#xff0c;就需要知…

告别繁琐粘贴,CleanClip Mac 版,让复制粘贴变得简单快捷!粘贴队列功能太强大了!

告别繁琐粘贴&#xff0c;CleanClip Mac 版&#xff0c;让复制粘贴变得简单快捷&#xff01; CleanClip for Mac &#x1f4cb; 是一款专为Mac用户设计的高效剪贴板管理工具。它解决了传统复制粘贴过程中的繁琐问题&#xff0c;让你的工作流程更加顺畅和高效。 &#x1f504;…

STM32单片机 内存 字 字节 位关系详细讲解

1、简介 STM32结合Freertos、ucous等操作系统后,经常需要考虑内存管理问题,但是对于小白来说,经常搞不清内存的关系,本文主要基于实际出发,讲解内存STM32内存关系。 2、STM32内存配置 STM32单片机的内存配置如下: Flash: xx KBSRAM: xx KB对于操作系统来说,每个任务开…

Redisson分布式锁实现及原理详解

随着技术快速发展&#xff0c;数据规模增大&#xff0c;分布式系统越来越普及&#xff0c;一个应用往往会部署在多台机器上&#xff08;多节点&#xff09;&#xff0c;在有些场景中&#xff0c;为了保证数据不重复&#xff0c;要求在同一时刻&#xff0c;同一任务只在一个节点…