2012-03-19 6 views
3

私はテーブル駆動型の一般的なデータ変換ルーチンを作成しようとしていますが、データをバインドするという基本的な問題に直面しています。私は動的SQLステートメントをバインドまたは構築するために、列内のデータを参照する方法に固執しています。特に、私はデータストレージとして行タイプを使用しています。簡単な例:Oracle PL/SQLの動的SQLはそれほど動的ではありませんか?

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パッケージには、簡単な方法がないです。オラクルは、ANYDATA、ANYTYPE、Piplined関数などの多くの動的機能を追加しようとしているようですが、この種の単純なデータ操作を可能にするものはありません。特に、Oracleの動的SQLメソッド4は、バインド・データが表の行の任意の列データから来るPL/SQLでは実行できないようです。確かに "ハードコードされた"列名にバインドできますが、それはもはや動的です。

さらに、各行に対して複数のUpdateByColumnメソッドを記述する必要があるという事実は、どのような行タイプも受け入れる汎用メソッドに行タイプを渡すことができないということです。私は、データが呼び出し側によってプリフェッチされ、データの一部が変更された可能性があるため、rowtypeを使用する必要があります。

または私が逃した何かがありますか?

+1

%の行型の記録ではないように思え使用するデータ型はわかりませんが、あなたが何をしようとしているのか、なぜそれを知っていなくてもわかりません。たぶん、控えめな配列が適しているでしょう。タイプは、のテーブルです。インデックスはvarchar2(30)です。 – llayland

+0

最終的にはそれも私が思いついたものです。行データ型からすべてのデータをANYDATAの連想配列に読み込み、配列を共通ルーチンに渡して、名前で列を参照できるようにすることになります。 – dreamfly

答えて

3

私は%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; 
/
+0

共通更新ルーチンのパラメータ一覧は何ですか?特定のタイプを「ハードコードする」必要もありませんか?例えばプロシージャの作成または置換UpdateByColumn(rec tbl_type、colName varchar2)は...したがって、それぞれの型は別個の類似したルーティンを必要としますか? – dreamfly

+0

あなたはそうです。 ANYDATAを使用するようにコードを更新しました。したがって、1つのプロシージャだけが必要です。新しいコードは次のものに基づいています:http://www.oracle-developer.net/display.php?id=218#1c1f6 –

+0

更新する列の数が異なるため、 'using p_anydata'も問題になります。したがって、ネイティブの動的SQLではバインディングは不可能です。私の最後の実装は2つの配列で構成されています.1つはANYDATAの連想配列で、ここではすべてのデータを行型からロードしました。もう1つは更新する列のリストです。アソシエーティブは列名で索引付けされるため、実際のデータを列名で参照できます。このようにして、DBMS_SQLを使用してバインディングを実行することもできます。 rowdata( 'colname')のようなリテラルで行タイプのデータを参照する方法がまだないというのはむしろ残念です。ここで、rowdataは%rowtypeです – dreamfly

関連する問題