Appending BEGIN EXCEPTION lines into plsql file using sed - plsql

I have a few sql files which contain thousands of INSERT statements. The problem when executing these scripts is that some of them violate the unique constraint.
I originally did something like this:
BEGIN
INSERT INTO .....;
INSERT INTO .....;
...
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
null;
END;
/
EXIT;
However it seems the first instance where the exception occurs will cause the entire block to end, so the rest of the statements are not executed.
Since the Oracle version I'm using is not 11, I can't use the nice CONTINUE command. So I am thinking of doing something like this for each INSERT INTO statement:
BEGIN
INSERT INTO ....;
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
null;
END;
/
...
How can I use sed to read each line in the sql file, scan for the words "INSERT INTO" and if that exists, then append the BEGIN and EXCEPTION lines in between the INSERT INTO line? The reason is, sometimes a line is just a comment and I don't want to append to that.

It's not clear if you are trying to add BEGIN before a block of INSERT INTO lines, or if you simply want to add BEGIN/EXCEPTION lines around each INSERT INTO line. If the latter:
sed '/^ *INSERT INTO/{ i\
BEGIN
a\
EXCEPTION
}' input-file

You can fix this problem in more than one way. One sloution is to detect that record is not duplicate before inserting. This can be done like this
select cout(*) INTO v_count
Fron table1 tb
where tb.column_name1 = val1;
IF v_count == 0 THEN
insert into table1 values (val1, vale2....);
END IF;
Here you are inserting only when there is no duplication.
Another way can be create a procedure and call the procedure instead of insert.
Here is psedo code
create procedure insert_row (val1 varchar2, val2 varchar2)
IS
BEGIN
INSERT INTO ....;
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
null;
END;
This procedure will handle exception due to duplicate record in calling procedure.
You can also write a loop. That depend on your data and logic of the program. Pdedo code is
Loop
/* get the date from the cursor*/
fetch cursor_c into record_r
EXIT WHEN cursor%NOTFOUND;
BEGIN
INSERT INTO .....;
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
null;
END;
END LOOP;
For getting all inserts use simple grep -i statement. This will collect all inserts in file. You can write a PL/SQL or C/C++/Java language program to generate new SQL script.

Related

How to correctly make a procedure in Pl/SQL in which I create a TABLE and use a CURSOR

The assignment I am trying to do is
"Create a procedure that places the names of all presidents who were born in one specific
state, in a temporary table. Display the contents of this table."
The procedure complies but when I try to invoke it, it gives me:
00000 - "table or view does not exist"
Error(8,5): PLS-00103: Encountered the symbol "CREATE" when expecting one of the following: begin function pragma procedure subtype type current cursor delete exists prior
I have been stuck for a while now. Does anybody know what I am doing wrong?
My code so far is:
CREATE OR REPLACE PROCEDURE stateofpresident(p_state president.state_born%TYPE)
AS
CURSOR c_state IS
SELECT *
FROM president;
BEGIN
execute immediate 'CREATE TABLE presidentFromState;
(
president_name VARCHAR2
)';
FOR r_state IN c_state LOOP
IF(p_state = r_state.state_born) THEN
execute immediate 'INSERT INTO presidentFromState VALUES(r_state.pres_name)';
commit;
END IF;
END LOOP;
execute immediate 'DROP TABLE presidentFromState';
END stateofpresident;
/
SET SERVEROUT ON
BEGIN
stateofpresident('VIRGINIA');
END;
/
SELECT *
FROM presidentFromState;
The immediate cause of your error is the semi-colon (;) at "presidentFromState;" At run fhat terminates the statement and the SQL interpreter at that point does not know what is want, the create syntax is invalid. The statement compiles because at compile time it is a properly formatted string. That is why dynamic SQL should be avoid if at all possible. Your script also has an additional error. Your last select will fail as the table presidentFromState ws not only created but also dropped in the procedure. Finally, just an FYI, the entire FOR cursor and the cursor itself is entirely unnecessary, the entire operation can be completed is one statement: Look into the structure
Insert into table_name(columns)
Select columns ...
Since this obviously an assignment or tutorial I'll leave the exact for your research.

Query outside of the transaction scope - Oracle

