How to select max value from a temporary table whose field is unnamed - oracle

I'm writing a PL/SQL function that needs to get dates from a few different tables and return the most recent one. My approach has been this:
Create a temporary table to hold the dates:
CREATE TYPE t_dates IS TABLE OF DATE;
/
Create a few local variables:
l_date DATE;
l_dates t_dates := t_dates();
l_idx INTEGER := 0;
l_output_date DATE;
Then select into a variable each date I'm interested in, and add it to the temporary table:
SELECT it.date
INTO l_date
FROM interesting_table it
WHERE it.id = 1
;
l_dates.extend;
l_idx := l_idx + 1;
l_dates(l_idx) := l_date;
After the temporary table has been populated, now I just want to select the max value from it. How do I do that? Something like...
SELECT max(*)
INTO l_output_date
FROM l_dates
;
RETURN l_output_date;
But I'm not sure I can reference my temporary table like that, nor how to find the max of a column that is unnamed.
Edit: When I test the above, I get an error: PL/SQL: ORA-00936: missing expression related to the line where I have SELECT max(*). However, I don't believe that this is my only error, because if I change the last block to:
SELECT *
INTO l_output_date
FROM l_dates
WHERE rownum = 1
;
which, just for testing, should select the first date in the temp table, then the error becomes PL/SQL: ORA-00942: table or view does not exist, which indicates to me that I can't refer to my temp table in this manner.
I have since learned from the comments that I should have max(column_value) rather than max(*), but using this, I still get the table or view does not exist error.

It is not possible to select from the nested tables, like you have tried.
You may have to use a logic to find the maximum value
l_idx := l_idx + 1;
l_dates(l_idx) := l_date;
if(l_date > l_dates(l_idx-1) OR l_idx =1) THEN
max_date :=l_date;
End if;
For your case, Rather than fetching a list of dates then getting the max date, you can find the max date in your query itself
SELECT max(it.date)
INTO l_date
FROM interesting_table it
WHERE it.id = 1
;

Related

PL/SQL : Need to compare data for every field in a table in plsql

