ORA-00904: Invalid identifier when using forall - oracle

When i run the following block, I get the error:
ORA-00904: Invalid identifier in "forall".
Can somebody please help me fix it?
The column "ID" is an 12c identity column, so number.
drop table t1 cascade constraints purge;
create table t1 (
c1 number
);
set serveroutput on;
declare
type l_t2 is table of number;
l_c1 l_t2;
begin
select ID
bulk collect into l_c1
from IDTABLE;
dbms_output.put_line('Number of records: ' || sql%rowcount);
forall i in l_c1.first..l_c1.last
insert into t1 values l_c1(i);
end;
/

You're missing parentheses around the PL/SQL table reference in the values clause. Change this line:
insert into t1 values l_c1(i);
to
insert into t1 values (l_c1(i));
Without them it thinks l_cl is a schema-level object of some kind, which doesn't exist; hence the error you see. With them it works:
set serveroutput on;
declare
type l_t2 is table of number;
l_c1 l_t2;
begin
select ID bulk collect into l_c1 from IDTABLE;
dbms_output.put_line('Number of records: ' || sql%rowcount);
forall i in l_c1.first..l_c1.last
insert into t1 values (l_c1(i));
end;
/
PL/SQL procedure successfully completed.
Number of records: 2

Related

populating variable value dynamically in oracle

i have four tables as below : I want to insert the variable value v_col into the table test.This variable name is stored in another table test_lkp.when i try to run the code i am getting an error. How to achieve this.
DROP TABLE TEST ;
DROP TABLE TEST_LKP;
DROP TABLE TEST_REF;
DROP TABLE TEST_Q;
CREATE TABLE TEST (COL VARCHAR2(10));
CREATE TABLE TEST_LKP (COL VARCHAR2(10));
CREATE TABLE TEST_REF (VAL VARCHAR2(10));
CREATE TABLE TEST_Q (ENAME VARCHAR2(10));
INSERT INTO TEST_LKP VALUES ('v_col');
INSERT INTO TEST_REF VALUES ('ENAME');
INSERT INTO TEST_Q VALUES ('TOM');
INSERT INTO TEST_Q VALUES ('JIM');
INSERT INTO TEST_Q VALUES ('MARK');
COMMIT;
Actual code :
SET SERVEROUTPUT ON;
declare
v_col VARCHAR2(30);
BEGIN
SELECT VAL INTO v_col FROM TEST_REF;
dbms_output.put_line(v_col);
EXECUTE IMMEDIATE
' INSERT INTO TEST SELECT '|| V_COL ||' FROM TEST_Q ' ;
END;
/
expectation is to build the insert statement dynamically through the look up table so that if any change in the columns can be handled through the look up table rather than modifying the script.
To achieve i have created the look up table TEST_LKP with the same structure as TEST and inserting values as V_COL.
This is what i tried
SET SERVEROUTPUT ON;
declare
v_col VARCHAR2(30);
q VARCHAR2(1000);
CURSOR c1 IS
SELECT
'INSERT INTO TEST '
||
'SELECT ' || COL || ' FROM TEST_Q ' DMLS
FROM TEST_LKP;
BEGIN
SELECT val INTO v_col FROM TEST_REF;
dbms_output.put_line(v_col);
FOR i IN c1
loop
dbms_output.put_line(i.DMLS);
execute immediate i.dmls;
end loop;
END;
/
erorr :
Error report -
ORA-00904: "V_COL": invalid identifier
ORA-06512: at line 17
00904. 00000 - "%s: invalid identifier"
*Cause:
*Action:
ENAME
INSERT INTO TEST SELECT v_col FROM TEST_Q
Expected :
i want v_col to be replaced with ENAME as
INSERT INTO TEST SELECT ENAME FROM TEST_Q
any solution for this ?

"ORA-01007: variable not in select list" when no rows are returned by EXECUTE IMMEDIATE