Is it possible to have insert statement outside of the scope of a transaction in a Stored Proc?
So there is a stored proc that writes some inserts, as part of a transaction
The reason for this is to write to Audit table and keep the audit records even if the stored proc fails or gets an exception
clarification, sorry if it was not clear, I am writing multiple audits for each action of the procedure so I can keep track what it did and what failed. not just a single Audit when there is an exception...
CREATE OR REPLACE PROCEDURE sampleProc
IS
BEGIN
start a transaction
INSERT to table 1
write to audit table about insert 1
INSERT to table 2
write to audit table about insert 2
INSERT to table 3
write to audit table about insert 3
INSERT to table 4
write to audit table about insert 4
if there is an exception - rollback except audit
all ok? commit.
END;
/
Yes, there is an option to perform actions in a separate transaction running simulteneously with your main one. See detail here
Example:
create table logs(creation_date date default sysdate, msg varchar2(4000));
create or replace procedure log_proc(sMessage varchar2)
is
pragma autonomous_transaction;
begin
insert into logs(msg)
values(sMessage);
commit; -- don't forget to commit in this separate transaction
end;
/
begin
log_proc('some message');
rollback;
end;
/
select * from logs
What you're after is PRAGMA AUTONOMOUS_TRANSACTION, which kicks off the module using it in a separate session.
Your code would be something like:
CREATE OR REPLACE PROCEDURE sample_proc
AS
procedure audit_insert (<params>)
is
pragma autonomous_transaction;
begin
<log details>
commit;
end audit_insert;
BEGIN
<INSERT to table 1>;
audit_insert(...);
<INSERT to table 2>;
audit_insert(...);
...
EXCEPTION
when others then
rollback;
raise;
END sample_proc;
/
Doing it like this would mean your audit details would be saved regardless of whether the calling code succeeds or fails.
N.B. I've created the audit_insert as a sub procedure of sample_proc. You would do better to have the code as individual procedures inside a package, rather than as one or more procedures.

Do I need to commit inside a subprogram that is called by an autonomous transaction procedure?

If I have a procedure that is AUTONOMOUS_TRANSACTION that does an insert and then it calls a procedure with an insert, does the second procedure need a commit, or will the procedure with the AUTONOMOUS_TRANSACTION handle the commit?
The answer is NO. The "second" procedure - invoked by the first - does not have to include a COMMIT statement. When you add this statement to the declaration section of a procedure or function...
PRAGMA AUTONOMOUS_TRANSACTION;
the following rule then applies:
Before the subprogram can be closed and control passed back to the
calling block, any DML changes made within that subprogram must be
committed or rolled back.
If there are any unsaved changes, the PL/SQL engine will raise the ORA-06519 exception, as shown below:
CREATE OR REPLACE FUNCTION nothing RETURN INTEGER
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
UPDATE employees SET last_name = 'abc';
RETURN 1;
END;
/
BEGIN
DBMS_OUTPUT.put_line (nothing);
END;
/
ORA-06519: active autonomous transaction detected and rolled back
ORA-06512: at "STEVEN.NOTHING", line 10
ORA-06512: at line 2
OK, so that's the basic idea. Now let's move on the specific question. What if an autonomous transaction procedure calls another procedure, which does not include the pragma shown above but does execute a DML statement and does not commit? Will we see an ORA-06519 error? The code below shows that we will not.
CREATE TABLE me_and_my_lovelies (name VARCHAR2 (100));
BEGIN
INSERT INTO me_and_my_lovelies VALUES ('Grandpa Steven');
INSERT INTO me_and_my_lovelies VALUES ('Loey');
INSERT INTO me_and_my_lovelies VALUES ('Juna');
COMMIT;
END;
/
CREATE OR REPLACE PROCEDURE not_auton_no_commit
AUTHID DEFINER
IS
BEGIN
UPDATE me_and_my_lovelies
SET name = UPPER (name);
END not_auton_no_commit;
/
CREATE OR REPLACE PROCEDURE auton_commit
AUTHID DEFINER
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
not_auton_no_commit ();
COMMIT;
END auton_commit;
/
BEGIN
auton_commit;
END;
/
SELECT COUNT(*) low_name
FROM me_and_my_lovelies
WHERE name <> UPPER (name)
/
LOW_NAME
--------
0
No error is raised. All rows have been updated. So let's go back to the rule:
Before the subprogram can be closed and control passed back to the calling block, any DML changes made within that subprogram must be committed or rolled back.
You might be thinking: But the UPDATE statement (the "DML change") was not made "within" auton_commit. Yes and no. Yes, the UPDATE statement is not part of the text of auton_commit. But the UPDATE statement was executed within the scope of auton_commit. And that's what counts. Any code executed by auton_commit, either "directly" in its executable section or "indirectly" by invoking another subprogram, is part of the autonomous transaction.
The only point at which the rule is applied is when PL/SQL attempts to close auton_commit and return control to the outer block.
LiveSQL script containing the above code here.
More information about autonomous transactions here.
Note: this Q&A was taken from by blog. Full post here.