I need to create a procedure which will take collection as an input and compare the data with staging table data row by row for every field (approx 50 columns).
Business logic :
whenever a staging table column value will mismatch with the corresponding collection variable value then i need to update 'FAIL' into staging table STATUS column and reason into REASON column for that row.
If matched then need to update 'SUCCESS' in STATUS column.
Payload will be approx 500 rows in each call.
I have created below sample script:
PKG Specification :
CREATE OR REPLACE
PACKAGE process_data
IS
TYPE pass_data_rec
IS
record
(
p_eid employee.eid%type,
p_ename employee.ename%type,
p_salary employee.salary%type,
p_dept employee.dept%type
);
type p_data_tab IS TABLE OF pass_data_rec INDEX BY binary_integer;
PROCEDURE comp_data(inpt_data IN p_data_tab);
END;
PKG Body:
CREATE OR REPLACE
PACKAGE body process_data
IS
PROCEDURE comp_data (inpt_data IN p_data_tab)
IS
status VARCHAR2(10);
reason VARCHAR2(1000);
cnt1 NUMBER;
v_eid employee_copy.eid%type;
v_ename employee_copy.ename%type;
BEGIN
FOR i IN 1..inpt_data.count
LOOP
SELECT ec1.eid,ec1.ename,COUNT(*) over () INTO v_eid,v_ename,cnt1
FROM employee_copy ec1
WHERE ec1.eid = inpt_data(i).p_eid;
IF cnt1 > 0 THEN
IF (v_eid=inpt_data(i).p_eid AND v_ename = inpt_data(i).p_ename) THEN
UPDATE employee_copy SET status = 'SUCCESS' WHERE eid = inpt_data(i).p_eid;
ELSE
UPDATE employee_copy SET status = 'FAIL' WHERE eid = inpt_data(i).p_eid;
END IF;
ELSE
NULL;
END IF;
END LOOP;
COMMIT;
status :='success';
EXCEPTION
WHEN OTHERS THEN
status:= 'fail';
--reason:=sqlerrm;
END;
END;
But in this approach i have below mentioned issues.
Need to declare all local variables for each column value.
Need to compare all variable data using 'and' operator. Not sure whether it is correct way or not because if there are 50 columns then if condition will become very heavy.
IF (v_eid=inpt_data(i).p_eid AND v_ename = inpt_data(i).p_ename) THEN
Need to update REASON column when any column data mismatched (first mismatched column name) for that row, in this approach i am not able to achieve.
Please suggest any other good way to achieve this requirement.
Edit :
There is only one table at my end i.e target table. Input will come from any other source as collection object.
REVISED Answer
You could load the the records into t temp table, but unless you want additional processing it's not necessary. AFAIK there is no way to identify the offending column (first one only) without slugging through column-by-column. However, your other concern having to declare a variable is not necessary. You can declare a single variable defined as %rowtype which gives you access to each column by name.
Looping through an array of data to find the occasional error is just bad (imho) with SQL available to eliminate the good ones in one fell swoop. And it's available here. Even though your input is a array we can use as a table by using the TABLE operator, which allows an array (collection) as though it were a database table. So the MINUS operator can till be employed. The following routine will set the appropriate status and identify the first miss matched column for each entry in the input array. It reverts to your original definition in package spec, but replaces the comp_data procedure.
create or replace package body process_data
is
procedure comp_data (inpt_data in p_data_tab)
is
-- define local array to hold status and reason for ecah.
type status_reason_r is record
( eid employee_copy.eid%type
, status employee_copy.status%type
, reason employee_copy.reason%type
);
type status_reason_t is
table of status_reason_r
index by pls_integer;
status_reason status_reason_t := status_reason_t();
-- define error array to contain the eid for each that have a mismatched column
type error_eids_t is table of employee_copy.eid%type ;
error_eids error_eids_t;
current_matched_indx pls_integer;
/*
Helper function to identify 1st mismatched column in error row.
Here is where we slug our way through each column to find the first column
value mismatch. Note: There is actually validate the column sequence, but
for purpose here we'll proceed in the input data type definition.
*/
function identify_mismatch_column(matched_indx_in pls_integer)
return varchar2
is
employee_copy_row employee_copy%rowtype;
mismatched_column employee_copy.reason%type;
begin
select *
into employee_copy_row
from employee_copy
where employee_copy.eid = inpt_data(matched_indx_in).p_eid;
-- now begins the task of finding the mismatched column.
if employee_copy_row.ename != inpt_data(matched_indx_in).p_ename
then
mismatched_column := 'employee_copy.ename';
elsif employee_copy_row.salary != inpt_data(matched_indx_in).p_salary
then
mismatched_column := 'employee_copy.salary';
elsif employee_copy_row.dept != inpt_data(matched_indx_in).p_dept
then
mismatched_column := 'employee_copy.dept';
-- elsif continue until ALL columns tested
end if;
return mismatched_column;
exception
-- NO_DATA_FOUND is the one error that cannot actually be reported in the customer_copy table.
-- It occurs when an eid exista in the input data but does not exist in customer_copy.
when NO_DATA_FOUND
then
dbms_output.put_line( 'Employee (eid)='
|| inpt_data(matched_indx_in).p_eid
|| ' does not exist in employee_copy table.'
);
return 'employee_copy.eid ID is NOT in table';
end identify_mismatch_column;
/*
Helper function to find specified eid in the initial inpt_data array
Since the resulting array of mismatching eid derive from a select without sort
there is no guarantee the index values actually match. Nor can we sort to build
the error array, as there is no way to know the order of eid in the initial array.
The following helper identifies the index value in the input array for the specified
eid in error.
*/
function match_indx(eid_in employee_copy.eid%type)
return pls_integer
is
l_at pls_integer := 1;
l_searching boolean := true;
begin
while l_at <= inpt_data.count
loop
exit when eid_in = inpt_data(l_at).p_eid;
l_at := l_at + 1;
end loop;
if l_at > inpt_data.count
then
raise_application_error( -20199, 'Internal error: Find index for ' || eid_in ||' not found');
end if;
return l_at;
end match_indx;
-- Main
begin
-- initialize status table for each input enter
-- additionally this results is a status_reason table in a 1:1 with the input array.
for i in 1..inpt_data.count
loop
status_reason(i).eid := inpt_data(i).p_eid;
status_reason(i).status :='SUCCESS';
end loop;
/*
We can assume the majority of data in the input array is valid meaning the columns match.
We'll eliminate all value rows by selecting each and then MINUSing those that do match on
each column. To accomplish this cast the input with TABLE function allowing it's use in SQL.
Following produces an array of eids that have at least 1 column mismatch.
*/
select p_eid
bulk collect into error_eids
from (select p_eid, p_ename, p_salary, p_dept from TABLE(inpt_data)
minus
select eid, ename, salary, dept from employee_copy
) exs;
/*
The error_eids array now contains the eid for each miss matched data item.
Mark the status as failed, then begin the long hard process of identifying
the first column causing the mismatch.
The following loop used the nested functions to slug the way through.
This keeps the main line logic clear.
*/
for i in 1 .. error_eids.count -- if all inpt_data rows match then count is 0, we bypass the enttire loop
loop
current_matched_indx := match_indx(error_eids(i));
status_reason(current_matched_indx).status := 'FAIL';
status_reason(current_matched_indx).reason := identify_mismatch_column(current_matched_indx);
end loop;
-- update employee_copy with appropriate status for each row in the input data.
-- Except for any cid that is in the error eid table but doesn't exist in the customer_copy table.
forall i in inpt_data.first .. inpt_data.last
update employee_copy
set status = status_reason(i).status
, reason = status_reason(i).reason
where eid = inpt_data(i).p_eid;
end comp_data;
end process_data;
There are a couple other techniques used you may want to look into if you are not familiar with them:
Nested Functions. There are 2 functions defined and used in the procedure.
Bulk Processing. That is Bulk Collect and Forall.
Good Luck.
ORIGINAL Answer
It is NOT necessary to compare each column nor build a string by concatenating. As you indicated comparing 50 columns becomes pretty heavy. So let the DBMS do most of the lifting. Using the MINUS operator does exactly what you need.
... the MINUS operator, which returns only unique rows returned by the
first query but not by the second.
Using that this task needs only 2 Updates: 1 to mark "fail", and 1 to mark "success". So try:
create table e( e_id integer
, col1 varchar2(20)
, col2 varchar2(20)
);
create table stage ( e_id integer
, col1 varchar2(20)
, col2 varchar2(20)
, status varchar2(20)
, reason varchar2(20)
);
-- create package spec and body
create or replace package process_data
is
procedure comp_data;
end process_data;
create or replace package body process_data
is
package body process_data
procedure comp_data
is
begin
update stage
set status='failed'
, reason='No matching e row'
where e_id in ( select e_id
from (select e_id, col1, col2 from stage
except
select e_id, col1, col2 from e
) exs
);
update stage
set status='success'
where status is null;
end comp_data;
end process_data;
-- test
-- populate tables
insert into e(e_id, col1, col2)
select (1,'ABC','def') from dual union all
select (2,'No','Not any') from dual union all
select (3,'ok', 'best ever') from dual union all
select (4,'xx','zzzzzz') from dual;
insert into stage(e_id, col1, col2)
select (1,'ABC','def') from dual union all
select (2,'No','Not any more') from dual union all
select (4,'yy', 'zzzzzz') from dual union all
select (5,'no e','nnnnn') from dual;
-- run procedure
begin
process_data.comp_date;
end;
-- check results
select * from stage;
Don't ask. Yes, you to must list every column you wish compared in each of the queries involved in the MINUS operation.
I know the documentation link is old (10gR2), but actually finding Oracle documentation is a royal pain. But the MINUS operator still functions the same in 19c;

