While INSERT got error PLS-00904: stud.col3 is invalid identifier - oracle

In my stored procedure I want if the value of col1 & col2 match with employee then insert the unique record of the employee. If not found then match the value of col1, col2 & col3 with employee match then insert the value. If also not found while match all these column then insert the record by using another column.
Also one more thing that I want find list of values like emp_id by passing the another column value and if a single record can not match then make emp_id as NULL.
Also I want to insert one record at a time after match with txt along with others table having data like emp.
create or replace procedure sp_ex
as
cursor c1 is select * from txt%rowtype;
v_col1 tbl1.col1%type;
type record is table of txt%rowtype; --Staging table
v_rc record := record();
begin
open c1;
loop
fetch c1 bulk collect into v_rc limit 1000;
loop
for i in 1..v_rc.count loop
select col1 into v_col1 from tbl1
where exists (select col1 from tbl1 where tbl1.col1 = emp.col1);
insert
when txt.col1 = emp.col1 and txt.col2 = stud.col2 then
into main_table(columns) values(v_rc(i).col1, ...)
when txt.col1 = emp.col1 and txt.col2 = stud.col2 and txt.col3 = stud.col3 then
into main_table(columns) values(v_rc(i).col1, ...)
else
insert into main_table(columns) values(v_rc(i).col1, ...)
select * from txt;
end loop;
exit when v_rc.count < limit;
end loop;
close c1;
end sp_ex;
While emp, stud are the different tables where i have to match with txt.
In that Stored Proc I want to load data from txt into main_table in batch processing mode. The data would be match one by one record then after if matching condition match then load into the main table. How can i create the stored proc so that the Data will load by above logic one by one in batch processing. Could you please help me to share your idea. Thanks

The syntax seems to be rather mixed up.
Multi-table insert is like this:
insert all -- alternatively, "insert first"
when dummy = 'X' then
into demo (id) values (1)
when dummy = 'Y' then
into demo (id) values (2)
else
into demo (id) values (3)
select * from dual;
Or perhaps you wanted a PL/SQL case statement:
case
when dummy = 'X' then
insert into demo (id) values (1);
when dummy = 'Y' then
insert into demo (id) values (2);
else
insert into demo (id) values (3);
end case;
Instead there seems to be a mixture of the two.
Also there is a missing end loop, and an implicit cursor (select col1 from tbl1) with no into clause.

Related

Oracle trigger to increment value in another table after inserted in other

How to create trigger or procedure to add incrementally +1 in column VALUE in TABLE1 every single time when something is inserted in TABLE2 ?
Lets say that we start from VALUE.TABLE1 = 1 and I insert record in TABLE2. After that I should see 2 in VALUE column in TABLE1.
Then someone else is inserting record in TABLE2 and that procedure/trigger should add yet again +1 in VALUE and now it should be 3
etc.
DB is Oracle 11
Thanks.
Don't use table table1 and don't use a trigger.
Use a view instead:
create or replace view v_table1 as
select count(*) + 1 as value
from table2;
CREATE TRIGGER table2_trg
AFTER INSERT
ON table2
FOR EACH ROW
BEGIN
UPDATE table1 set value = value + 1;
END;
Or
CREATE or REPLACE TRIGGER TABLE2_trg
FOR INSERT
ON TABLE2
COMPOUND TRIGGER
cnt PLS_INTEGER := 0;
AFTER EACH ROW IS
BEGIN
cnt := cnt + 1;
END AFTER EACH ROW ;
AFTER STATEMENT IS
BEGIN
UPDATE table1 SET value = value + cnt;
END AFTER STATEMENT;
END;
I am not sure it is better or not and how it will work when some rows in table2 are dropped due to exceptions at the time of insertion.
It's better to use view if don't bother about deleted rows in table2.

Is it possible to return the Primary Key on an Insert as select statement - Oracle?

