I am trying to write a generic data transformation routine that is table driven but run into a fundamental issue of binding data. I am stuck on how to refer to data in column in order to bind or construct a dynamic SQL statement. In particular, I am using rowtype as a data storage. Simplified Example:
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;
The actual problem I have is there is no easy way, even in DBMS_SQL package, to extract any row data by name. While Oracle seems to try to add a lot of dynamic features such as ANYDATA, ANYTYPE, Piplined functions but none I can find that allows this kind of simple data manipulation. In particular, “Oracle Dynamic SQL Method 4” seems to not be doable in PL/SQL where the bind data comes from any column data in a row of a table. Sure you can bind to a “hard coded” column name but then it’s no longer dynamic.
In addition, the fact that I have to write multiple UpdateByColumn method for each table because I can’t pass the rowtype to ONE generic method that will accept any rowtype is another limitation. I need to use rowtype because the data has been pre-fetched by the caller and possibly changed some of the data.
Or there is something I missed?
I think
%ROWTYPEis a dead-end here. As far as I know, there is no way to extract useful metadata about a PL/SQL variable.But things are different if you can use an abstract data type (ADT, or "object"). It is more powerful, and kind of similar to
%ROWTYPE. But it is not quite as convenient, and will make your initial code a little more complicated. You will have to pre-define the objects, and use them in your SQL.For example, replace code like this:
with this:
If that is acceptable, you can use dynamic PL/SQL for your updates:
UPDATE
%ROWTYPEonly exists in PL/SQL, there is no way to pass or convert those values. But you can store the record as a package variable, and then pass the name and type of that variable to your function. The function can refer to the record using dynamic PL/SQL, and then can convert it to a value to be used by the SQL.(This does not address the issue of changing multiple columns at once, it is just a demo showing a way to dynamically use a
%ROWTYPE.)