Using sequences as primary key

I have one table called INTER and I want to transfer some data from INTER to FINAL (a new table which is already created and empty) using a procedure.
I have to make use of sequence as a primary key for the FINAL table. INTER is the parent table and it has all columns present in FINAL.
I have created a sequence M_SQ. Here is what I tried:
create or replace
PROCEDURE STAGING_TO_CUSTOMER
AS
BEGIN
INSERT INTO FINAL (C_ID,C_NAME,C_PHONE,C_ADDRESS)
SELECT M_SQ.NEXTVAL,C_NAME,C_PHONE,C_ADDRESS FROM INTER;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END;
This procedure gets compiled but no records are transferred to FINAL.
Can somebody help? I am using sqldeveloper 11g.
you actually have to run the procedure for it to do what is inside it:
BEGIN
STAGING_TO_CUSTOMER;
END;
Also, your procedure might as well look like:
CREATE OR REPLACE PROCEDURE STAGING_TO_CUSTOMER AS
BEGIN
INSERT INTO FINAL (C_ID,C_NAME,C_PHONE,C_ADDRESS)
SELECT M_SQ.NEXTVAL,C_NAME,C_PHONE,C_ADDRESS FROM INTER;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
So you know if something happens.
You do not need a stored proc - you can just run the command as is:
INSERT INTO FINAL (C_ID,C_NAME,C_PHONE,C_ADDRESS) SELECT M_SQ.NEXTVAL,C_NAME,C_PHONE,C_ADDRESS FROM INTER;
COMMIT;

Execute Immediate

Can any one help me to to find out the error.
create or replace procedure sample
is
begin
DECLARE AGG_COLUMNS VARCHAR2(2000);
BEGIN
EXECUTE IMMEDIATE 'SELECT COLUMNS FROM COLUMN_NAMES' INTO AGG_COLUMNS;
END;
begin
EXECUTE IMMEDIATE
'CREATE TABLE NEW_BW_COLUMN_ROW_CELL_JOIN AS
(
SELECT *
FROM BW_COLUMN_ROW_CELL_JOIN
PIVOT
(
MAX(STRING_VALUE)
FOR COLUMN_NAME IN ('||AGG_COLUMNS||')))';
END;
end;
Error(9,5): PL/SQL: Statement ignored
Error(17,36): PLS-00201: identifier 'AGG_COLUMNS' must be declared
Thanks
The immediate error is that the local variable AGG_COLUMNS is declared in the first nested PL/SQL block. That means that it is out of scope as soon as the first nested block completes. You cannot, therefore, use it in the second nested PL/SQL block. You can fix that by declaring the local variable in your procedure's declaration section. You can also get rid of the nested PL/SQL blocks
create or replace procedure sample
is
AGG_COLUMNS VARCHAR2(2000);
begin
EXECUTE IMMEDIATE 'SELECT COLUMNS FROM COLUMN_NAMES' INTO AGG_COLUMNS;
EXECUTE IMMEDIATE
'CREATE TABLE NEW_BW_COLUMN_ROW_CELL_JOIN AS
(
SELECT *
FROM BW_COLUMN_ROW_CELL_JOIN
PIVOT
(
MAX(STRING_VALUE)
FOR COLUMN_NAME IN ('||AGG_COLUMNS||')))';
end;
This should remove the immediate compilation error. I'm not sure, though, whether that resolves all your problems.
COLUMN_NAMES isn't a table in a default Oracle install. This would have to be something that you created in order for this code to run.
If you did create the COLUMN_NAMES table and COLUMNS stores a comma-separated string, then there is no need to use dynamic SQL to query the table. You can do a simple SELECT ... INTO.

Resources