So I usually get the Primary Key of a newly inserted record as the following while using a trigger.
insert into table1 (pk1, notes) values (null, "Tester") returning pk1
into v_item;
I am trying to use the same concept but with an insert using a select statement. So for example:
insert into table1 (pk1, notes) select null, description from table2 where pk2 = 2 returning pk1
into v_item;
Note:
1. There is a trigger on table1 which automatically creates a pk1 on insert.
2. I need to use a select insert because of the size of the table that is being inserted into.
3. The insert is basically a copy of the record, so there is only 1 record being inserted at a time.
Let me know if I can provide more information.
I don't believe you can do this with insert/select directly. However, you can do it with PL/SQL and FORALL. Given the constraint about the table size, you'll have to balance memory usage with performance using l_limit. Here's an example...
Given this table with 100 rows:
create table t (
c number generated by default as identity,
c2 number
);
insert into t (c2)
select rownum
from dual
connect by rownum <= 100;
You can do this:
declare
cursor t_cur
is
select c2
from t;
type t_ntt is table of number;
l_c2_vals_in t_ntt;
l_c_vals_out t_ntt;
l_limit number := 10;
begin
open t_cur;
loop
fetch t_cur bulk collect into l_c2_vals_in limit l_limit;
forall i in indices of l_c2_vals_in
insert into t (c2) values (l_c2_vals_in(i))
returning c bulk collect into l_c_vals_out;
-- You have access to the new ids here
dbms_output.put_line(l_c_vals_out.count);
exit when l_c2_vals_in.count < l_limit;
end loop;
close t_cur;
end;
You can't use that mechanism; as shown in the documentation railroad diagram:
the returning clause is only allowed with the values version, not with the subquery version.
I'm interpreting your second restriction (about 'table size') as being about the number of columns you would have to handle, possibly as individual variables, rather than about the number of rows - I don't see how that would be relevant here. There are ways to avoid having lots of per-column local variables though; you could select into a row-type variable first:
declare
v_item number;
v_row table1%rowtype;
begin
...
select null, description
into v_row
from table2 where pk2 = 2;
insert into table1 values v_row returning pk1 into v_item;
dbms_output.put_line(v_item);
...
or with a loop, which might make things look more complicated than necessary if you really only ever have a single row:
declare
v_item number;
begin
...
for r in (
select description
from table2 where pk2 = 2
)
loop
insert into table1 (notes) values (r.description) returning pk1 into v_item;
dbms_output.put_line(v_item);
...
end loop;
...
or with a collection... as #Dan has posted while I was answering this so I won't repeat! - though again that might be overkill or overly complicated for a single row.

PLS 00357 Error- Table, View or Sequence "txt.col1" not allowed in the context

I have created one Stored Procedure. In that Stored Proc I want if the value of col1 & col2 match with employee then insert the unique record of the employee. If not found then match the value of col1, col2 & col3 with employee match then insert the value. If also not found while match all these column then insert the record by using another column.
Also one more thing that i want find list of values like emp_id by passing the another column value and if a single record can not match then make emp_id as NULL.
create or replace procedure sp_ex
AS
empID_in varchar2(10);
fname_in varchar2(20);
lname_in varchar2(30);
---------
type record is ref cursor return txt%rowtype; --Staging table
v_rc record;
rc rc%rowtype;
begin
open v_rc for select * from txt;
loop
fetch v_rc into rc;
exit when v_rc%notfound;
loop
select col1 from tbl1
Where EXISTS (select col1 from tbl1 where tbl1.col1 = rc.col1);
IF txt.col1 = rc.col1 AND txt.col2 = rc.col2 THEN
insert into main_table select distinct * from txt where txt.col2 = rc.col2;
ELSIF txt.col1 = rc.col1 AND txt.col2 = rc.col2 AND txt.col3 = rc.col3 THEN
insert into main_table select distinct * from txt where txt.col2 = rc.col2;
ELSE
insert into main_table select * from txt where txt.col4 = rc.col4;
end if;
end loop;
close v_rc;
end sp_ex;
I found an error while compile this Store Procedure PLS-00357: Table,View Or Sequence reference not allowed in this context. How to resolve this issue and how to insert value from staging to main table while using CASE or IF ELSIF statement. Could you please help me so that i can compile the Stored Proc.
Since I don't have your database to work with it's difficult to be 100% certain, but to my eye the line which reads
rc rc%rowtype;
should say
rc txt%rowtype;
You've defined the cursor v_rc as returning txt%rowtype, and your SQL statement used with this cursor is select * from txt, but that data type is at odds with the definition of rc. Thus, it appears you need to change rc as shown.
It also looks like the LOOP statement which comes immediately after exit when v_rc%notfound; should be removed, as there's nothing after that which would terminate that loop.
In addition, you have many references to columns in the txt table, e.g. IF txt.col1 = rc.col1. You can't refer to values in a table in this manner. I'm not quite sure what you're trying to do here so I can't really suggest anything.
Also, the statement
select col1 from tbl1
Where EXISTS (select col1 from tbl1 where tbl1.col1 = rc.col1);
is selecting a column from the database, but isn't putting it anywhere. This should be either a singleton SELECT (SELECT..INTO) or a cursor.
One more thing: you can't use distinct *. You need to use a column list with distinct.
Perhaps the following would be close to what you're trying to do:
create or replace procedure sp_ex
AS
begin
FOR rc IN (SELECT * FROM TXT)
LOOP
FOR t1 IN (SELECT *
FROM TBL1
WHERE TBL1.COL1 = rc.COL1)
LOOP
IF t1.COL1 = rc.COL1 AND
t1.COL2 = rc.COL2
THEN
insert into main_table
select *
from txt
where txt.col2 = rc.col2;
ELSIF t1.col1 = rc.col1 AND
t1.col2 = rc.col2 AND
t1.col3 = rc.col3
THEN
insert into main_table
select *
from txt
where txt.col2 = rc.col2;
ELSE
insert into main_table
select *
from txt
where txt.col4 = rc.col4;
END IF;
END LOOP; -- t1
END LOOP; -- rc
end sp_ex;
Best of luck.

PL SQL batch processing with insert from select

