I have a dynamic sql :
forall k in 1..Job_ID.COUNT Save exceptions
EXECUTE IMMEDIATE 'insert into XYZ values(:1,:2,:3)' using sequence_one.NextVal,job_id(k),Name(k);
///Exception handling.
On running the abouve query only one row is getting inserted.And following error is thrown:
ORA-24381: error(s) in array DML.
Any idea why above thing is happening?
Do you really want every inserted row to have the same sequence value in the first column? If not you should do this:
forall k in 1..Job_ID.COUNT Save exceptions
EXECUTE IMMEDIATE 'insert into XYZ values(sequence_one.NextVal,:2,:3)'
using job_id(k),Name(k);
Or even better (unless you have a good reason for using dynamic SQL:
forall k in 1..Job_ID.COUNT Save exceptions
insert into XYZ values(sequence_one.NextVal, job_id(k), Name(k));
Related
I have been asked to create an SP which creates temporary table and insert some records.
I am preparing some sample code for the same as mentioned below but the output is not displayed.
create or replace procedure Test
is
stmt varchar2(1000);
stmt2 varchar2(1000);
begin
stmt := 'create global temporary table temp_1(id number(10))';
execute immediate stmt;
insert into temp_1(id) values (10);
execute immediate 'Select * from temp_1';
execute immediate 'Drop table temp_1';
commit;
end;
When i am executing the SP by (Exec Test) desired O/P is not displayed.
I am expecting O/P of "Select * from temp_1" to be displayed. But it is not happening. Please suggest where i am doing wrong.
But i am interesting in knowing why ( execute immediate 'Select * from temp_1';) do not yield any result
For two reasons. Firstly because as #a_horse_with_no_name said PL/SQL won't display the result of a query. But more importantly here, perhaps, the query is never actually executed. This behaviour is stated in the documentation:
If dynamic_sql_statement is a SELECT statement, and you omit both into_clause and bulk_collect_into_clause, then *execute_immediate_statement( never executes.
You would have to execute immediate into a variable, or more likely a collection if your real scenario has more than one row, and then process that data - iterating over the collection in the bulk case.
There is not really a reliable way to display anything from PL/SQL; you can use dbms_output but that's more suited for debugging than real output, and you usually have no guarantee that the client will be configured to show whatever you put into its buffer.
This is all rather academic since creating and dropping a GTT on the fly is not a good idea and there are better ways to accomplish whatever it is you're trying to do.
The block you showed shouldn't actually run at all; as you're creating temp_1 dynamically, the static SQL insert into temp_1 will error as that table does not yet exist when the block is compiled. The insert would have to be dynamic too. Any dynamic SQL is a bit of a warning sign you're maybe doing something wrong, though it is sometimes necessary; having to do everything dynamically suggests the whole approach needs a rethink, as does creating objects at runtime.
I am new to Oracle (11gr2) and I have the following script:
BEGIN
DECLARE
source varchar2(1);
BEGIN
dbms_output.enable;
BEGIN
EXECUTE IMMEDIATE 'DROP VIEW SP_AD;';
SELECT SOURCE INTO source FROM map_switch WHERE ROWNUM = 1;
IF source = 'A'
THEN
EXECUTE IMMEDIATE 'DROP TABLE SP_AD_B;';
EXECUTE IMMEDIATE 'RENAME TABLE SP_AD_A TO SP_AD;';
ELSE
EXECUTE IMMEDIATE 'DROP TABLE SP_AD_A;';
EXECUTE IMMEDIATE 'RENAME TABLE SP_AD_B TO SP_AD;';
END IF;
COMMIT WORK;
dbms_output.put_line('SP_AD table issue fixed');
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Exception, rolling back transaction, SP_AD not resolved.');
ROLLBACK WORK;
END;
END;
END;
/
Essentially, its determining which table to drop, then it drops the view and renames the other table.
If I run the statements individually, it works perfectly well, but in the script above, it returns procedure executed successfully but nothing was executed.
I'm suspecting that its rolling back for some odd reason, but I'm hesitating to execute it without the rollback in place (these tables have in excess of 300,000 records).
Can someone tell me what's wrong and also, is there something wrong with my exception block?
As pointed out by commenters, there are a few reasons why your code isn't working as expected.
Firstly, don't use semicolons inside the strings that you pass to EXECUTE IMMEDIATE, as doing that will give you an ORA-00911 'invalid character' error:
SQL> BEGIN
2 EXECUTE IMMEDIATE 'DROP TABLE SP_AD_B;';
3 END;
4 /
BEGIN
*
ERROR at line 1:
ORA-00911: invalid character
ORA-06512: at line 2
After running this, you can then verify that the table still exists:
SQL> SELECT * FROM SP_AD_B;
no rows selected
(I don't have your table SP_AD_B, so I just created one named SP_AD_B with a single integer column in it. I didn't bother putting any data in it.)
If you remove the semicolon inside the string, not the one outside, it works:
SQL> BEGIN
2 EXECUTE IMMEDIATE 'DROP TABLE SP_AD_B';
3 END;
4 /
PL/SQL procedure successfully completed.
SQL> SELECT * FROM SP_AD_B;
SELECT * FROM SP_AD_B
*
ERROR at line 1:
ORA-00942: table or view does not exist
Now that the table's gone, we get an error attempting to query it.
Hopefully, this should allow you to fix your script so that it works and drops the relevant tables.
But why weren't you getting any helpful information in your output message? Well, let's recreate the SP_AD_B table, and reintroduce the semicolon, and try dropping the table again, but with an EXCEPTION handler similar to yours:
SQL> BEGIN
2 EXECUTE IMMEDIATE 'DROP TABLE SP_AD_B;';
3 EXCEPTION
4 WHEN OTHERS THEN
5 dbms_output.put_line('Exception, rolling back transaction, SP_AD not resolved.');
6 END;
7 /
Exception, rolling back transaction, SP_AD not resolved.
PL/SQL procedure successfully completed.
In this case, we got an error message telling us something went wrong, so the table wasn't dropped. But what went wrong? There are thousands of errors that Oracle can report, and it can be difficult to guess what the problem is without knowing the error message.
There are a number of approaches you can take here. Firstly, you could write the error message, in SQLERRM to dbms_output:
SQL> BEGIN
2 EXECUTE IMMEDIATE 'DROP TABLE SP_AD_B;';
3 EXCEPTION
4 WHEN OTHERS THEN
5 dbms_output.put_line('Exception, rolling back transaction, SP_AD not resolved.');
6 dbms_output.put_line('Error message was: ' || SQLERRM);
7 END;
8 /
Exception, rolling back transaction, SP_AD not resolved.
Error message was: ORA-00911: invalid character
PL/SQL procedure successfully completed.
You can also use dbms_utility.format_error_backtrace to return the current stacktrace as a string, if you so wish. That might help you figure out where the error came from.
Alternatively, you can reraise the exception. Using RAISE on its own in an EXCEPTION handler reraises the current exception:
SQL> BEGIN
2 EXECUTE IMMEDIATE 'DROP TABLE SP_AD_B;';
3 EXCEPTION
4 WHEN OTHERS THEN
5 dbms_output.put_line('Exception, rolling back transaction, SP_AD not resolved.');
6 RAISE;
7 END;
8 /
Exception, rolling back transaction, SP_AD not resolved.
BEGIN
*
ERROR at line 1:
ORA-00911: invalid character
ORA-06512: at line 6
However, given the fact that your EXCEPTION handler isn't really doing anything helpful, the best approach is quite probably to get rid of it altogether.
Your exception handler doesn't achieve anything because you can't commit or rollback DDL statements such as CREATE, ALTER, DROP or TRUNCATE. Each of these statements issues a COMMIT immediately before and after it runs. If a DROP succeeds but a RENAME fails, you can't get the dropped table back by rolling back a transaction. I'd recommend getting rid of your COMMIT WORK and ROLLBACK WORK statements.
Finally, commenter Jeffrey Kemp noticed this line:
SELECT SOURCE INTO source FROM map_switch WHERE ROWNUM = 1;
This assigns into a variable named source the value of the column SOURCE from some arbitrary row of the table map_switch. It could be any row; as you haven't specified any ordering, Oracle is free to order the rows of map_switch however it likes.
If there's only one row in the table, then it's clear which row you'll get back. However, if this is the case, why specify ROWNUM = 1? Does the table have more than one row and is the ROWNUM = 1 part is just there to silence an 'exact fetch returns more than requested number of rows' error?
You would be better off doing something like the following:
SELECT SOURCE INTO source
FROM (SELECT SOURCE FROM map_switch ORDER BY some_column)
WHERE ROWNUM = 1;
I don't know what columns there are in your map_switch table, so I've just used some_column above as a placeholder for one of them. Choose a column that has unique values, if possible.
Note that we can't simply do SELECT ... WHERE ROWNUM = 1 ORDER BY some_column as that would apply the ROWNUM = 1 clause before doing the sorting, and there's not a lot of point sorting a single row as there's only one order it can be returned in.
Please help me in resolving below issue i am facing, i have to insert data into a table(table name genereted using variable value and table is created already) within FORALL..
Declare
TYPE dept_data_rec IS RECORD
(
Dept_no number,
Dept_name varchar2(100),
Dept_loc Varchar2(20)
);
TYPE nt_dept_data IS TABLE OF dept_data_rec ;
l_dept_data_nt nt_dept_data;
BEGIN
FORALL j IN 1..l_dept_data_nt.COUNT SAVE EXCEPTIONS
EXECUTE IMMEDIATE 'INSERT INTO '||l_get_dept_rec.dept_seq_no||'_Dept_Data VALUES '||
l_dept_data_nt(j);
COMMIT;
while compiling this code i am getting below error:
PLS-00306: wrong number or types of arguments in call to '||'
However when code using actual table name it works
FORALL j IN 1..l_dept_data_nt.COUNT SAVE EXCEPTIONS
INSERT INTO A1_dept_data VALUES
l_dept_data_nt(j);
COMMIT;
Oracle 10g -
In versions of Oracle prior to 11g, you can't use FORALL with EXECUTE IMMEDIATE, only with INSERT, UPDATE, or DELETE.
See http://docs.oracle.com/cd/B13789_01/appdev.101/b10807/13_elems021.htm
It's a special syntax
that reads like a FOR loop but isn't, and
is used by PL/SQL to perform bulked DML operations and only with the exact keyword, not with dynamic SQL or any other code.
Oracle 11g +
In 11g, the restriction on using EXECUTE IMMEDIATE was lifted. See http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/forall_statement.htm
However, the only variables allowed in the string are subscripted elements from a single array in the USING clause.
The documentation is unclear whether you can dynamically "change" the table per row using the FORALL syntax. Remember that the FORALL is used by PL/SQL to perform a bulk DML operation and that needs to go to one table for this to yield any performance benefit.
Best performance solution for the above problem
You should make two levels of arrays, the first defines which table and the second defines the data for that table.
Use an ordinary FOR loop over the table array and inside that loop use the special FORALL syntax to perform all the DML for the one table.
I would like to ask you how would you increase the performance on Insert cursor in this code?
I need to use dynamic plsql to fetch data but dont know how to improve the INSERT in best way. like Bulk Insert maybe?
Please let me know with code example if possible.
// This is how i use cur_handle:
cur_HANDLE integer;
cur_HANDLE := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cur_HANDLE, W_STMT, DBMS_SQL.NATIVE);
DBMS_SQL.DESCRIBE_COLUMNS2(cur_HANDLE, W_NO_OF_COLS, W_DESC_TAB);
LOOP
-- Fetch a row
IF DBMS_SQL.FETCH_ROWS(cur_HANDLE) > 0 THEN
DBMS_SQL.column_value(cur_HANDLE, 9, cont_ID);
DBMS_SQL.COLUMN_VALUE(cur_HANDLE, 3, proj_NR);
ELSE
EXIT;
END IF;
Insert into w_Contracts values(counter, cont_ID, proj_NR);
counter := counter + 1;
END LOOP;
You should do database actions in sets whenever possible, rather than row-by-row inserts. You don't tell us what CUR_HANDLE is, so I can't really rewrite this, but you should probably do something like:
INSERT INTO w_contracts
SELECT ROWNUM, cont_id, proj_nr
FROM ( ... some table or joined tables or whatever... )
Though if your first value there is a primary key, it would probably be better to assign it from a sequence.
Solution 1) You can populate inside the loop a PL/SQL array and then just after the loop insert the whole array in one step using:
FORALL i in contracts_tab.first .. contracts_tab.last
INSERT INTO w_contracts VALUES contracts_tab(i);
Solution 2) if the v_stmt contains a valid SQL statement you can directly insert data into the table using
EXECUTE IMMEDIATE 'INSERT INTO w_contracts (counter, cont_id, proj_nr)
SELECT rownum, 9, 3 FROM ('||v_stmt||')';
"select statement is assembled from a website, ex if user choose to
include more detailed search then the select statement is changed and
the result looks different in the end. The whole application is a web
site build on dinamic plsql code."
This is a dangerous proposition, because it opens your database to SQL injection. This is the scenario in which Bad People subvert your parameters to expand the data they can retrieve or to escalate privileges. At the very least you need to be using DBMS_ASSERT to validate user input. Find out more.
Of course, if you are allowing users to pass whole SQL strings (you haven't provided any information regarding the construction of W_STMT) then all bets are off. DBMS_ASSERT won't help you there.
Anyway, as you have failed to give the additional information we actually need, please let me spell it out for you:
will the SELECT statement always have the same column names from the same table name, or can the user change those two?
will you always be interested in the third and ninth columns?
how is the W_STMT string assembled? How much control do you have over its projection?
I am writing a PL/SQL parser to identify the operations(Select,Insert,Delete) performed on the Table when I run Procedure, Function or Package.
GOAL:I Goal of this tool is to identify which all the tables will be affected by running the procedure,Fun to prepare with better test case.
Any better ideas or tool will really help a lot.
INPUT:
some SQL file with procedure
or proc file.
OUTPUT required is:
SELECT from: First_table, secondTable
-> In procedure XYZ --This is if the procedure is calling one more procedure
INSERT into: SomeTable
INSERT into: SomeDiffTable
-> END of procedure XYZ --End of one more procedure.
DELETE from: xyzTable
INSERT into: OnemoreTable
My requirement is When I am parsing porc1 if it calls another proc2. I have to go inside that proc2 to find out what all the operation is performed in that and come back to proc1 and continue.:
For this I have to store the all procedure some where and while parsing I have to check each token(word with space) in the tempStorage to find out if it is procedure or not.
As my logic's takes lot of time. Can any body suggest better logic to achieve my GOAL.
There's also the possiblity of triggers being involved. That adds an additional layer of complexity.
I'd say you're better off mining DBA_DEPENDENCIES with a recursive query to determine impact analysis in the abstract; it won't capture dynamic SQL, but nothing will 100% of the time. In your case, proc1 depends on proc2, and proc2 depends on whatever it depends on, and so forth. It won't tell you the nature of the dependency - INSERT, UPDATE, DELETE, SELECT - but it's a beginning.
If you're really interested in determining the actual impact of a single-variable-value run of a procedure, implement it in a non-production system, and then turn auditing on your system up to 11:
begin
for i in (select owner, object_type, object_name from dba_objects
where owner in ([list of application schemas]
and object_type in ('TABLE', 'PACKAGE', 'PROCEDURE', 'FUNCTION', 'VIEW')
loop
execute immediate 'AUDIT ALL ON ' || i.owner || '.' || i.object_type ||
' BY SESSION';
end loop;
end;
/
Run your test, and see what objects got touched as a result of the exectution by mining the audit trail. It's not bulletproof, as it only audits objects that got touched by that execution, but it does tell you how they got touched.