zoukankan      html  css  js  c++  java
  • 动态SQL详解

    动态SQL

    在之前用户所编写的PL/SQL程序时有一个最大的特点:就是所操作的数据库对象(例如:表)必须存在,否则创建的子程序就会出问题,而这样的操作在开发之中被称为静态SQL操作,而动态SQL操作可以让用户在定义程序时不指定具体的操作对象,而在执行时动态的传入所需要的数据库对象,从而使程序变得更加的灵活。

    创建一个功能,输入表名,输出表中有多少行,若没有这个表,则创建这个表。

    首先禁用所有触发器

    GRANT CREATE ANY TABLE TO SCOTT

    create or replace function get_table_count_fun(p_table_name varchar2) return number as

    v_sql_statement varchar2(200);--定义操作的SQL语句

    v_count number;--保存表中记录

    begin

    select count(*) into v_count from user_tables where table_name=UPPER(p_table_name);

    if v_count=0 then--数据表不存在

    v_sql_statement:='CREATE TABLE '|| p_table_name ||'(id number ,name varchar2(30) not null)';--创建数据表,注意此处不要忽略空格,按照正常写create table语句的格式加空格,否则无法识别

    execute immediate v_sql_statement;--执行动态SQL

    end if;

    v_sql_statement:=' select count(*) from '|| p_table_name;--查询数据表记录,注意加空格

    execute immediate v_sql_statement into v_count;--执行动态SQL并保存数据记录

    return v_count;

    end;

    /

    declare

    v1 varchar2(100);

    begin

    v1:=get_table_count_fun('t100');--查看t100表

    dbms_output.put_line(v1);

    end;

    /

    EXECUTE IMMEDIATE语句

    在动态SQL之中EXECUTE IMMEDIATE是最为重要的执行命令,使用此语句可以方便的在PL/SQL程序之中执行DML(INSERT/UPDATE/DELETE/单列SELECT)、DDL(CREATE/ALTER/DROP)、DCL(GRANT/REVOKE)语句,EXECUTE IMMEDIATE语法定义如下:

    EXECUTE IMMEDIATE 动态SQL字符串 [[BULK COLLECT]] INTO 自定义变量,...|记录类型|

    [USING [IN | OUT |IN OUT|绑定参数,...]

    [[RETURNING | RETURN][BULK COLLECT] INTO 绑定参数,...];

    EXECUTE IMMEDIATE由以下三个主要字句组成:

    INTO:保存动态SQL执行结果,如果返回多行记录可以通过BULK COLLECT设置批量保存;

    USING:用来为动态SQL设置占位符设置内容;

    RETURNING | RETURN:两者使用效果一样,是取得更新表记录被影响的数据,通过BULK COLLECT来设置批量绑定。

    使用动态SQL创建表和PLSQL块

    declare

    v_sql_statement varchar2(200);

    v_count number;--保存查找结果

    begin

    select count(*) into v_count from user_tables where table_name='VDATA_TAB';

    if v_count=0 then--数据表不存在

    v_sql_statement:='CREATE TABLE vdata_tab(

    id number primary key,

    url varchar2(50) not null)';--定义动态SQL

    execute immediate v_sql_statement;

    Else--数据表存在

    v_sql_statement:='TRUNCATE TABLE vdata_tab';

    execute immediate v_sql_statement;

    end if;

    v_sql_statement:='begin

    for x in 1 .. 10 loop

    insert into vdata_tab(id,url) values(x,''www.vdata.com.cn''||x);

    end loop;

    end;';

    execute immediate v_sql_statement;

    commit;--提交事物

    end;

    /

    使用绑定变量

    Declare

    V_sql_statement varchar2(200);

    V_deptno dept.deptno%type:=60;

    V_dname dept.dname%type:=’VDATA’;

    V_loc dept.loc%type:=’beijing’;

    Begin

    V_sql_statement:=’insert into dept(deptno,dname,loc) values(:dno,:dna,:dl)’;

    Execute immediate v_sql_statement using v_deptno,v_dname,v_loc;

    Commit;

    End;

    /

    如果有字段为NULL,则不能直接绑定NULL,需要通过变量设置

    如果在本程序中索要增加的部门位置为NULL,则以下的设置方式是错误的:EXECUTE IMMEDIATE v_sql_statement using v_deptno,v_dname,NULL;如果现在希望LOC的内容为NULL,可以将v_loc变量设置为NULL,其他执行部分不做改变。

    查询数据

    declare

    v_sql_statement1 varchar2(2000);

    v_empno emp.empno%type:=7369;

    v_emprow emp%rowtype;

    begin

    v_sql_statement1:='select * from emp where empno=:eno';

    execute immediate v_sql_statement1 into v_emprow using v_empno;

    dbms_output.put_line('ename: '||v_emprow.empno||', ename: '||v_emprow.ename||', position: '||v_emprow.job);

    end;

    /

    通过以上的操作可以发现,所有使用绑定变量的代码都只是针对基本的数据类型,例如字符串、数字等,但是这种方式不可能针对DDL操作,例如,将要创建或阶段的表名称使用绑定变量的话就是出现错误。

    创建表时使用绑定变量

    DECLARE

        v_sql_statement VARCHAR2(200) ;

        v_table_name VARCHAR2(200) := 'mldn' ;

        v_id_column VARCHAR2(200) := 'id' ;

    BEGIN

        v_sql_statement := 'CREATE TABLE ' || v_table_name ||' (' || v_id_column ||' NUMBER PRIMARY KEY)' ;

    EXECUTE IMMEDIATE v_sql_statement ;

    END  ;

    更新数据,取得更新后的结果

    Declare

    V_sql_statement varchar2(200);--定义SQL操作语句

    V_empno emp.empno%type:=7369;--要更新的雇员编号

    V_salary emp.sal%type;--保存更新后的sal内容

    V_job emp.job%type;--保存更新后的Job内容

    Begin

    V_sql_statement:=’update emp set sal=sal*1.2,job=”developer”’||’where empno=:eno returning sal.job into :salary,:job’;

    Execute immediate v_sql_statement using v_empno returning into v_salary,v_job;

    Dbms_output.put_line(‘salary: ‘||v_salary||’, new position: ‘||v_job);

    End;

    /

    也可以使用RETURN接收影响数据行的数据。

    在接收影响数据行数据时,也可以利用return进行操作,如下所示:

    v_sql_statement:=’update emp set sal=sal*12,job=”developer” ’||’ where empno=:eno return sal,job into :salary,:job’;

    Execute immediate v_sql_statement using v_empno return into v_salary,v_job;

    使用return与returning相比没有任何区别

    删除数据,取得删除前的结果

    Declare

    V_sql_statement varchar2(200);--定义SQL操作语句

    V_emprow emp%rowtype;--保存emp类型

    V_empno emp.empno%type:=7369;--删除的雇员编号

    V_ename emp.ename%type;--删除的雇员姓名

    V_sal emp.sal%type;--删除的雇员工资

    Begin

    V_sql_statement:=’delete from emp where empno=:eno returning ename,sal into :name,:sal;

    Execute immediate v_sql_statement using v_empno returning into v_ename,v_sal;

    Dbms_output.put_line(‘empno: ‘||v_empno||’, ename: ‘||v_ename||’, salary: ‘||v_sal);

    End;

    /

    在使用USING或RETURNING语句时都可以设置参数模式(IN/OUT/IN OUT),其中对USING子句主要是使用变量定义的内容,所以默认的模式是IN模式。使用RETURNING子句时不需要设置内容,只需接收返回内容,所以其模式为OUT。

    编写部门增加过程

    Create or replace procedure dept_insert_proc(

    P_deptno in out dept.deptno%type,--此处可以将p_deptno的内容回传

    P_dname dept.dname%type,--默认为in模式

    P_loc dept.loc%type) as --默认为in模式

    Begin

    Select max(deptno) into p_deptno from dept;--取得最大的deptno内容

    P_deptno:=p_deptno+1;--让最大值部门编号+1,此处不考虑超过两位数字情况

    Insert into dept(deptno,dname,loc) values (p_deptno,p_dname,p_loc);

    End;

    /

    批量绑定

    通过动态SQL进行查询或更新操作时,每次都是向数据库提交了一条操作语句,如果现在希望数据库可以一次性接收多条SQL,以及数据库可以一次性将操作结果返回到某一个集合中时,就可以采用批量处理操作完成,在进行批量处理操作中,注意依靠BULK COLLECT进行操作。

    使用BULK COLLECT更新

    DECLARE

    TYPE ename_index IS TABLE OF emp.ename%TYPE INDEX BY PLS_INTEGER ;

    TYPE job_index IS TABLE OF emp.job%TYPE INDEX BY PLS_INTEGER ;

    TYPE sal_index IS TABLE OF emp.sal%TYPE INDEX BY PLS_INTEGER ;

    v_ename ename_index ;

    v_job job_index ;

    v_sal sal_index ;

    v_sql_statement VARCHAR2(200) ;         --定义动态SQL

    v_deptno emp.deptno%TYPE := 10 ;        --查询10部门

    BEGIN

    v_sql_statement := 'UPDATE emp SET sal=sal*1.2 WHERE deptno=:dno ' ||

    ' RETURNING ename,job,sal INTO :ena, :ej, :es' ;-- 此时返回多行更新结果(注意空格,否则无法识别语句)

    EXECUTE IMMEDIATE v_sql_statement USING v_deptno

    RETURNING BULK COLLECT INTO v_ename,v_job,v_sal ;

    FOR x IN 1 .. v_ename.COUNT LOOP

    DBMS_OUTPUT.put_line('雇员姓名' || v_ename(x) || ',职位:' || v_job(x) || ',工资:' || v_sal(x)) ;

    END LOOP ;

    END ;

    雇员姓名:CLARK,职位:MANAGER,工资:3528

    雇员姓名:KING,职位:PRESIDENT,工资:7200

    雇员姓名:MILLER,职位:CLERK,工资:1872

    查询时使用BULK COLLECT

    Declare

    type ename_index is table of emp.ename%type index by pls_integer;

    Type job_index is table of emp.job%type index by pls_integer;

    Type sal_index is table of emp.sal%type index by pls_integer;

    V_ename ename_index;

    V_job job_index;

    V_sal sal_index;

    V_sql_statement varchar2(200);--定义动态SQL

    V_deptno emp.deptno%type:=10;--查询10部门

    Begin

    V_sql_statement:=’select ename,job,sal from emp where deptno=:dno’;--此时返回多行更新结果

    Execute immediate v_sql_statement

    Bulk collect into v_ename,v_job,v_sal

    Using v_deptno;

    For x in 1..v_ename.count loop

    Dbms_output.put_line(‘ename: ’||v_ename(x)||’ , position: ‘||v_job(x)||’, salary: ‘||v_sal(x));

    End loop;

    End;

    /

    FORALL

    如果要向动态SQL之中设置多个绑定参数,则就必须利用FORALL语句完成,此语句的语法如下所示。

    FORALL索引变量IN参数集合最小值..参数集合最大值

    EXECUTE IMMEDIATE 动态SQL字符串

    [USING 绑定参数|绑定参数(索引), ...]

    [[RETURNING | RETURN] BULK COLLECT INTO 绑定参数集合 , ...];

    通过FORALL设置多个参数

    Declare

    Type empno_nested is table of emp.empno%type;--定义嵌套表

    Type ename_index is table of emp.ename%type index by pls_integer;--定义索引表

    Type job_index is table of emp.job%type index by pls_integer;--定义索引表

    Type sal_index is table of emp.sal%type index by pls_integer;--定义索引表

    V_ename ename_index;--保存删除后的姓名

    V_job job_index;--保存删除后的职位

    V_sal sal_index;--保存删除后的工资

    V_empno empno_nested:=empno_nested(7369,7566,7788);--定义要删除雇员编号

    V_sql_statement varchar2(200);--动态SQL

    Begin

    V_sql_statement:=’delete from emp where empno=:eno’||’

    Returning ename,job,sal into :ena, :ej, :es’;--删除数据SQL

    FORALL x in 1 .. v_empno.count --FORALL绑定多个变量

    EXECUTE IMMEDIATE v_sql_statement using v_empno(x)

    Returning bulk collect into v_ename,v_job,v_sal;

    For x in 1 .. v_ename.count loop

    Dbms_output.put_line(‘ename: ‘||v_ename(x)||’, position: ‘||’, salary: ‘||v_sal(x));

    End loop;

    End;

    /

    处理游标操作

    动态SQL操作之中,除了可以处理单行查询操作之外,也可以利用游标完成多行数据的操作,而在游标定义时也同样可以使用动态绑定变量的方式,此时就需要在打开游标变量时增加USING子句操作。

    在游标中使用动态SQL

    DECLARE
    emp_cur SYS_REFCURSOR ; -- 定义游标变量
    v_emprow emp%ROWTYPE ; -- 定义emp行类型
    v_deptno emp.deptno%TYPE := 10 ; -- 定义要查询雇员的部门编号
    BEGIN
    OPEN emp_cur FOR 'SELECT * FROM emp WHERE deptno=:dno '
    USING v_deptno ;
    LOOP
    FETCH emp_cur INTO v_emprow ; -- 取得游标数据
    EXIT WHEN emp_cur%NOTFOUND ; -- 如果没有数据则退出
    DBMS_OUTPUT.put_line('雇员姓名:' || v_emprow.ename || ',雇员职位:' || v_emprow.job) ;
    END LOOP ;
    CLOSE emp_cur ;
    END ;

    FETCH

    在FETCH语句之中利用BULK COLLECT一次性将多个数据保存到集合类型之中,语法如下所示。

    FETCH 动态游标 BULK COLLECT INTO 集合变量 ...;

    利用FETCH保存查询结果

    Declare

    Emp_cur sys_refcursor;--定义游标变量

    Type emp_index is table of emp%rowtype index by pls_integer;--定义索引表

    V_emprow emp_index;--定义emp行类型

    V_deptno emp.deptno%type:=10;--定义要查询雇员的部门编号

    Begin

    Open emp_cur for ‘select * from emp where deptno=:dno’

    Using v_deptno;

    Fetch emp_cur bulk collect into v_emprow;

    Close emp_cur

    For x in 1 .. v_emprow.count loop

    Dbms_output.put_line(‘empno: ‘||v_emprow(x).empno||’, ename: ‘||v_emprow(x).ename||’, position: ‘||v_emprow(x).job);

    End loop;

    End;

    /

    使用动态SQL可以在依赖对象不存在时创建子程序;

    动态SQL主要利用EXECUTE IMMEDIATE语句执行DML/DDL/DCL等语句操作;

    如果使用了绑定变量,则必须在EXECUTE IMMEDIATE中使用USING子句设置所需要的绑定变量;

    使用RETURNING或RETURN语句可以接收查询或更新后的返回结果;

    使用批处理可以一次性将数据库之中取回的多个数据保存在集合里,或者使用FORALL将多个绑定参数设置到动态SQL之中。

    动态显示游标

    输出HR或SCOTT下每张表对应的所有记录数

    declare

    emp_cur sys_refcursor;

    v_emprow emp%rowtype;

    v_deptno emp.deptno%type:=10;

    begin

    open emp_cur for 'select * from emp where deptno=:dno'

    using v_deptno;

    loop

    fetch emp_cur into v_emprow;

    exit when emp_cur%notfound;

    dbms_output.put_line('ename: '||v_emprow.ename||' position: '||v_emprow.job);

    end loop;

    close emp_cur;

    end;

    /

  • 相关阅读:
    form表单提交数据,页面必定会刷新,ajax提交数据不会刷新,做到悄悄提交,多选删除,ajax提交实例
    Jenkins持续集成
    centos6.X安装python3.X
    python ddt、yml
    Python之ATP框架
    python之多线程多进程
    Python自带unittest单元测试简介
    Python自动化之logging模块
    python自动化学习笔记(九)
    python自动化学习笔记(八)
  • 原文地址:https://www.cnblogs.com/kawashibara/p/9097308.html
Copyright © 2011-2022 走看看