SQL trigger to stop update when a condition is met

I have 3 tables: Projects, Components, and Suppliers.
What I am trying to do is writing a trigger that doesn't allow the value of city to be modified if the component and the project have the same city as the supplier.
What I have tried so far:
create or replace TRIGGER Supplier_control
BEFORE UPDATE of city
ON Suppliers
BEGIN
DECLARE v_counter NUMBER := 0;
SELECT COUNT(*) FROM (SELECT * FROM Suppliers s JOIN Projects p ON (s.city=p.city) JOIN Components c ON (c.city=s.city)) INTO v_counter;
IF (v_counter != 0)
THEN
raise_application_error(-20111,'Can't change the city for this supplier!');
END IF;
END;
After trying to run this, I am getting the following error:
Error at line 3: PLS-00103: Encountered the symbol "JOIN" when expecting one of the following:
) , with group having intersect minus order start union where
connect
Please note that the line number refers to the number of the line after BEGIN!
I have also tried writing the declare part before BEGIN, I am getting the following error:
Error at line 3: PL/SQL: SQL Statement ignored
What needs to be done in order to get rid of these errors?
There are some syntax errors.
DECLARE goes before the BEGIN statement.
INTO goes after SELECT and before FROM.
At raise_application_error(-20111,'Can't change the city for this supplier!'); you cannot write Can't because the first single quote will end at the quote of Can't causing the string to end there. So you should remove it or do: raise_application_error(-20111,'Can''t change the city for this supplier!');
With all that being said, the full code should look like:
CREATE OR REPLACE TRIGGER Supplier_control
BEFORE UPDATE of city
ON Suppliers
DECLARE
v_counter NUMBER := 0;
BEGIN
SELECT COUNT(*)
INTO v_counter
FROM (SELECT * FROM Suppliers s JOIN Projects p ON s.city=p.city JOIN Components c ON c.city=s.city);
IF v_counter != 0 THEN
raise_application_error(-20111,'Can''t change the city for this supplier!');
END IF;
END;
Hope this helps.
You are trying to access the variable which is assigned as zero in the declaration.
set the variable from the query (result of this will be a number)
create or replace TRIGGER Supplier_control
BEFORE UPDATE of city
ON Suppliers
BEGIN
DECLARE v_counter NUMBER := 0;
-- change this line
SET v_counter = (SELECT COUNT(*) FROM (SELECT * FROM Suppliers s JOIN Projects p ON (s.city=p.city) JOIN Components c ON (c.city=s.city)));
IF (v_counter != 0)
THEN
raise_application_error(-20111,'Can't change the city for this supplier!');
END IF;
END;
changed the count query to the edited one. First simply run the query SELECT COUNT(*) FROM (SELECT * FROM Suppliers s JOIN Projects p ON (s.city=p.city) JOIN Components c ON (c.city=s.city)) if the output is number then assign to the variable v_counter
There is no need for the overhead of a Join nor even transfer of any date. Assuming City is indexed in Projects and Components (probably should be an indexed FK), the following requires only a simple index prob on each table.
create or replace trigger supplier_control
before update of city
on suppliers
for each row
declare
project_component_exists integer ;
begin
select null
into project_component_exists
from dual
where exists ( select null
from projects
where city = :old.city
)
and exists ( select null
from components
where city = :old.city
);
raise_application_error(-20111,'Can''t change the city for this supplier!');
exception
when no_data_found
then null;
end;
Check ,by lower() or upper() to match including case-insensitivity, whether a city match throughout the whole table Project's values.
You don't need to and shouldn't use Supplier table in the select statement due to risk of table name is mutating error :
CREATE OR REPLACE TRIGGER Supplier_control
BEFORE UPDATE of city
ON Suppliers
DECLARE
v_counter PLS_INTEGER;
BEGIN
SELECT COUNT(*)
INTO v_counter
FROM Projects p
WHERE lower(:new.city)=lower(p.city);
IF (v_counter != 0) THEN
raise_application_error(-20111,'Can''t change the city for this supplier!');
END IF;
END;
where an initialization is redundant for v_counter, if there's no matching record found it would already be zero. Moreover, notice the order of declare keyword including variable definition part with it.

How do I display a nested table in Oracle - PL/SQL?

I have an assignment where I'm supposed to create a user-defined function that will return all employees after 1968. My code is as follows:
First I create a new object, called emp_dobs, to hold the employees' firstname, lastname, and date of birth, using the same data types as the original employee table:
CREATE OR REPLACE TYPE emp_dobs AS OBJECT (
emp_fname VARCHAR2(20),
emp_lname VARCHAR2(20),
emp_dob DATE
);
/
Then I create emp_dobs_nested as a table of emp_dobs:
CREATE OR REPLACE TYPE emp_dobs_nested AS TABLE OF emp_dobs;
/
Lastly, I create a function that's supposed to return an emp_dobs_nested table:
CREATE OR REPLACE FUNCTION get_emp_dobs RETURN emp_dobs_nested
AS
dobs emp_dobs_nested;
BEGIN
SELECT emp_dobs(firstname, lastname, birthdate) BULK COLLECT INTO dobs
FROM employee
WHERE birthdate < TO_DATE('01-JAN-1968', 'DD-MON-YYYY');
RETURN dobs;
END;
/
There is a weird quirk with compiling emp_dob_nested, however, where Oracle SQL Developer will display a new tab labeled "Output Variables - Log," and only show EMP_FNAME and EMP_LNAME. Despite that, everything compiles.
Now I want to test the function and display its results to prove that it works, but when I try this:
DECLARE
dobs emp_dobs_nested;
BEGIN
dobs := get_emp_dobs;
DBMS_OUTPUT.PUT_LINE(dobs);
END;
/
I get this error:
Error report -
ORA-06550: line 5, column 5:
PLS-00306: wrong number or types of arguments in call to 'PUT_LINE'
ORA-06550: line 5, column 5:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
What am I missing here? Thanks.
You cannot pass the whole collection to DBMS_OUTPUT, rather you must loop through it and display individual columns at each index.
DECLARE
dobs emp_dobs_nested;
BEGIN
dobs := get_emp_dobs;
FOR i IN 1..dobs.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(dobs(i).emp_fname||','||dobs(i).emp_lname||','||dobs(i).emp_dob);
END LOOP;
END;
/
You can also use TABLE function to unnest a collection of objects into a relational resultset:
select * from table( get_emp_dobs )
Live demo: http://sqlfiddle.com/#!4/8cfb2/1
Another way of display is to use XML, try
DBMS_OUTPUT.PUT_LINE(XMLTYPE(dobs));
why not a create a view instead?
CREATE VIEW emp_view
AS
SELECT firstname, lastname, birthdate
FROM employee
WHERE birthdate < TO_DATE('01-JAN-1968', 'DD-MON-YYYY');
You can also try this,
DECLARE
dobs emp_dobs_nested;
i NUMBER := 0;
BEGIN
dobs := get_emp_dobs;
LOOP
i := dobs.NEXT(i);
DBMS_OUTPUT.PUT_LINE(dobs(i).emp_fname||','||dobs(i).emp_lname||','||dobs(i).emp_dob);
IF i = dobs.LAST THEN
EXIT;
END IF;
END LOOP;
END;
/

Getting inconsistent datatypes error while fetching data from ref cursor into a nested table

I'm getting an error while fetching data from ref cursor into a nested table. Here is what I'm doing.
Object and table types:
CREATE OR REPLACE TYPE obj_report_org IS OBJECT
(dummy1 VARCHAR2(50),
dummy2 VARCHAR2(50),
dummy3 VARCHAR2(50),
dummy4 NUMBER(10));
CREATE OR REPLACE TYPE tab_report_org IS TABLE OF obj_report_org;
Procedure:
CREATE OR REPLACE PROCEDURE areaReport_test(v_name1 VARCHAR2,
v_name2 VARCHAR2, tab_report_set OUT tab_report_org)
IS
str VARCHAR2(2000);
v_num NUMBER := 1;
recordset SYS_REFCURSOR;
lv_tab_report_set tab_report_org := tab_report_org();
BEGIN
str := ' SELECT tab2.dummy1 ,tab3.dummy2,tab2.dummy3,tab1.dummy4 '||
' FROM tab1,tab2, tab3, tab4'||
' WHERE <JOIN CONDITIONS>';
OPEN recordset FOR
str||' START WITH tab1.name = :name1'||
' CONNECT BY PRIOR tab1.id = tab1.parent_id'||
' UNION '||
str||' START WITH tab1.name = :name2'||
' CONNECT BY PRIOR tab1.id = tab1.parent_id'
USING v_name1,v_name2;
FETCH recordset BULK COLLECT into lv_tab_report_set;
CLOSE recordset;
tab_report_set := lv_tab_report_set;
END;
Then I have an anonymous block to call the procedure:
DECLARE
l_tab_report_set tab_report_org;
BEGIN
areaReport_test('PRASHANT',null,l_tab_report_set);
FOR i in 1 .. l_tab_report_set.count
LOOP
DBMS_OUTPUT.PUT_LINE(
l_tab_report_set(i).dummy1|| ' | ' ||
l_tab_report_set(i).dummy2|| ' | ' ||
l_tab_report_set(i).dummy3|| ' | ' ||
l_tab_report_set(i).dummy4|| );
END LOOP;
END;
After running the anonymous block, I'm get this error:
ORA-00932: inconsistent datatypes: expected - got -
ORA-06512: at "AREAREPORT_TEST", line 36
ORA-06512: at line 5
00932. 00000 - "inconsistent datatypes: expected %s got %s"
It seems we cannot bulk fetch data into nested table formed from a SQL object. While sequence of fields and datatypes in object match with select query.
Please advise.
I will start from the bottom:
FETCH recordset BULK COLLECT into lv_tab_report_set;
There is a recordset that you are fetching into table of objects. So, recordset should contain list of objects.
SELECT tab2.dummy1 ,tab3.dummy2,tab2.dummy3,tab1.dummy4...
It is a list of columns and not an object. Here is a list of objects:
select obj_report_org(tab2.dummy1 ,tab3.dummy2,tab2.dummy3,tab1.dummy4) ...
few remarks about code in general
When you use UNION instead of UNION ALL, Oracle would sort both results and eliminate duplicates (there is no other way to do that). So just think about if you really need UNION and remember that there is a price for that.
You UNION two datasets that looks like could be combined together
START WITH tab1.name in (:name1, :name2)
Continues after ORA-22950
You are implisitly ordering your elements, but how ORACLE knows how to sort them?
By dummy1 or dummy2 or ...?
You have to options:
do not use sorting
that basically means no DISTINCT, GROUP BY, ORDER BY and no UNION (you are OK with UNION ALL)
define sorting for your objects
Follow up about methods in ORACLE Types
Your oracle type could have additional methods, some of them are special, notice 'ORDER MEMBER FUNCTION' below (copy from the link):
<!-- language: pl/sql -->
CREATE OR REPLACE TYPE location_typ AS OBJECT (
building_no NUMBER,
city VARCHAR2(40),
ORDER MEMBER FUNCTION match (l location_typ) RETURN INTEGER
);
/
CREATE OR REPLACE TYPE BODY location_typ AS
ORDER MEMBER FUNCTION match (l location_typ) RETURN INTEGER IS
BEGIN
IF building_no < l.building_no THEN
RETURN -1; -- any negative number will do
ELSIF building_no > l.building_no THEN
RETURN 1; -- any positive number will do
ELSE
RETURN 0;
END IF;
END;
END;/
Its not a problem of fetch its kind of datatype mismatch. Opening cursors and fetching is correct what you have written.

How to return a Cursor for pl/sql table

I select data from several tables. Then i need to edit the data returned from the cursor before returning. The cursor will then be passed to a perl script to display the rows.
To that i build a pl/sql table as in the following code. What i need to know is how to return the to that table ?
At present i get the error "table or view doesn't exist". Test code i use for a simple table is attached here.
CREATE OR REPLACE FUNCTION test_rep
RETURN SYS_REFCURSOR
AS
CURSOR rec_Cur IS
SELECT table1.NAME,
table1.ID
FROM TESTREPORT table1;
TYPE rec_Table IS TABLE OF rec_Cur%ROWTYPE INDEX BY PLS_INTEGER;
working_Rec_Table rec_Table;
TYPE n_trade_rec IS RECORD
(
NAME VARCHAR2(15),
ID NUMBER
);
TYPE ga_novated_trades IS TABLE OF n_trade_rec index by VARCHAR2(15);
va_novated_trades ga_novated_trades;
v_unique_key VARCHAR2(15);
TYPE db_cursor IS REF CURSOR;
db_cursor2 db_cursor;
BEGIN
OPEN rec_Cur;
FETCH rec_Cur BULK COLLECT INTO working_Rec_Table;
FOR I IN 1..working_Rec_Table.COUNT LOOP
v_unique_key := working_Rec_Table(I).NAME;
va_novated_trades(v_unique_key).NAME := working_Rec_Table(I).NAME;
va_novated_trades(v_unique_key).ID := working_Rec_Table(I).ID;
END LOOP; --FOR LOOP
OPEN db_cursor2 FOR SELECT * FROM va_novated_trades; --ERROR LINE
CLOSE rec_Cur;
RETURN db_cursor2;
END test_rep;
/
Basically there is a way to select from a table type in oracle using the TABLE() function
SELECT * FROM table(va_novated_trades);
But this works only for schema table types and on plsql tables (table types defined in the SCHEMA and not in a plsql package):
CREATE TYPE n_trade_rec AS OBJECT
(
NAME VARCHAR2(15),
ID NUMBER
);
CREATE TYPE ga_novated_trades AS TABLE OF n_trade_rec;
But I still think you should try to do it all in a query (and/or in the perl script),
For example, there is one field where i have to analyse the 4th
character and then edit other fields accordingly
This can be achieved in the query, could be something like:
select case when substr(one_field, 4, 1) = 'A' then 'A.' || sec_field
when substr(one_field, 4, 1) = 'B' then 'B.' || sec_field
else sec_field
end as new_sec_field,
case when substr(one_field, 4, 1) = 'A' then 100 * trd_field
when substr(one_field, 4, 1) = 'B' then 1000 * trd_field
else trd_field
end as new_trd_field,
-- and so on
from TESTREPORT

Resources