I have to move the data from table A to table B (they have almost the same fields).
What I have now is a cursor, that iterates over the records that has to be moved, insert one record in the destination table and updates the is_processed field in the source table.
Something like:
BEGIN
FOR i IN (SELECT *
FROM A
WHERE A.IS_PROCESSED = 'N')
LOOP
INSERT INTO B(...) VALUES(i....);
UPDATE A SET IS_PROCESSED = 'Y' WHERE A.ID = i.ID;
COMMIT;
END LOOP;
END;
The questions is, how to do the same using INSERT FROM SELECT(without the loop) and then update IS_PROCESSED of all the moved rows?
There is no BULK COLLECT INTO for INSERT .. SELECT
May be you can try this. I don't think it's better than your LOOP.
DECLARE
TYPE l_src_tp IS TABLE OF t_source%ROWTYPE;
l_src_rows l_src_tp;
BEGIN
SELECT *
BULK COLLECT INTO l_src_rows
FROM t_source;
FORALL c IN l_src_rows.first .. l_src_rows.last
INSERT INTO t_dest (td_id, td_value)
VALUES (l_src_rows(c).ts_id, l_src_rows(c).ts_value);
FORALL c IN l_src_rows.first .. l_src_rows.last
UPDATE t_source
SET ts_is_proccesed = 'Y'
WHERE ts_id = l_src_rows(c).ts_id;
END;
If you reverse the order and first make update and then insert you can use:
DECLARE
ids sys.odcinumberlist;
BEGIN
UPDATE a SET is_processed = 'Y' WHERE is_processed = 'N' RETURNING id BULK COLLECT INTO ids;
INSERT INTO b (id) SELECT column_value id FROM TABLE(ids);
COMMIT;
END;
In the SELECT you can join the ids table and get other data from other tables you want to insert into b.
Hello I should prefer pure SQL rather than PLSQL. I don't know why another update statement is requires for this simpler task. Let me know if this helps.
INSERT INTO <new table>
SELECT col1,col2,
.....,'Y'
FROM <old_table>
WHERE processed_in = 'N';

PL/SQL Extract Column Names and use in select statment

not sure if this is possible at all but im trying to do this with as little manual work as possible.
I have a table with 150 columns based on different combinations of factors.
I wish to extract the column names where a certain certain string is inside the column name.
I have done the following which does this. This is a basic example of what I have
--Create the table
Create Table temp
(id number,
Fac1_Fac2_Fac_3_Fac4_Fac5 number,
Fac1_Fac6_Fac_3_Fac4_Fac5 number,
Fac1_Fac6_Fac_7_Fac4_Fac5 number,
Fac1_Fac9_Fac_3_Fac4_Fac5 number,
Fac1_Fac10_Fac_3_Fac4_Fac5 number,
Fac1_Fac2_Fac_3_Fac11_Fac5 number,
Fac1_Fac2_Fac_3_Fac4_Fac12 number,
Fac13_Fac2_Fac_3_Fac4_Fac5 number);
Insert into temp Values (1,35634,3243,343,564,56,4635,3,334);
Insert into temp Values (2,3434234,3243,343,564,56,435,3,34234);
Insert into temp Values (3,5555,3243,33,564,56,435,3,3434);
Insert into temp Values (4,34234,343,343,564,56,4335,3,34);
commit;
--Extract Column Names
Select * from (
Select COLUMN_NAME
from user_tab_cols
where lower(table_name) ='temp'
)
where column_name like '%FAC13%'
--This is what I want to automate.
Select id, FAC13_FAC2_FAC_3_FAC4_FAC5
From temp
--I want the column name to come fron the select statment above as there may be lots of names.
Basically, I want to select all the rows from my table that have Fac13 in the column name all in one query if possible.
Thanks
I do not think you can do that in one query. First, your extract column names query can be simplified to one query as a cursor, and then use a dynamic select statement as follows:
CREATE OR REPLACE proc_dyn_select IS
CURSOR c1 IS
SELECT column_name
FROM user_tab_cols
WHERE LOWER(table_name) ='temp' and column_name LIKE '%FAC13%';
cols c1%ROWTYPE;
sqlstmt VARCHAR2(2000);
BEGIN
OPEN c1;
LOOP
FETCH c1 into cols;
EXIT WHEN c1%NOTFOUND;
sqlstmt := sqlstmt ||cols.column_name||',';
END LOOP;
CLOSE c1;
sqlstmt := 'select '||substr(sqlstmt, 1, length(sqlstmt)-1)||' FROM temp';
EXECUTE IMMEDIATE sqlstmt;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('error '||sqlerrm);
END;
/
Explanation
First, the cursor will store the columns that meet your conditions (to be from the table temp and the column names have the sub string FAC13. Then in execution section (after BEGIN), you will build your query dynamically using columns names stored in the cursor c1. With each round of the loop, a column name is added as a string and concatenated with a comma. So a string of columns will be built like this 'col1, col2, col3, ... coln,'. The string is stored in sqlstmt variable.
After the loop end, you amend the string to build sql statement, by adding the keywords SELECT, FROM and table name. However, we remove the last character of the sqlstmt variable, as it is an extra comma.
EXECUTE IMMEDIATE statement, will run the query stored in sqlstmt.
By using a procedure, you can always pass parameters, such that this procedure can perform any dynamic sql statement you want.

Resources