PLS-00049: bad bind variable 'D_DEPT_ID' in toad - oracle

I created procedure with substitution variable in Toad for Oracle but its giving me error
"PLS-00049: bad bind variable 'D_DEPT_ID'"
Procedure Code:
CREATE OR REPLACE PROCEDURE add_dept
IS
dept_id dept.deptno%TYPE;
dept_name dept.dname%TYPE;
BEGIN
dept_id := :d_dept_id;
dept_name := ':d_ name';
INSERT INTO dept(deptno,dname) VALUES (dept_id,dept_name);
DBMS_OUTPUT.PUT_LINE (' INSERTED ' || SQL%ROWCOUNT || ' ROW ');
END;

You cannot place bind variables inside of procedure or function code. To accomplish your task, you must pass the values in as parameters of the procedure at runtime.
CREATE OR REPLACE PROCEDURE add_dept(dept_id dept.deptno%TYPE, dept_name dept.dname%TYPE) IS
BEGIN
INSERT INTO dept(deptno,dname) VALUES (dept_id,dept_name);
DBMS_OUTPUT.PUT_LINE (' INSERTED ' || SQL%ROWCOUNT || ' ROW ');
END;
Then at runtime, in your anonymous block or calling code, you pass the values in from the end user..
BEGIN
add_date(:d_dept_id, :d_dept_name);
END;

Related

Use Input Parameter of procedure to declare %Rowtype record

I am trying to insert the data into a table using Bulk Collect.
Here is the code:
create or replace procedure insert_via_bulk_collect authid current_user
as
lc_status number;
cursor lc_old_tb_data is select * from old_table_name;
type lc_old_tb_type is table of old_table_name%rowtype;
lc_old_tb_row lc_old_tb_type;
begin
open lc_old_tb_data;
loop
fetch lc_old_tb_data bulk collect into lc_old_tb_row;
forall i in 1..lc_old_tb_row.count
insert into new_table_name values lc_old_tb_row(i);
commit;
exit when lc_old_tb_data%notfound;
end loop;
close lc_old_tb_data;
end insert_via_bulk_collect;
It's working for me. But I want to pass the table name dynamically.
like
insert_via_bulk_collect(new_tb_nm varchar2(30), old_tb_name varchar2(30))
But I am not able to use those variables in cursor and declaring the record of OLD_TABLE%rowtype.
Is there a way to do this?
Try below code
CREATE OR REPLACE PROCEDURE INSERT_VIA_BULK_COLLECT
(NEW_TABLE_NAME varchar2, OLD_TABLE_NAME varchar2)
as
l_str varchar2(4000);
begin
l_str := 'declare
CURSOR lc_old_tb_data IS SELECT * FROM ' || OLD_TABLE_NAME || ';
TYPE lc_old_tb_type IS TABLE OF '|| OLD_TABLE_NAME || '%rowtype;
lc_old_tb_row lc_old_tb_type;
begin
Open lc_old_tb_data;
loop
fetch lc_old_tb_data bulk collect into lc_old_tb_row;
FORALL i IN 1..lc_old_tb_row.count
INSERT INTO ' || NEW_TABLE_NAME || ' VALUES lc_old_tb_row(i);
COMMIT;
exit when lc_old_tb_data%notfound;
end loop;
close lc_old_tb_data;
end; ';
dbms_output.put_line (l_str);
EXECUTE IMMEDIATE l_str;
end insert_via_bulk_collect;
You can add Exception handing. Also it is recommended to add limit for fetching of data as below fetch lc_old_tb_data bulk collect into lc_old_tb_row l_size; you can put l_size value as per your requirement.
As mentioned in comments, it is better to use direct insert ... select .... In your case, you can combine this way with execute immediate:
create or replace procedure insert_via_bulk_collect(new_tb_nm varchar2, old_tb_name varchar2) is
begin
execute immediate 'insert into ' || new_tb_nm ||
'(<columns list>) select <columns list> from ' || old_tb_nm;
end;

