Ora 01403 when try to insert data into oracle table - oracle

I have the following trigger
CREATE OR REPLACE TRIGGER L_BIUR_G_LAY
BEFORE INSERT OR UPDATE ON G_LAY
FOR EACH ROW
When (new.g_roo is not null)
DECLARE
x number(1);
stmt varchar(255);
BEGIN
FOR I IN (SELECT DISTINCT G_TAB FROM G_LEG)
LOOP
stmt := 'select distinct g_lind from ' || i.g_tab || ' where g_roo = ' || :new.g_roo;
Execute immediate stmt into x;
IF (x<>0) THEN
RAISE_APPLICATION_ERROR(-2001, 'G_ROO cannot be inserted where G_LIND IS NOT ZERO');
END IF;
END LOOP;
END;
/
and when I do
insert into G_LAY (G_OGCS, G_OGC, G_ROO, G_NM, G_TI, G_AB, G_DATE)
(select G_LAY_SEQ.NEXTVAL, 1, G_ROO, G_LGNDIT, G_UNE, 'Pipe Data Long - ' || G_UR, sysdate
from G_DTABLE where G_LIND = 0);
I get the following error
Error report:
SQL Error: ORA-01403: no data found
ORA-06512: at "L_BIUR_G_LAY", line 10
ORA-04088: error during execution of trigger 'G_LAY'
01403. 00000 - "no data found"
Any help would be greatly appreciated. It tried PRAGMA AUTONOMOUS_TRANSACTION after Declare in my trigger but it didn't help me

The problem is not with your INSERT statement. The problem occurs when the dynamically-built SELECT statement in the trigger is executed - no data is found so an ORA-01403 is raised. It looks to me like the easiest thing to do would be to do a SELECT COUNT(...):
CREATE OR REPLACE TRIGGER L_BIUR_G_LAY
BEFORE INSERT OR UPDATE ON G_LAY
FOR EACH ROW
When (new.g_rowno is not null)
DECLARE
x number;
stmt varchar(255);
BEGIN
FOR I IN (SELECT DISTINCT G_DISPCNTRLTAB FROM G_LEG) LOOP
stmt := 'select COUNT(*) from ' || i.g_dispcntrltab ||
' where g_rowno = ' || :new.g_rowno ||
' and g_lind <> 0';
Execute immediate stmt into x;
IF x <> 0 THEN
RAISE_APPLICATION_ERROR(-20001, 'G_RWNO cannot be inserted where G_LIND IS NOT ZERO');
END IF;
END LOOP;
END L_BIUR_G_LAY;
I also changed the error number which is raised as -2001 is not in the range of errors which can be raised using RAISE_APPLICATION_ERROR (the valid range is -20000 to -20999).
Share and enjoy.

Related

Oracle PL/SQL: Help resolving "PLS-00103: Encountered the symbol "LOOP" when expecting one of the following: if"