I have a procedure which receives as parameter a where clause (i.e. where col1 = 1). I am using this clause to search in some tables using an EXECUTE IMMEDIATE statement and the result to be inserted into a nested table, and than be displayed.
The procedure works fine if any data is found but in case no data is found, then the above error is thrown.
Can someone explain what cause this error, please?
Here is the procedure:
create or replace procedure prc_checks(pi_where varchar2) as
cursor c_tables is
select object_name,
case object_name
when 'XP_IMPORT_MW' THEN 99999999
when 'XP_IMPORT_MW_ARCH' THEN 99999998
else TO_NUMBER(SUBSTR(object_name, -8, 8))
end to_order
from dba_objects
where object_type = 'TABLE'
and object_name IN ('XP_IMPORT_MW', 'XP_IMPORT_MW_ARCH')
or REGEXP_LIKE (object_name, 'XP_IMPORT_MW_ARCH_201(5|6|7)[0-9]{4}') order by 2 desc;
type t_result is table of xp_import_mw%rowtype;
v_result t_result;
v_sql varchar2(300);
BEGIN
for i in c_tables
loop
v_sql := 'select * from ' || i.object_name || ' ' || pi_where;
execute immediate v_sql bulk collect into v_result;
if v_result.count > 0
then
for j in v_result.first .. v_result.last
loop
dbms_output.put_line(v_result(j).art_nr);
end loop;
dbms_output.put_line('... the required information was found on table name ' || upper(i.object_name));
exit;
end if;
end loop;
END prc_checks;
You'll get this is one of the tables being found by the cursor has fewer columns than xp_import_mw. For example:
create table xp_import_mw (col1 number, art_nr number, dummy number);
create table xp_import_mw_arch_20160102 (col1 number, art_nr number, dummy number);
create table xp_import_mw_arch_20160101 (col1 number, art_nr number);
insert into xp_import_mw_arch_20160101 values (1, 42);
So the main xp_import_mw table has three columns but no matching data. One of the old archive tables has one fewer columns.
I added a dbms_output.put_line(v_sql) to the procedure to see which table it fails against, then ran it:
set serveroutput on
exec prc_checks('where col1 = 1');
which got output:
select * from XP_IMPORT_MW where col1 = 1
select * from XP_IMPORT_MW_ARCH_20160102 where col1 = 1
select * from XP_IMPORT_MW_ARCH_20160101 where col1 = 1
Error starting at line : 49 in command -
BEGIN prc_checks('where col1 = 1'); END;
Error report -
ORA-01007: variable not in select list
ORA-06512: at "MY_SCHEMA.PRC_CHECKS", line 25
ORA-06512: at line 1
01007. 00000 - "variable not in select list"
*Cause:
*Action:
So the problem isn't that there is no data found; the problem is that there is matching data in a table which has the wrong structure.
You could construct the select list based on the xp_import_mw table's structure, instead of using *; that won't stop it failing, but would at least give you a slightly more helpful error message - in this case ORA-00904: "DUMMY": invalid identifier instead of ORA-01007.
You could do a quick and crude check for discrepancies with something like:
select table_name, count(column_id) as column_count,
listagg(column_name, ',') within group (order by column_id) as columns
from dba_tab_columns
where table_name IN ('XP_IMPORT_MW', 'XP_IMPORT_MW_ARCH')
or REGEXP_LIKE (table_name, 'XP_IMPORT_MW_ARCH_201(5|6|7)[0-9]{4}')
group by table_name
having count(column_id) != (
select count(column_id) from dba_tab_columns where table_name = 'XP_IMPORT_MW'
);
... although if you're using dba_* or all_* view you should really be including the owner, here and in your procedure.

How to access a column whose name is stored in a variable?

create or replace
PROCEDURE PROCEDURE1
AS
MYCOLUMNVAR VARCHAR2(50);
BEGIN
SELECT <<some query>> INTO MYCOLUMNVAR FROM DUAL; --this query contains
-- a regular expression
-- and some other criteria
-- to store a column
-- name into MYCOLUMNVAR
INSERT INTO TABLE_TMP(ID,COLUMN1)
(SELECT a.ID,b.MYCOLUMNVAR FROM TAB1 a, TAB2 b WHERE...)
the value assigned to MYCOLUMNVAR from the above query is DATA. I have a column name DATA in the table TAB2. While trying to run the procedure I am getting
Error(50,3): PL/SQL: ORA-00904: "B"."MYCOLUMNVAR": invalid identifier.
You need a dynamic query to solve that kind of issue:
EXECUTE IMMEDIATE 'INSERT INTO TABLE_TMP(ID,COLUMN1)
(SELECT a.ID,b.' || MYCOLUMNVAR || ' FROM TAB1 a, TAB2 b WHERE...)'

