procedure using loop in oracle - sql

I used oracle
I want to declare a procedure that allows me to make insertion in a table
I try with this code without success
CREATE OR REPLACE PROCEDURE ADDSTEP(nbrStep character varying)
is
i integer :=0;
BEGIN
FOR i IN 0..nbrStep LOOP
INSERT INTO mytabletest
VALUES (i);
END LOOP;
END;
I have this error :
PROCEDURE ADDSTEP compiled
Errors: check compiler log

There are multiple issues with your code:
nbrStep character varying
There is no such data type called "character varying" in Oracle. For string you would use VARCHAR2. However, since you want to use it later in the loop for iteration, you need it to be NUMBER.
FOR i IN 0..nbrStep LOOP
You need to iterate from 1 till the boundary.
i integer :=0;
Not needed.
Modify the procedure as:
CREATE OR REPLACE PROCEDURE ADDSTEP(nbrStep NUMBER)
is
i integer :=0;
BEGIN
FOR i IN 0..nbrStep LOOP
INSERT INTO mytabletest
VALUES (i);
END LOOP;
END;
/
Anyway, you entire procedure could be done in a single INSERT SQL. It is called row generator method.
Try,
INSERT INTO mytabletest
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 100;
Above, in place of 100, you could use the value of your choice which is the value you are passing as parameter **nbrStep ** in the above procedure.

Strange syntax of input variables. Corrected for Oracle -
CREATE OR REPLACE PROCEDURE addstep(nbrstep IN NUMBER)
IS
BEGIN
FOR i IN 0.. nbrstep
LOOP
INSERT INTO mytabletest
VALUES (i);
END LOOP;
END;

Related

Redshift dollar quoting inside a procedure

