I am new to Oracle PL/SQL. I have found this package and it is called from a trigger. I just cannot figure exactly what this simple looking package code is doing.
It is called from a trigger as below:
IF INSERTING THEN
i := STATE_PKG_OVERRIDE_CN.AffectedRows.COUNT+1;
STATE_PKG_OVERRIDE_CN.AffectedRows(i).IDU := :new.IDU;
STATE_PKG_OVERRIDE_CN.AffectedRows(i).cn := :new.cn;
This is the package. Can somebody please explain the basics of what is it doing? does it return a value? change a value? what is AffectedRows RIDARRAY or EMPTY ?
create or replace PACKAGE STATE_PKG_OVERRIDE_CN
AS
TYPE rowid_cn IS RECORD
(
idu dirxml.USR.IDU%TYPE,
cn dirxml.USR.CN%TYPE
);
TYPE RIDARRAY IS TABLE OF rowid_cn INDEX BY BINARY_INTEGER;
AffectedRows RIDARRAY;
EMPTY RIDARRAY;
END;
I have googled EMPTY but found nothing, i believe it is creating a table of type record. The trigger is passing in a value of cn or IDU am i am familiar with these two values. But what is the package doing? or returning ? im confused.
Cheers
This is a bespoke package belonging to your organisation. (That's why Google wasn't helpful for you.) We cannot tell you for sure what it does or how it's used. But we can guess.
The package has no procedures or functions, it just defines array variables of a bespoke type, which can be used by other program units such as triggers. The trigger you posted assigns values to the array AffectedRows. Presumably this trigger fires FOR EACH ROW. Likely there is another trigger on the same table firing AFTER STATEMENT which reads that array and does some processing, then assigns AffectedRows := EMPTY to reset the array.
The purpose of this infrastructure is to pass state across trigger actions. A common reason for doing this is work around a mutating table exception. They are risky because state cannot be guaranteed; for instance if a insert fails before the AFTER STATEMENT trigger fires the AffectedRows array is not re-initialised, so subsequent processing will be incorrect (or will fail).
Since 11g Oracle provides compound triggers which remove the need for this sort of package. Find out more.
To investigate further, first you want to check USER_TRIGGERS to find other triggers on the table which owns the trigger you mentioned. If that doesn't help or you want to see whether other tables also use this package run this query:
select *
from user_dependencies
where referenced_type = 'PACKAGE'
and referenced_name = 'STATE_PKG_OVERRIDE_CN'
It begins with the package which
declares a type ROWID_CN as a record that contains two values: IDU and CN
syntax requires to create another type (RIDARRAY) which is based on previously declared ROWID_CN
affectedrows and empty are arrays whose type is RIDARRAY. Basically, you can imagine them as a table that has two columns, IDU and CN
Now, a trigger: piece of code you posted says that those 3 lines are executed when someone inserts a row into a table the trigger is based on. For example,
create or replace trigger trg_biu_emp
before insert or update on emp
for each row
declare
i number;
begin
if inserting then ...
Apparently, there's some code that is executed when updating, or even deleting rows.
Anyway:
i := ... line counts number of elements in affectedrows array (which is declared in the package) and adds 1 to that number. For example, if there were 3 elements, i would be 4.
...affectedrows(i).idu := :new.idu enters a new row into the array. Its ordinal number is i (4 in our example). As you're inserting a row into the table, trigger knows IDU column's :new value and puts it into the array. For example, if you used
insert into emp (idu, cn) values (100, 'A')
then affectedrows(4).idu = 100, while affectedrows(4).cn = 'A'
It is, probably, something similar with EMPTY array. Google can't return anything useful, it is just a custom-made array.
Related
I am doing data insert into a table in Oracle which is having a sequence set to it in one of the columns say Id column. I would like to know how to do data loads into such tables.
I followed the below link -
It's possible to use OleDbConnections with the Script Component?
and tried to create a function to get the .nextval from the Oracle table but I am getting the following error -
Error while trying to retrieve text for error ORA-01019
I realized that manually setting the value via the package i.e. by using the Script task to enumerate the values but is not incrementing the sequence and that is causing the problem. How do we deal with it? Any links that can help me solve it?
I am using SSIS-2014 but I am not able to tag it as I don't due to paucity of reputation points.
I created a workaround to cater to this problem. I have created staging tables of the destination without the column that takes the Sequence Id. After the data gets inserted, I am then calling SQL statement to get the data into the main tables from staging table and using the .nextval function. Finally truncating/dropping the table depending on the need. It would still be interesting to know how this same thing can be handled via script rather having this workaround.
For instance something like below -
insert into table_main
select table_main_sequence_name.nextval
,*
from (
select *
from table_stg
)
ORA-01019 may be related to fact you have multiple Oracle clients installed. Please check ORACLE_HOME variable if it contains only one client.
One workaround I'm thinking about is creating two procedures for handling sequence. One to get value you start with:
create or replace function get_first from seq as return number
seqid number;
begin
select seq_name.nexval into seqid from dual;
return seqid;
end;
/
Then do your incrementation in script. And after that call second procedure to increment sequence:
create or replace procedure setseq(val number) as
begin
execute immediate 'ALTER SEQUENCE seq_name INCREMENT BY ' || val;
end;
/
This is not good approach but maybe it will solve your problem
I have a trigger that calls a stored procedure when activated, passing :NEW values as a parameter. I have about 40 tables that use the same trigger, and I would like to use the same code for each trigger. Therefore, I am trying to pass all columns of a new row. My code is below and shows what I am attempting to do (however, the problem is that :NEW.* is not a valid expression):
CREATE OR REPLACE TRIGGER "TRIG_TEST_TRIGGER"
AFTER INSERT OR DELETE OR UPDATE ON TRIG_TEST
FOR EACH ROW
DECLARE
BEGIN
MY_STORED_PROC('Trigger Activated: ' || :NEW.*);
END;
Most likely, you can't.
You could write a procedure that uses dynamic SQL to generate the appropriate trigger code for each table. Of course, that would require that you re-run the procedure to re-create the trigger every time the table changes.
I'm a bit hard-pressed, though, to imagine what my_stored_proc might be doing that it would make sense to pass it a string representing every column from 1 of 40 tables with, presumably, 40 different sets of columns. If you're writing to a log table, if you want the data from every column, that generally implies that you want to be able to see the evolution of a particular row over time. But that is extremely hard to do if your log table just has strings in all sorts of different formats from many different tables since you'd constantly have to do things like parsing the string that you logged.
I have a stored procedure made in Oracle 9g that returns a cursor with different columns depending on a parameter, its something like this:
CREATE OR REPLACE PROCEDURE ASCHEMA.SP_TWOCURSORS
(
aParam NUMBER,
P_RETURN OUT SYS_REFCURSOR
)
IS
BEGIN
IF aParam = 1 THEN
OPEN P_RETURN FOR
SELECT
a.column1, (number)
a.column2 (varchar2)
FROM
table1 a;
ELSE
OPEN P_RETURN FOR
SELECT
b.column1, (varchar2)
b.column2, (number)
b.column3 (number)
FROM
table1 b;
END IF;
END;
I have to consume this procedure in PowerBuilder and pass the returned data to a DataWindow1 or DataWindow2, depending on the returned cursor, these datawindows are filled in runtime by the execution of another procedures coming from other source. I can't modify the database objects (like split the sp in two), just the PowerBuilder code. My problem is how to handle this scenario in an elegant way. I have some ideas but don't know if it will work:
Create a DataWindow object that handles every column involved in both cursors returned from the sp, then copy each row to the expected DataWindow.
Create a DataStore and pass the sp with the Create method, then copy the rows in the expected DataWindow.
Execute the procedure dynamically, fetch every row and add each result into a new row of the expected DataWindow.
I haven't tried the first one because there are many columns and it will take a long time to do. The second looks good but I don't know how to handle a DataStore with no DataWindow object and don't know if this is possible (1). The third is my last option to solve this problem. I want to ask people before start implementing this solution because I'm new to PowerBuilder, and even if I won't work on it too long I want to do it in the right way.
Thanks for the help.
(1) I have found this article about using Custom DataStores but I don't know if I can use only 1 DataStore or I should use 2. Also, for the Oracle connection I don't use SQLCA but another transaction object, so I don't know how to do this.
Keep It Simple.
You know the details of the stored proc. If you are calling this sp from PB, you are knowing its aParam already before the call. Why not defining 2 datawindows, one for each version of the results ?
Each DW would have a retrieval argument (the one that is passed to the stored proc) and will get its result from the sp.
At runtime, depending on the retrieval argument, and before retrieving the values, assign the corresponding dataobject to the datawindow object that is on : either the DW that suits the aParam = 1 or the DW that suits the else part.
I need to write a sproc which performs some INSERTs on a table, and compile a list of "statuses" for each row based on how well the INSERT went. Each row will be inserted within a loop, the loop iterates over a cursor that supplies some values for the INSERT statement. What I need to return is a resultset which looks like this:
FIELDS_FROM_ROW_BEING_INSERTED.., STATUS VARCHAR2
The STATUS is determined by how the INSERT went. For instance, if the INSERT caused a DUP_VAL_ON_INDEX exception indicating there was a duplicate row, I'd set the STATUS to "Dupe". If all went well, I'd set it to "SUCCESS" and proceed to the next row.
By the end of it all, I'd have a resultset of N rows, where N is the number of insert statements performed and each row contains some identifying info for the row being inserted, along with the "STATUS" of the insertion
Since there is no table in my DB to store the values I'd like to pass back to the user, I'm wondering how I can return the info back? Temporary table? Seems in Oracle temporary tables are "global", not sure I would want a global table, are there any temporary tables that get dropped after a session is done?
If you are using Oracle 10gR2 or later then you should check out DML error logging. This basically does what you want to achieve, that is, it allows us to execute all the DML in a batch process by recording any errors and pressing on with the statements.
The principle is that we create an ERROR LOG table for each table we need to work with, using a PL/SQL built-in package DBMS_ERRLOG. Find out more. There is a simple extension to the DML syntax to log messages to the error log table. See an example here. This approach doesn't create any more objects than your proposal, and has the merit of using some standard Oracle functionality.
When working with bulk processing (that is, when using the FORALL syntax) we can trap exceptions using the built-in SQL%BULK_EXCEPTIONS collection. Check it out. It is possible to combine Bulk Exceptions with DML Error Logging but that may create problems in 11g. Find out more.
"Global" in the case of temporary tables just means they are permanent, it's the data which is temporary.
I would define a record type that matches your cursor, plus the status field. Then define a table of that type.
TYPE t_record IS
(
field_1,
...
field_n,
status VARCHAR2(30)
);
TYPE t_table IS TABLE OF t_record;
FUNCTION insert_records
(
p_rows_to_insert IN SYS_REFCURSOR
)
RETURN t_table;
Even better would be to also define the inputs as a table type instead of a cursor.
I'm having requirement to generate a insert script file from excel sheet. I'm partly successfully in generating script file. But i got struck in a situation,I need help from any1.
My logic is some thing like this, Read first cell,check if the value in the cell already exists in DB.If not, generate an insert script as follow
declare
PK1 integer;
begin
select tablename_seq.currval into PK1 from dual;
insert into TableName valuestablename_seq_seq.nextval,'Blagh',1);
end;
Im storing PK1 in hashtable with data has KEY .so that if the same data appears in the next rows,using Hashtable search, I will get the hashtable value for corresponding data key and pass it has parameter to another insert script. But every time i generate new variable like PK1,Pk2...etc.I have keep 'BEGIN' key word after Declare and also add 'END' key word after every insert,If i do so scope of variable goes out off scope.I may be using those declared variables in another insert statements has a parameter. Is there any chance of saving PK1,Pk2..... has session/Global variables for the script execution. So they wil become avialable for entire script execution time.
My inclination is to say that each line of your spreadsheet should just be creating a statement like insert into TableName values (tablename_seq_seq.nextval,'Blagh',1) returning ID into PK1;, then wrap the whole thing in a single DECLARE-BEGIN-END block with the appropriate variables defined, something like:
declare
pk1 integer;
pk2 integer;
begin
insert into TableName
values (tablename_seq_seq.nextval,'Blagh',1)
returning ID into PK1;
insert into TableName
values (tablename_seq_seq.nextval,'Urgh',2)
returning ID into PK2;
[...]
end;
You could even create the list of variable declarations in one column and the SQL in another, then copy and paste them into the right place in the block.
I'd start off with a
DECLARE
PROCEDURE action (p_val IN INTEGER) IS
...
END action;
BEGIN
Then have each line in the spreadsheet just do a call to the procedure so that a spreadsheet entry of 1 becomes
action (1);
Then you end up with something like
DECLARE
PROCEDURE action (p_val IN INTEGER) IS
...
END action;
BEGIN
action (1);
action (8);
action (23);
action (1);
action (1);
END;
The action procedure can be as complicated as you like, storing information in tables/arrays whatever.
Is the question about the best way to update a database from a spreadsheet, or the best way to generate a script from a spreadsheet?
I would recommend loading the spreadsheet data into a temporary table and then using a simple INSERT/SELECT statement, unless you're worried about uniqueness collisions in which case I would use a MERGE statement instead. This is much easier than trying to generate a script with logic for each insert statement.
Instead of each Line, Consider as each cell. Each cell will generate insert script into corresponding cell. I have created in the same way, problem is If i want to use PK1 variable some where in the row 10 ,column 10 (cell value) insert script,because we are ending 'end' immediately after Begin block, the scope of PK1 always be remain in Begin block.For this i have created Begin with one insert and then create another Begin with another insert and so on.and # the end im adding end;end;But the problem with above method is,I'm trrying to insert 200 row X 200 columns = 400 cells insert scripts. in this flow when i try to run script, it throws an runtime error ' Stack Overflow'
If you use Oracle E-Business, you might be interested by webadi.
This tool creates a excel files to be populated and then loaded into database via a procedure. You can then validate your data.
Creating custom Web ADI Integrators
WebADI - Using a Custom Integrator