Mutating table error thrown from stored procedure fired by an after insert trigger

I have a requirement to call a stored procedure via 'after insert' trigger whenever data is inserted into table but I run into "error ORA-04091: table TEST.EMP is mutating, trigger/function may not see it". I understand the reason behind this error but how can I overcome this through Compound Triggers without disturbing the procedure?
create TABLE emp(
id NUMBER(4),
emp_name VARCHAR2(30),
dept_name VARCHAR2(10));
create or replace PROCEDURE emp_count(dept_name_v emp.dept_name%TYPE) as
DECLARE
dept_emp_count NUMBER(4) := 0;
BEGIN
SELECT count(*) INTO dept_emp_count FROM emp WHERE dept_name = dept_name_v;
UPDATE dept_stat SET d_emp_count = dept_emp_count WHERE dept_name = dept_name_v;
END;
create or replace TRIGGER dept
AFTER INSERT ON emp
FOR EACH ROW
BEGIN
emp_count(:NEW.dept_name);
END;
There is an example in documentation how to create a compound trigger: http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CHDFEBFJ
Just change a few identifiers and declarations in this example, and you will get a trigger for your case:
CREATE OR REPLACE TRIGGER some_trigger
FOR INSERT ON emp
COMPOUND TRIGGER
TYPE dept_names_t IS TABLE OF emp.dept_name%TYPE INDEX BY SIMPLE_INTEGER;
dept_names dept_names_t;
idx SIMPLE_INTEGER := 0;
-- AFTER EACH ROW Section:
AFTER EACH ROW IS
BEGIN
idx := idx + 1;
dept_names(idx) := :NEW.dept_name;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
FOR j IN 1..idx
LOOP
emp_count(dept_names(j));
END LOOP;
END AFTER STATEMENT;
END;
/

Assigning more than one column to a collection using BULK COLLECT

I am not sure why I am getting this error with this code, Please help me with debugging this code Thanks in advance.
declare
type emp_t is table of employees%rowtype
index by pls_integer;
rec emp_t;
begin
select employee_id,salary,manager_id bulk collect into rec
from employees where rownum <100;
forall i in 1..rec.last
update employees
set salary=salary+10
where employee_id=rec(i).employee_id;
end;
ORA-06550: line 7, column 3:
PL/SQL: ORA-00913: too many values
ORA-06550: line 6, column 3:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
I have changed the code to the below format but it's still giving me "expression is of wrong type error"
declare
type emp_t is table of employees%rowtype
index by pls_integer;
rec emp_t;
begin
for val in (
select employee_id,salary,manager_id
from employees where rownum <100)
loop
rec:=val;
end loop;
forall i in 1..rec.last
update employees
set salary=salary+10
where employee_id=rec(i).employee_id;
end;
Use either one or the other:
TYPE emp_t IS TABLE OF employees%ROWTYPE INDEX BY PLS_INTEGER;
-- ^^^^^^^^^^^^^^^^^
-- each record is "one row" of table `employees`
...
SELECT * BULK COLLECT INTO rec FROM employees WHERE ROWNUM < 100;
-- ^
-- get all columns for each row
Or
TYPE emp_rec IS RECORD (
employee_id employees.employee_id%TYPE,
salary employees.salary%TYPE,
manager_id employees.manager_id%TYPE
);
TYPE emp_t IS TABLE OF emp_rec
-- ^^^^^^^
-- each record only contains the necessary fields
...
SELECT employee_id,salary,manager_id BULK COLLECT INTO rec FROM employees WHERE ROWNUM < 100;
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- get only what we need (and in that particular order)
Very probably you are looking for the second form. Using a custom type for the RECORD you are trying to bulk collect. Please notice the use of %TYPE attribute in the record declaration. That's an alias to the type of each column.
emp_t is declared as ROWTYPE, that includes all the columns,
but while retrieving records your are retrieving only employee_id,salary,manager_id,
that could be the reason of your error.
Try this :
declare
type emp_t is table of employees%rowtype
index by pls_integer;
rec emp_t;
begin
select employee_id,salary,manager_id bulk collect into rec
from employees where rownum <100;
forall i in 1..rec.last
update employees
set salary=salary+10
where employee_id=rec(i).employee_id;
end;
The second one seem to have other issues like TYPE difference VAR and REC

Resources