Oracle PL/SQL 动态 SQL 不那么动态?

标签 oracle plsql dynamic-sql

我正在尝试编写一个由表驱动但遇到绑定(bind)数据的基本问题的通用数据转换例程。我被困在如何引用列中的数据以绑定(bind)或构造动态 SQL 语句。特别是,我使用 rowtype 作为数据存储。简化示例:

Create or replace Procedure UpdateByColumn(rec tbl%rowtype, colName varchar2) is
  Sqlstmt varchar2(1000);
Begin
  -- this won't work because can't refer to data by column name
  Sqlstmt := 'update tbl set ' || colName || '=' || rec(colName);

  -- this will work but no longer dynamic
  Sqlstmt := 'update tbl set ' || colName || '=' || rec.MSRP;
End;

我遇到的实际问题是,即使在 DBMS_SQL 包中,也没有简单的方法可以按名称提取任何行数据。虽然 Oracle 似乎尝试添加许多动态特性,例如 ANYDATA、ANYTYPE、Piplined 函数,但我找不到允许这种简单数据操作的功能。特别是,“Oracle 动态 SQL 方法 4”在 PL/SQL 中似乎不可行,其中绑定(bind)数据来自表行中的任何列数据。当然,您可以绑定(bind)到“硬编码”列名,但它不再是动态的。

此外,我必须为每个表编写多个 UpdateByColumn 方法,因为我不能将行类型传递给一个可以接受任何行类型的通用方法,这是另一个限制。我需要使用rowtype,因为调用者已经预先获取了数据,并且可能更改了一些数据。

还是我错过了什么?

最佳答案

我认为 %ROWTYPE这里是死胡同。据我所知,没有办法提取关于 PL/SQL 变量的有用元数据。
但是,如果您可以使用抽象数据类型(ADT,或“对象”),情况就不同了。它更强大,有点类似于 %ROWTYPE .但这不是很方便,并且会使您的初始代码更加复杂。您必须预先定义对象,并在 SQL 中使用它们。
例如,替换如下代码:

declare
    v_test tbl%rowtype;
begin
    select * into v_test from tbl;
end;
/
有了这个:
declare
    v_test2 tbl_type;
begin
    select tbl_type(msrp, some_other_column) into v_test2 from tbl;
end;
/
如果这是可以接受的,您可以使用动态 PL/SQL 进行更新:
--Create table, ADT, and test data
create table tbl(MSRP varchar2(100), some_other_column varchar2(100));

create or replace type tbl_type as object
(
    msrp varchar2(100),
    some_other_column varchar2(100)
);
/
insert into tbl values('1', '1');

--Convert object to ANYDATA, process with dynamic PL/SQL
declare
    my_tbl tbl_type := tbl_type('2', '3');

    procedure UpdateByColumn(p_anydata in anydata, colName in varchar2) is
        v_typename varchar2(30) := p_anydata.getTypeName;
    begin
        execute immediate '
            declare
                v_anydata2 anydata := :anydata;
                v_object '||v_typename||';
                v_dummy pls_integer;
            begin
                v_dummy := v_anydata2.getObject(v_object);
                update tbl set '||colName||' = v_object.'||colName||';
            end;
        ' using p_anydata;
    end;
begin
    updateByColumn(anyData.convertObject(my_tbl), 'MSRP');
end;
/

--Show the new data
select * from tbl;

MSRP    SOME_OTHER_COLUMN
----    -----------------
2       1
更新 %ROWTYPE仅存在于 PL/SQL 中,无法传递或转换这些值。但是您可以将记录存储为包变量,然后将该变量的名称和类型传递给您的函数。该函数可以使用动态 PL/SQL 引用记录,然后可以将其转换为 SQL 使用的值。
(这并没有解决同时更改多个列的问题,它只是一个演示,展示了一种动态使用 %ROWTYPE 的方法。)
--Create table and test data
create table tbl(MSRP varchar2(100), some_other_column varchar2(100));
insert into tbl values('1', '1');
commit;

--Create another table, tbl2, that will be used to update tbl
--(The tables in this example have similar columns, but that is not 
--actually necessary.)
create table tbl2(MSRP varchar2(100), some_other_column varchar2(100));
insert into tbl2 values('2', '2');
commit;

--New function works by passing in names of global variables and 
--their types, instead of actual values.
create or replace procedure UpdateByColumn(
    p_package_and_variable_name in varchar2,
    p_rowtype in varchar2,
    colName in varchar2) is
begin
    execute immediate '
        declare
            v_rec '||p_rowtype||' := '||p_package_and_variable_name||';
        begin
            update tbl set '||colName||' = v_rec.'||colName||';
        end;
    ';
end;
/

--A test package that calls the function to update tbl.
create or replace package test_package is
    tbl2_rec tbl2%rowtype;
    procedure test_procedure;
end;
/

create or replace package body test_package is
    procedure test_procedure is
    begin
        select * into tbl2_rec from tbl2;
        UpdateByColumn('test_package.tbl2_rec', 'tbl2%rowtype', 'MSRP');
    end;
end;
/

begin
    test_package.test_procedure;
end;
/

关于Oracle PL/SQL 动态 SQL 不那么动态?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9763457/

相关文章:

sql - 在 Oracle 的 Greatest 函数中处理 Null

java - PL/SQL - 仅当记录不存在时插入记录

sql-server-2008 - 动态SQL错误: 'CREATE TRIGGER' must be the first statement in a query batch

java - SQL trunc/group/order by dates (day/month/quarter/year) with sum skip dates with no data

postgresql - 在 PostgreSQL 中为交叉表动态生成列

sql - 多次使用相同参数的 format() 动态查询

java - 如何使用 Oracle Forms 将 CSV 列值插入 block 和表中?

oracle - 如何让 "Microsoft ODBC for Oracle"驱动程序在 Windows 7 中工作

oracle - 安装适用于nodejs 0.10.20的oracle 11g2r2驱动程序

indexing - 在PLSQL中通过循环重建索引