I have a procedure which accepts the table name and the 3 fields that need filling. This is to be called from another procedure that loops through another table deciding which sub-table to put stuff into. The important bit is a simple insert statement, e.g.
insert into table1 values
('blah','String','50');
So that 4 parameters coming in (table1, and the 3 values). How would I do this using dollar quoting? Obviously this doesnt work but gives you an idea of what I'm trying to accomplish:
create or replace procedure
insert_dc_table(p_tblname varchar,
p_name varchar,
p_datatype varchar,
p_datalen varchar)
as $$
begin
execute
'insert into '||p_tblname||'(name,datatype,datalen) values '
||'('
||p_name||', '
||p_datatype||', '
||p_datalen
||')';
end;
$$ language plpgsql;
I'd need double-dollars around some, but am unsure of exactly where $$ and quotes go in all this !*&#!
I could declare a variable to hold the execute statement and do:
declare a _output varchar(200);
a_output := $$ insert into $$||p_tblname||$$(name,datatype,datalen) values ( '$$||p_name||$$',
well, i get lost there!
Thanks in advance for help!
Redshift Stored Procedures only require dollar quoting of the procedure body. Quotes inside the the procedure body are interpreted as normal.
You may find that your SQL client does not submit the SP create correctly due to the dollar quotes. If so I recommend using psql to create the stored procedure.
Sample stored procedures are available in our "Amazon Redshift Utils" GitHub repository.
Here's a modification of your example:
-- DROP PROCEDURE insert_dc_table(VARCHAR,VARCHAR ,VARCHAR ,VARCHAR);
CREATE OR REPLACE PROCEDURE insert_dc_table(
p_tblname VARCHAR, p_name VARCHAR, p_datatype VARCHAR, p_datalen VARCHAR )
AS $$
DECLARE
rows INTEGER;
BEGIN
sql := 'INSERT INTO '||p_tblname||' (name, datatype, datalen)'
||' VALUES ('||p_name||','||p_datatype||','||p_datalen||');';
RAISE INFO 'Running SQL: %', sql;
EXECUTE sql;
GET DIAGNOSTICS rows := ROW_COUNT;
RAISE INFO 'Rows inserted = %', rows;
END
$$ LANGUAGE plpgsql;
-- CALL insert_dc_table ('test_table', 'name', 'type', 'length');

Inserting multipe values in PL/SQL

I am trying to insert values into a table using a for loop. I can't understand why it isnt working. I am a beginner. Any help would be appreciated.
create or replace procedure valueinput
as i number;
begin
for i in 1..10
loop
man_id number:=&Manager_Id;
man_fname varchar2(100):=&First_Name;
man_lname varchar2(100):=&Last_Name;
emp_id number:=&Employee_ID_Managed;
insert into managerinfo
values (man_id,man_fname,man_lname,emp_id);
i:=i+1;
end loop;
end;
You must declare your variables in the DECLARATION section of your PL/SQL block, right now you're trying to do it in the EXECUTION section.
Also, for a PL/SQL FOR..IN loop, you don't need to declare or increment your index.
Try something like this-
create or replace procedure valueinput
as
man_id number;
man_fname varchar2(100);
man_lname varchar2(100);
emp_id number;
begin
for i in 1..10
loop
man_id :=&Manager_Id;
man_fname :=&First_Name;
man_lname :=&Last_Name;
emp_id :=&Employee_ID_Managed;
insert into managerinfo
values (man_id,man_fname,man_lname,emp_id);
end loop;
end;
It would also be best practice to declare your variable types as the same as the table column that you're inserting into.
For example-
man_id managerinfo.manager_id%TYPE;

Unable to call a stored procedure from the trigger

CREATE OR REPLACE PROCEDURE UPDATE_AGE_VALUES
IS
cursor_ssn_number tbl_Patient.ssn_number%type;
cursor_patient_age tbl_Patient.patient_age%type;
age1 tbl_Patient.patient_age%type;
age2 tbl_Patient.patient_age%type;
age3 tbl_Patient.patient_age%type;
ssn_number1 tbl_Patient.ssn_number%type;
ssn_number2 tbl_Patient.ssn_number%type;
ssn_number3 tbl_Patient.ssn_number%type;
average number:=0;
i number:=1;
CURSOR cursor_tbl_Patient IS
SELECT ssn_number,patient_age FROM tbl_Patient ORDER BY patient_age ASC;
BEGIN
OPEN cursor_tbl_Patient;
LOOP
FETCH cursor_tbl_Patient into cursor_ssn_number,cursor_patient_age;
EXIT WHEN cursor_tbl_Patient%NOTFOUND;
IF i=1 THEN
age1:=cursor_patient_age;
ssn_number1:=cursor_ssn_number;
i:=i+1;
ELSIF i=2 THEN
age2:=cursor_patient_age;
ssn_number2:=cursor_ssn_number;
i:=i+1;
ELSIF i=3 THEN
age3:=cursor_patient_age;
ssn_number3:=cursor_ssn_number;
average:=(age1+age2+age3)/3;
UPDATE tbl_Patient SET patient_age=average where ssn_number IN (ssn_number1,ssn_number2,ssn_number3);
i:=1;
average:=0;
commit;
END IF;
END LOOP;
CLOSE cursor_tbl_Patient;
END;
/
CREATE OR REPLACE TRIGGER CHANGE_ROW_VALUES
AFTER INSERT ON tbl_Patient
FOR EACH ROW
BEGIN
CALL UPDATE_AGE_VALUES;
END;
/
Above code tries to modify the row values after a row is inserted. The procedure is created a execute.But I'm not able to call the procedure from the Trigger. I don't know why.
The error I'm getting is :
2/7 ,PLS-00103: Encountered the symbol "UPDATE_AGE_VALUES" when
,expecting one of the following:
,:= . ( # % ;
,The symbol ":=" was substituted for "UPDATE_AGE_VALUES" to
,continue.
Am I missing out anything?
Thanks in advance!!!
CALL is not a valid statement in PL/SQL. To call a procedure in PL/SQL you just need to give its name, as in:
CREATE OR REPLACE TRIGGER CHANGE_ROW_VALUES
AFTER INSERT ON tbl_Patient
FOR EACH ROW
BEGIN
UPDATE_AGE_VALUES;
END;
Your next problem will be that the UPDATE_AGE_VALUES reads and updates tbl_Patient, which will fail because UPDATE_AGE_VALUES is called from a row trigger which is defined on tbl_Patient. If you remove the FOR EACH ROW from the trigger it might work, but I won't answer for the performance.
Share and enjoy.
CREATE OR REPLACE TRIGGER CHANGE_ROW_VALUES
AFTER INSERT ON tbl_Patient
FOR EACH ROW
BEGIN
UPDATE_AGE_VALUES();
END;
This worked for me!!!

Sending multiple sets of parameters to procedure

I'm using vb.net and oracle db, and currently I have a stored-procedure that is called from my code. Right now it looks similar to this:
CREATE OR REPLACE PROCEDURE MYPROCEDURE(
param1 table.field1%TYPE,
param2 table.field2%TYPE,
param3 table.field3%TYPE,
param4 varchar2,
output OUT number) AS
BEGIN
DO STUFF
END;
I want to ask if it is possible to change this to send multiple sets of parameters at once, so I could use a FOR LOOP inside my procedure to minimize the number of calls. I want to achieve something like this:
CREATE OR REPLACE PROCEDURE MYPROCEDURE(
param myArray
output OUT number) AS
BEGIN
FOR i IN 1..myArray.COUNT LOOP
UPDATE FIELD FROM TABLE WHERE ID = myArray(i).field1;
END LOOP;
END;
Or if there's anything else that would work the same it would be great.
Many thanks.
Yes you can pass a list of objects as parameter in oracle procedure. First you must create the datatype of this list of objects, but you can't do this inside a procedure you have to define it as an oracle object. For example:
CREATE OR REPLACE TYPE TEST."MY_TYPE" AS OBJECT
(PARAM1 VARCHAR (20), PARAM2 NUMBER);
Unfortunately you can define dynamic datatypes inside objects (table.field1%TYPE), but I think you know what datatype this field have.
Second, create a package that have the list of parameter and procedure definition like this:
CREATE OR REPLACE PACKAGE ARRAY_EXAMPLE2
AS
TYPE COL IS TABLE OF MY_TYPE;
PROCEDURE PROCESS_ARRAY (ArrayIn IN COL);
END;
And finally the package implementation
CREATE OR REPLACE PACKAGE BODY ARRAY_EXAMPLE2
AS
PROCEDURE PROCESS_ARRAY (ArrayIn IN COL)
IS
BEGIN
FOR i IN 1 .. ArrayIn.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE ('Hello ' || ArrayIn (i).PARAM1);
END LOOP;
END;
END;
You can try it using this lines of code:
BEGIN
ARRAY_EXAMPLE2.
PROCESS_ARRAY (
array_example2.
COL (MY_TYPE ('Peter', 12),
MY_TYPE ('Jorge', 4),
MY_TYPE ('Bryan', 5)));
END;

cannot perform DML operation inside a query

I cannot convince why I can't add DML operation inside Oracle Function especially inside cursor loop. I feel Oracle don't support DML operation inside cursor loop.
How can I do If I need to insert into table inside cursor loop? Create new store procedure inside it or something else?
Error Message : cannot perform DML operation inside a query
Here is my function,
CREATE OR REPLACE FUNCTION TEST_FUNC(U_ID IN VARCHAR2)
RETURN VARCHAR2
IS
V_MESSAGE VARCHAR2(30);
CURSOR C_PERSON (V_ID VARCHAR2) IS
SELECT NAME_UPPER
FROM TBL_PERSON
WHERE NAME_UPPER = V_ID;
BEGIN
FOR C_PERSON_CURSOR IN C_PERSON(U_ID)
LOOP
INSERT INTO TMP_PERSON(NAME) VALUES (C_PERSON_CURSOR.NAME_UPPER);
END LOOP;
RETURN V_MESSAGE;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END;
You can use DML inside a PL/SQL function - no problem. However, the function can only be called from PL/SQL, not from SQL - i.e. it can be called like this:
declare
l_message varchar2(30);
begin
l_message := test_func('123');
end;
... but not like this:
select test_func(empno) from emp;
That leads to the error message you posted.
Many people (including me) don't like functions that have "side effects" like this, but that is a matter of best practice and standards, not a technical issue.
You can perform DML operations inside an Oracle PL/SQL function and, although this is generally not a good practice, call it from SQL. The function has to be marked with a pragma AUTONOMOUS_TRANSACTION and the transaction has to be committed or rolled back before exiting the function (see AUTONOMOUS_TRANSACTION Pragma).
You should be aware that this kind of function called from SQL can dramatically degrade your queries performances. I recommend you use it only for audit purposes.
Here is an example script starting from your function:
CREATE TABLE TBL_PERSON (NAME_UPPER VARCHAR2(30));
CREATE TABLE TMP_PERSON (NAME VARCHAR2(30));
INSERT INTO TBL_PERSON (NAME_UPPER) VALUES ('KING');
CREATE OR REPLACE FUNCTION TEST_FUNC(U_ID IN VARCHAR2)
RETURN VARCHAR2
IS
PRAGMA AUTONOMOUS_TRANSACTION; -- Needed to be called from SQL
V_MESSAGE VARCHAR2(2000);
CURSOR C_PERSON (V_ID VARCHAR2) IS
SELECT NAME_UPPER
FROM TBL_PERSON
WHERE NAME_UPPER = V_ID;
BEGIN
FOR C_PERSON_CURSOR IN C_PERSON(U_ID)
LOOP
INSERT INTO TMP_PERSON(NAME) VALUES (C_PERSON_CURSOR.NAME_UPPER);
V_MESSAGE := SQL%ROWCOUNT
|| ' Person record successfully inserted into TMP_PERSON table';
END LOOP;
COMMIT; -- The current autonomous transaction need to be commited
-- before exiting the function.
RETURN V_MESSAGE;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END;
/
PROMPT Call the TEST_FUNC function and insert a new record into TMP_PERSON table
SELECT TEST_FUNC('KING') FROM DUAL;
PROMPT Content of the TMP_PERSON table
COL NAME FOR A30
SELECT * FROM TMP_PERSON;
When running the previous script we get the following output:
Table created.
Table created.
1 row created.
Function created.
Calling the TEST_FUNC function and insert a new record into TMP_PERSON table
TEST_FUNC('KING')
------------------------------------------------------------
1 Person record successfully inserted into TMP_PERSON table
Content of the TMP_PERSON table
NAME
------------------------------
KING