oracle returning ID in execute immediate giving "missing keyword" error

Following is an oracle procedure
create or replace
PROCEDURE INSERT_COMMON(
ENTITY_NAME IN VARCHAR2
, INSERT_QUERY IN varchar2
)
AS
NEW_ID NUMBER;
BEGIN
-- execute insert
DBMS_OUTPUT.PUT_LINE('INSERT_QUERY: ' || INSERT_QUERY);
-- execute IMMEDIATE INSERT_QUERY returning ID into NEW_ID;
-- above gives me a syntax error so using as below
execute IMMEDIATE INSERT_QUERY || ' returning ID into NEW_ID';
DBMS_OUTPUT.PUT_LINE('NEW_ID: ' || NEW_ID);
END INSERT_COMMON;
and Pl/SQL I am using
DECLARE
ENTITY_NAME VARCHAR2(200);
INSERT_QUERY VARCHAR2(200);
BEGIN
ENTITY_NAME := 'company';
INSERT_QUERY := 'INSERT INTO COMPANY (NAME) VALUES (''A Company 2'')';
INSERT_COMMON(ENTITY_NAME,INSERT_QUERY);
END;
This gives me following error
Error report:
ORA-00905: missing keyword
ORA-06512: at "SYSTEM.INSERT_COMMON", line 20
ORA-06512: at line 8
00905. 00000 - "missing keyword"
However, I have tested and following works fine
DECLARE
NEW_ID NUMBER;
BEGIN
INSERT INTO COMPANY (NAME) VALUES ('A Company 2') returning ID into NEW_ID;
DBMS_OUTPUT.PUT_LINE('NEW_ID: ' || NEW_ID);
END;
You need to specify a bind variable you're returning into in the dynamic sql statement, but you also then need to add the returning into <variable> clause to the execute immediate statement.
I believe (untested, since you didn't provide the statements to set up your table and associated triggers) that the following should sort your issue:
create or replace procedure insert_common (entity_name in varchar2,
insert_query in varchar2)
as
new_id number;
begin
-- execute insert
dbms_output.put_line ('INSERT_QUERY: ' || insert_query);
-- above gives me a syntax error so using as below
execute immediate insert_query || ' returning ID into :NEW_ID' returning into new_id;
dbms_output.put_line ('NEW_ID: ' || new_id);
end insert_common;
/

In Oracle PL/SQL can we insert bulk data dynamically?

I have a requirement to insert bulk data through procedure but the table and columns will be dynamic.
Suppose for one account there is one table and they have their configured columns, similarly there can be 1000 of such accounts, so there will 1000 number tables and their columns.
How can we achieve this in a single procedure?
A very primitive example, just so you get the feeling:
CREATE OR REPLACE PROCEDURE yourProcedure (tableName VARCHAR2,
colName1 VARCHAR2, colName2 VARCHAR2, colName3 VARCHAR2) IS
v_column VARCHAR2(30);
sql_stmt VARCHAR2(200);
BEGIN
sql_stmt := 'SELECT ' || colName1 || ', ' || colName2 || ', ' colName3 || ' FROM ' || tableName
EXECUTE IMMEDIATE sql_stmt;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE ('Node Data Found');
END yourProcedure;
You could pass an array of columns instead of 3 columns only, if you the columns you need to select are not known beforehand.
You can also bind variables to the select statement adding the "USING" clause. Refer to documentation examples: http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/dynamic.htm#CHDGJEGD

Create nested procedure in Oracle 10g

I want to create a nested procedure where in the 1st procedure, I want to create a table dynamically with 2 columns and in the 2nd procedure, I want to insert values into that table.
Below is the code I am trying to use; what am I doing wrong?
CREATE or replace PROCEDURE mytable (tname varchar2)
is
stmt varchar2(1000);
begin
stmt := 'CREATE TABLE '||tname || '(sname varchar2(20) ,sage number (4))';
execute immediate stmt;
end;
create PROCEDURE mytable1 (emp_name varchar2,emp_age number,tname varchar2)
is
stmt1 varchar2(1000);
begin
stmt1 := 'insert into '||tname||' values ('Gaurav' ,27)';
execute immediate stmt1;
end;
There's no need to create a nested procedure here. You can do everything in a single procedure.
Note my use of bind variables in the execute immediate statement
create or replace procedure mytable (
Ptable_name in varchar2
, Pemp_name in varchar2
, Pemp_age in number
) is
begin
execute immediate 'create table ' || Ptable_name
|| ' (sname varchar2(20), sage number (4))';
execute immediate 'insert into ' || Ptable_name
|| ' values (:emp_name, :emp_age)'
using Pemp_name, Pemp_age;
end;
More generally, there's no need to use execute immediate at all; creating tables on the fly is indicative of a poorly designed database. If at all possible do not do this; create the table in advance and have a simple procedure to insert data, should you need it:
create or replace procedure mytable (
, Pemp_name in varchar2
, Pemp_age in number
) is
begin
insert into my_table
values (Pemp_name, Pemp_age);
end;
I would highly recommend reading Oracle's chapter on Guarding Against SQL Injection.
If you really feel like you have to do this as a nested procedure it would look like this; don't forget to call the nested procedure in the main one as the nested procedure isn't visible outside the scope of the first.
create or replace procedure mytable (
Ptable_name in varchar2
, Pemp_name in varchar2
, Pemp_age in number
) is
procedure myvalues (
Pemp_name in varchar2
, Pemp_age in number
) is
begin
execute immediate 'insert into ' || Ptable_name
|| ' values (:emp_name, :emp_age)'
using Pemp_name, Pemp_age;
end;
begin
execute immediate 'create table ' || Ptable_name
|| ' (sname varchar2(20), sage number (4))';
myvalues ( Pemp_name, Pemp_age);
end;
Please see Oracle's documentation on PL/SQL subprograms

SYS_REFCURSOR as OUT parameter

I have a table contains (username-primarykey,password,age,gender);
have to create procedure like procedure(username in varchar,s_cursor out sys_refcursor);
procedure has to accept username and returns row (where username=in parameter )as cursor.
Rule:Cursor must and should be having unique sequence no along with the record it gives.
example:(unique no(sequence),username ,password,age,gender)
Every time procedure should return single record along with uniqueno(sequence)
You can try something like this, if you need more information you have to provide more details.
Create a sequence for unique no.
CREATE SEQUENCE emp_seq
MINVALUE 1
MAXVALUE 999999999999999999999999999
START WITH 1
INCREMENT BY 1
CACHE 20;
Create a procedure which returns sys_refcursor as OUT parameter and emp_id as IN parameter
CREATE OR REPLACE PROCEDURE get_employee_details (user_id
YOURTABLE.USERNAME%TYPE,
emp_cursor OUT SYS_REFCURSOR)
AS
BEGIN
OPEN emp_cursor FOR
SELECT emp_seq.NEXTVAL,
USERNAME,
PASSWORD,
AGE,
GENDER
FROM YOURTABLE
WHERE USERNAME = user_id;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.put_line ('<your message>' || SQLERRM);
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('<your message>' || SQLERRM);
END get_employee_details;
/
And to execute the procedure from sqlplus
variable usercur refcursor;
DECLARE
user_id YOURTABLE.USERNAME%TYPE;
BEGIN
user_id := 'JON';
get_employees(user_id,:usercur);
END;
/
print usercur
Update 1
I assume that you are calling your procedure from sqlplus or from Toad, then you could execute your procedure as
variable dcursor refcursor;
DECLARE
p_arrival DEFAULT_DETAILS.ARRIVALCOUNTRY%TYPE;
BEGIN
p_arrival := '123';
PROCEDURE_SAMPLE(p_arrival,:dcursor);
END;
/
print dcursor
Update 2
To execute procedure from SQL Developer, do as
var usercur refcursor
exec procedure_sample('AU',:usercur)
print usercur

Resources