See Second Edit. New coding gets different error.
I am getting the "encountered a loop when expecting an if" error. My goal is to return a list of tables that have been updated by a specific project_id. The project_id is stored in a column on each table labeled project_id.
I used a with statement to create 2 tables. One table (sb_table) is a single column of all the tables that I want to look at. There are many more tables, but I figured it would speed things up if I narrowed down the list first. The other table I am creating (project) returns a single value turning the prjt_name provided into the actual project number (don't ask me why, but this is how my company has it set up, user creates a prct_name and is never aware of the project number).
Then I am trying to loop through the tables to see if they have the project number in their project_id column. If they do not, I delete them from the sb_table.
Ultimately, I am going to want to get all of the updated rows from all of the updated tables, but I am currently stuck on getting a list of the updated tables.
declare
query varchar2(10000);
table_count NUMBER;
update_count number;
prjt_name varchar2 not null := "01213264B";
cursor my_cur is select sbt.table_name from sb_table sbt;
begin
with sb_tables as (select table_name from all_tab_columns#db2 where
column_name = 'PROJECT_ID' and owner = 'SANDBOX'),
project as (select project_id from sandbox.sb_project#db2 where
project_name = upper(prjt_name))
--select sbt.table_name
--from sb_table sbt
for tableName in my_cur loop
query := 'select count(t.project_id) as "CNT" '||
'from sandbox.' || tableName || '#db2 t, project p '||
' where t.project_id = p.project_id ';
Execute immediate query
into update_count;
if update_count <= 0 then
query := 'DELETE FROM sb_tables where table_name = ' || tableName;
execute immediate query ;
end loop;
end;
Edit 1: Per comments, moved the select statement to the declare and am looping through my_cur now. I still get the same error.
Edit 2: Updated coding based on suggestions. I now get a different error message.
NEW ERROR: ORA-06550: line 12, column 16:
PLS-00306: wrong number or types of arguments in call to '||'
ORA-06550: line 12, column 7:
Edit 3: I was missing an = in my sub query which produced the error "Missing expression at line 13."
Edit 4: Now I get some results then error out with the following message
ORA-29913: error in executing ODCIEXTTABLEOPEN callout
ORA-29400: data cartridge error
KUP-04040: file ext_qsp_benefit.dat in DATA_DIR not found
ORA-02063: preceding 3 lines from ADHOC_POS15
ORA-06512: at line 13
Edit 5: Success! Apparently I cannot query certain tables. So I just took those tables out.
Final coding is:
declare
query varchar2(10000);
update_count integer := 0;
prjt_name varchar2(100) := '01213264B';
cursor my_cur is (select table_name from all_tab_columns#db2 where column_name = 'PROJECT_ID' and owner = 'SANDBOX' and table_name in ('X') );
tableName varchar2(100);
begin
open my_cur;
loop
fetch my_cur into tableName;
exit when my_cur%NOTFOUND;
update_count := 0;
execute immediate
'select count(project_id) as "CNT" from sandbox.' || tableName || '#db2 '
|| ' where project_id = (select project_id from sandbox.sb_project#db2 where project_name = ''' || prjt_name || ''' ) '
into update_count;
if update_count > 0 then
dbms_output.put_line (tableName);
end if;
end loop;
close my_cur;
end;
This doesn't do exactly what I wanted. It sends the results to dbms_output. But It is a start! Thanks everyone for you help!
DECLARE
update_count integer := 0;
prjt_name varchar2(100) := 'tttt';
mysql varchar2(100);
tablename varchar2(100);
cursor my_cur is
select 'DUAL'
from dual
where 'PROJECT_ID' = 'PROJECT_ID' and 'SANDBOX' = 'SANDBOX';
begin
open my_cur;
LOOP
FETCH my_cur into tablename;
EXIT WHEN my_cur%NOTFOUND;
update_count := 0;
mysql := 'select count(*) ' || ' from '
|| tablename
|| ' where ''TTTT'' = upper('''
|| prjt_name
|| ''')' ;
Execute immediate mysql INTO update_count;
dbms_output.put_line (mysql);
dbms_output.put_line (update_count);
END LOOP;
CLOSE my_cur;
end;
I tried one like yours. This executes successfully
declare
query varchar2(10000);
update_count integer := 0;
prjt_name varchar2(100) := '01213264B';
cursor my_cur is
select table_name
from all_tab_columns#adhoc_pos15
where column_name = 'PROJECT_ID' and owner = 'SANDBOX';
tableName varchar2(100);
begin
open my_cur;
loop
fetch my_cur into tableName;
exit when my_cur%NOTFOUND;
update_count := 0;
query := 'select count(t.project_id) as ''CNT'' from sandbox.'
|| tableName
|| '#adhoc_pos15 t'
|| ' where t.project_id = (select project_id from sandbox.sb_project#adhoc_pos15 where project_name = ''' || prjt_name || ''') ' ;
execute immediate query into update_count;
dbms_output.put_line (query);
dbms_output.put_line (update_count);
end loop;
close my_cur;
end;
i have tried your code with some execution, but before that you need to correct your with clause query. below code has executed except with clause so, make some change dependence on your requirements.
**code :**
declare
query varchar2(10000);
table_count NUMBER;
update_count number;
prjt_name varchar2 not null := '01213264B';
cursor my_cur is
select sbt.table_name from sb_table sbt;
begin
/* with sb_tables as (select table_name from all_tab_columns#db2 where
column_name = 'PROJECT_ID' and owner = 'SANDBOX'),
project as (select project_id from sandbox.sb_project#db2 where
project_name = upper(prjt_name))*/
for tableName in my_cur
loop
query := 'select count(t.project_id) into '|| update_count || 'from sandbox.' || tableName || '#db2 t, project p '||' where t.project_id = p.project_id ';
Execute immediate query;
--into update_count;
if update_count <= 0 then
query := 'DELETE FROM sb_tables where table_name = ' || tableName;
execute immediate query ;
end if;
end loop;
end;
If you run it in sql-developer please note, there are different buttons for running query (green triangle) and running script (green smaller triangle over a white document). Use 'run script' button

Trying to get the actual data that cause an exception

I have a procedure that takes an input of 2 Associative arrays and after some basic count checks, does a FORALL statement to insert the data into a table.
Here is the procedure:
PROCEDURE INSERT_RECS(P_PROD_TYP IN prod_type, P_PROD_ADD_PK IN prod_pk_type)
IS
uniq_key EXCEPTION;
PRAGMA EXCEPTION_INIT(uniq_key, -00001);
loc_cnt NUMBER;
BEGIN
IF P_PROD_TYP.COUNT = P_PROD_ADD_PK.COUNT THEN
FORALL i IN P_PROD_TYP.FIRST .. P_PROD_TYP.LAST
INSERT INTO product_table ( pk,
id,
created_by,
created_on,
last_chg_by,
last_chg_on)
VALUES (P_PROD_ADD_PK(i),
P_PROD_TYP(i).id,
P_PROD_TYP(i).created_by,
P_PROD_TYP(i).created_on,
NULL,
NULL);
END IF;
EXCEPTION
WHEN uniq_key THEN
loc_cnt := SQL%BULK_EXCEPTIONS.count;
FOR i IN 1 .. loc_cnt LOOP
dbms_output.put_line('EXCEPTION: Array Index: ' || SQL%BULK_EXCEPTIONS(i).error_index ||
' Message: ' || SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE) ||
' SQLERRM: ' || SQLERRM ||
' SQLCODE: ' || SQLCODE ||
' stack: ' || SYS.dbms_utility.format_call_stack);
END LOOP;
RETURN;
END;
What I would like is if I hit an exception, is there a way that I could have view of the record that is causing the issue, essentially the index in the associative array or at least have the SQL% info have the info.
I have look at the following:
http://www.dba-oracle.com/plsql/t_plsql_exceptions.htm
but this outputs the info about the column but that is not what I am after.
Not sure if really answers your query but you can use the loop variable i in exception block to display the content of the exception array in your case. See below an example procedure:
CREATE OR REPLACE PROCEDURE PROC1 (V_EMP_ID DBMS_SQL.NUMBER_TABLE)
IS
lv_error_string VARCHAR2(4000);
BEGIN
FORALL INDX IN V_EMP_ID.FIRST..V_EMP_ID.LAST SAVE EXCEPTIONS
UPDATE EMPLOYEES
---trying to rasie an exception by using a calculation
SET SALARY=SALARY * 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
WHERE ID_E= V_EMP_ID(INDX);
EXCEPTION
WHEN OTHERS
THEN
FOR i IN 1 .. SQL%BULK_EXCEPTIONS.COUNT
LOOP
---Am printing the value of the exception array.
dbms_output.put_line('exception Raised for record' ||V_EMP_ID(i));
END LOOP;
END;
/
Ouput:
SQL> DECLARE
empid DBMS_SQL.NUMBER_TABLE;
BEGIN
empid (1) := 1;
empid (2) := 9;
PROC1 (empid);
END;
/
exception Raised for record 1
PL/SQL procedure successfully completed.

Oracle, drop table if it exists AND empty

I need to drop an Oracle table only if it 1) exists AND 2) Is NOT Empty
I wrote this code but if the table does not exist the code does not work:
DECLARE
rec_cnt1 NUMBER :=0;
rec_cnt2 NUMBER :=0;
BEGIN
SELECT COUNT(*) INTO rec_cnt1 FROM ALL_TABLES WHERE TABLE_NAME = 'MyTable';
SELECT num_rows INTO rec_cnt2 FROM USER_TABLES WHERE TABLE_NAME = 'MyTable';
IF rec_cnt1 = 1 THEN
BEGIN
IF rec_cnt2 < 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE MyTable cascade constraints';
END IF;
END;
END IF;
END;
/
What am I doing wrong? Please help.
Many thanks in advance
If you want to drop a table if it exists and empty(as the title of the question states) you could do this as follows:
create or replace procedure DropTableIfEmpty(p_tab_name varchar2)
is
l_tab_not_exists exception;
pragma exception_init(l_tab_not_exists, -942);
l_is_empty number;
l_query varchar2(1000);
l_table_name varchar2(32);
begin
l_table_name := dbms_assert.simple_sql_name(p_tab_name);
l_query := 'select count(*)
from ' || l_table_name ||
' where rownum = 1';
execute immediate l_query
into l_is_empty;
if l_is_empty = 0
then
execute immediate 'drop table ' || l_table_name;
dbms_output.put_line('Table "'|| p_tab_name ||'" has been dropped');
else
dbms_output.put_line('Table "'|| p_tab_name ||'" exists and is not empty');
end if;
exception
when l_tab_not_exists
then dbms_output.put_line('Table "'|| p_tab_name ||'" does not exist');
end;
When you are trying to drop a table, or query a table, which does not exist, Oracle will raise ORA-00942 exception and execution of a pl/sql block halts. We use pragma exception_init statement to associate ORA-00942 exception with our locally defined exception l_tab_not_exists in order to handle it appropriately.
Test case:
SQL> exec droptableifempty('tb_test'); -- tb_test table does not exists
Table "tb_test" does not exist
SQL> create table tb_test(
2 col number
3 );
table TB_TEST created.
SQL> exec droptableifempty('tb_test');
Table "tb_test" has been dropped
As a side note. Before querying num_rows column of [dba][all][user]_tables in order to determine number of rows a table has, you need to gather table statistic by executing dbms_stats.gather_table_stats(user, '<<table_name>>');, otherwise you wont get the actual number of rows.
In PL/SQL it is 'normal' to catch the exception.
If it is the correct exception then continue with the next part of your code.
DECLARE
rec_cnt1 NUMBER :=0;
rec_cnt2 NUMBER :=0;
BEGIN
SELECT COUNT(*) INTO rec_cnt1 FROM ALL_TABLES WHERE TABLE_NAME = 'MyTable';
SELECT num_rows INTO rec_cnt2 FROM USER_TABLES WHERE TABLE_NAME = 'MyTable';
IF rec_cnt1 = 1 THEN
BEGIN
IF rec_cnt2 < 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE MyTable cascade constraints';
END IF;
END;
END IF;
EXCEPTION
DBMS_OUTPUT.PUT_LINE('OH DEAR AN EXCEPTION WAS THROWN DUE TO' || SQLERRM);
DBMS_OUTPUT.PUT_LINE('THE ORACLE CODE IS ' || SQLCODE);
-- if it is the oracle code for no such table, or no data selected
-- everything is fine.
END;
Of course it won't work if the table doesn't exist. Your second select would get a "No data found" exception, and you're not doing any exception handling. At least you should move the second select inside the first IF block. Best to add exception handling.
here is an easy way to solve this problem:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE [sssss]';
EXCEPTION WHEN OTHERS THEN NULL;
END;

Stored procedure exception handling

SQL> DECLARE
2 TotalUpd NUMBER(36) := 0;
3 BEGIN
4 dbms_output.put_line ('Job Start time............... : ' || to_char(SYSDATE, ' hh24:mi:ss'));
5 UPDATE Asset SET _status = 'PROGRESS' WHERE status is null;
6 TotalUpd := SQL%ROWCOUNT;
7 dbms_output.put_line('Total Records Updated. : ' || TotalUpd);
8 COMMIT;
9 EXCEPTION
10 WHEN NO_DATA_FOUND THEN
11 dbms_output.put_line ('No more data to update.');
12 WHEN OTHERS THEN
13 dbms_output.put_line ('Error while status as SUCCESS ');
14 END ;
15 /
The result for the above procedure is
Job Start time............... : 04:41:41
Total Records Updated. : 0
But my expected result is "No more row to be updated" must be printed,since i have truncated the table Asset.Please tell where I went wrong in this.
The NO_DATA_FOUND error is not thrown in update statements.
It is thrown in select into statements if the select statement would return nothing.
See also Tahiti on select into under select_item: *If the SELECT INTO statement returns no rows, PL/SQL raises the predefined exception NO_DATA_FOUND.*
Oracle does not consider it an exception if an update statement does not update anything, hence no exception is thrown. However, if a select into statement cannot fill the variables, it is considered an error (and therefore in this case the NO_DATA_EXCEPTION is thrown(
it is as simple as the update does not geneate an error if there is no data.
you need to look at the value of TotalUpd if you want to control the flow of your code
DECLARE
TotalUpd NUMBER(36) := 0;
BEGIN
dbms_output.put_line ('Job Start time............... : '
|| TO_CHAR(SYSDATE, ' hh24:mi:ss'));
UPDATE Asset SET _status = 'PROGRESS' WHERE status IS null;
TotalUpd := SQL%ROWCOUNT;
IF TotalUpd = 0 THEN
dbms_output.put_line ('No more data to update.');
ELSE
dbms_output.put_line('Total Records Updated. : '
|| TotalUpd);
END IF;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line ('Error while status as SUCCESS ');
END;
NO_DATA_FOUND is thrown if a select into does not return any row, not if no rows were updated after an update statement.
I suggest you move your logic for handling this exception after the update itself:
IF (SQL%ROWCOUNT = 0) THEN
dbms_output.put_line ('No more data to update.');
Think NO_DATA_FOUND exception is only raised by a SELECT statement which you aren't using. Try testing SQL%COUNT and output as required.

Execute For Each Table in PLSQL

I want to the the number of records in all tables that match a specific name criteria. Here is the SQL I built
Declare SQLStatement VARCHAR (8000) :='';
BEGIN
SELECT 'SELECT COUNT (*) FROM ' || Table_Name || ';'
INTO SQLStatement
FROM All_Tables
WHERE 1=1
AND UPPER (Table_Name) LIKE UPPER ('MSRS%');
IF SQLStatement <> '' THEN
EXECUTE IMMEDIATE SQLStatement;
END IF;
END;
/
But I get the following error:
Error at line 1
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 3
Script Terminated on line 1.
How do I modify this so that it runs for all matching tables?
Update:
Based on an answer received, I tried the following but I do not get anything in the DBMS_OUTPUT
declare
cnt number;
begin
for r in (select table_name from all_tables) loop
dbms_output.put_line('select count(*) from CDR.' || r.table_name);
end loop;
end;
/
declare
cnt number;
begin
for r in (select owner, table_name from all_tables
where upper(table_name) like ('%MSRS%')) loop
execute immediate 'select count(*) from "'
|| r.owner || '"."'
|| r.table_name || '"'
into cnt;
dbms_output.put_line(r.owner || '.' || r.table_name || ': ' || cnt);
end loop;
end;
/
If you're selecting from all_tables you cannot count on having been given the grants necessary to select from the table name. You should therefore check for the ORA-00942: table or view does not exist error thrown.
As to the cause for your error: You get this error because the select statement returns a result set with more than one row (one for each table) and you cannot assign such a result set to a varchar2.
By the way, make sure you enable dbms_output with SET SERVEROUT ON before executing this block.

Resources