Unexpected OUT value from Postgres Stored Procedure - sql

Using Postgres10.
I'm having an issue where I am calling a stored procedure and expecting a specific value for my OUT param and I am not getting it. I am calling the Items stored procedure with the call code below.
PROBLEM
I expect the first time I call the Item stored procedure to get an insert with a rtn value of 1 but I get a 4... This means the IF EXISTS is finding a row in the table with the same name but my table is empty.
I am expecting there is something weird going on where the IF EXISTS statement is being re-evaluated after the INSERT statement and entering the block where rtn gets set to 4. Is this something to do with plpgsql? It's acting as if the order of the stored procedure is not going top to bottom always when I put in Raise commands to test values at certain points.
SCHEMA/TABLE
CREATE TABLE aips.Item (
ItemPk SERIAL PRIMARY KEY,
Name VARCHAR(100) NOT NULL,
CONSTRAINT UNI_Item_Name UNIQUE(Name)
);
STORED PROCEDURE
CREATE OR REPLACE FUNCTION aips.Item(
INOUT p_ItemPk INT,
INOUT p_Name VARCHAR(100),
OUT rtn INT
) AS
$$
DECLARE rowcnt INT;
BEGIN
-- Insert or Find Path
IF p_ItemPk IS NULL THEN
-- Check for Find
IF EXISTS (SELECT * FROM aips.Item where Name = p_Name) THEN
SELECT ItemPk, Name
INTO p_ItemPk, p_Name
FROM aips.Item
WHERE Name = p_Name;
rtn := 4;
RETURN;
END IF;
-- Perform insert
INSERT INTO aips.Item (Name)
VALUES (p_Name)
RETURNING ItemPk INTO p_ItemPk;
GET DIAGNOSTICS rowcnt = ROW_COUNT;
IF rowcnt = 1 THEN
rtn := 1;
ELSE
rtn := 0;
RAISE EXCEPTION 'Expecting to insert a single row and rows returned --> %', rowcnt;
END IF;
ELSE -- Update or No Operation Path
-- Check for no changes
IF EXISTS (SELECT ItemPk
FROM aips.Item
WHERE ItemPk = p_ItemPk
AND Name = p_Name) THEN
rtn := 5;
RETURN;
END IF;
-- Perform Update
UPDATE aips.Item
SET Name = p_Name
WHERE ItemPk = p_ItemPk;
GET DIAGNOSTICS rowcnt = ROW_COUNT;
IF rowcnt = 1 THEN
rtn := 2;
ELSE
rtn := 0;
RAISE EXCEPTION 'Expecting to update a single row and rows returned --> %', rowcnt;
END IF;
END IF;
RETURN;
END;
$$ LANGUAGE plpgsql;
CALL
select (aips.Item(NULL, 'Test 1')).*;

The problem is the way you call the function:
select (aips.Item(NULL, 'Test 1')).*; -- WRONG!
because it is executed three times, once for each output column. The function should be called in the FROM clause:
select * from aips.Item(NULL, 'Test 1');

Related

Populate an Associative Array inside a Nested Table

In relation to my Previous Question, Include RowId value in Nested Table,
I have the below sample table:
create table data_test
(
data_id number,
data_value varchar2(100),
batch_name varchar2(100)
);
I have used this table as a parameter which includes the rowid:
create or replace package dat_pkg is
type typ_dat_rec is record (
data_rec data_test%rowtype,
data_rowid rowid);
type typ_dat_tst is table of typ_dat_rec index by pls_integer;
procedure transform_dat (p_batch_name data_test.batch_name%type);
procedure proc_test (p_dat typ_dat_tst);
end dat_pkg;
/
Using the procedure transform_dat, I want to populate the variable l_dat_rec with the filtered records from the data_test table, transform the data, and finally update the records using the proc_test procedure:
create or replace package body dat_pkg is
procedure transform_dat (p_batch_name data_test.batch_name%type)
is
cursor cur_dat is
select rowid, a.*
from data_test a
where batch_name = p_batch_name;
l_dat_rec typ_dat_tst;
begin
open cur_dat;
fetch cur_dat
BULK COLLECT
into l_dat_rec;
close cur_dat;
-- Do the Transformation here. Example --
for i in l_dat_rec.count loop
if l_dat_rec(i).data_value = 'hello' then
l_dat_rec(i).data_id := l_dat_rec(i).data_id + l_dat_rec(i).data_id;
else
l_dat_rec(i).data_id := l_dat_rec(i).data_id * l_dat_rec(i).data_id;
end if;
end loop;
-- update the table
proc_test (p_dat => l_dat_rec);
end transform_dat;
procedure proc_test (p_dat typ_dat_tst)
is
begin
for i in 1..p_dat.count loop
update data_test
set data_value = p_dat(i).data_value
where data_id = p_dat(i).data_id
and rowid = p_dat(i).data_rowid;
end loop;
end proc_test;
end dat_pkg;
/
however i am getting the error PLS-00597: expression 'L_DAT_REC' in the INTO list is of wrong type. The same error is being raised when i use BULK COLLECT.
What should I use to populate l_dat_rec?
In the answer to your previous question, I mentioned that populating the collection would be harder with the %rowtype field. As far as I'm aware, unless you declare an SQL-level object type instead of a record type you can't use bulk collect for this (though it's worth checking if that has changed in 12c perhaps).
I believe you are stuck with using a simpler cursor loop that builds the two fields in your type (i.e. the %rowtype sub-field and the rowid field) separately, and then builds up the collection a line at a time:
create or replace package body dat_pkg is
procedure transform_dat (p_batch_name data_test.batch_name%type)
is
cursor cur_dat is
select rowid, a.*
from data_test a
where batch_name = p_batch_name;
l_dat_tst typ_dat_tst;
l_rec data_test%rowtype;
begin
for rec_dat in cur_dat loop
l_rec.data_id := rec_dat.data_id;
l_rec.data_value := rec_dat.data_value;
l_rec.batch_name := rec_dat.batch_name;
-- or use a counter you increment for this...
l_dat_tst(l_dat_tst.count + 1).data_rec := l_rec;
l_dat_tst(l_dat_tst.count).data_rowid := rec_dat.rowid;
end loop;
-- Do the Transformation here. Example --
for i in 1..l_dat_tst.count loop
if l_dat_tst(i).data_rec.data_value = 'hello' then
l_dat_tst(i).data_rec.data_value := 'was hello';
else
l_dat_tst(i).data_rec.data_value := 'was not hello';
end if;
end loop;
-- update the table
proc_test (p_dat => l_dat_tst);
end transform_dat;
procedure proc_test (p_dat typ_dat_tst)
is
begin
for i in 1..p_dat.count loop
update data_test
set data_value = p_dat(i).data_rec.data_value
where data_id = p_dat(i).data_rec.data_id
and rowid = p_dat(i).data_rowid;
end loop;
end proc_test;
end dat_pkg;
/
As also discussed before, the references to the sub-field-record's fields have to be qualified properly, so I've inserted .data_rec in the references in both procedures. I've changed the dummy transformation to modify the value instead of the ID, as that means no updates were ever going to happen.
Demo with some dummy data:
insert into data_test values (1, 'hello', 'test');
insert into data_test values (2, 'hello', 'test');
insert into data_test values (3, 'hello', 'exclude');
insert into data_test values (4, 'goodbye', 'test');
exec dat_pkg.transform_dat('test');
select * from data_test;
DATA_ID DATA_VALUE BATCH_NAME
---------- -------------------- --------------------
1 was hello test
2 was hello test
3 hello exclude
4 was not hello test

sql insert procedure: insert values into table and control if value is existing in another table

I have little problem. I am trying to insert value into table. This is working. But I would like to control if value id_trainer is existing in another table. I want this -> execute insertClub(1, 5, 'someName'); -> and if id_trainer 5 not exists in table Trainer, procedure gives me message about this. (I tried to translate it into eng. lng., so you can find some gramm. mistakes)
create or replace procedure insertClub
(id_club in number, id_trainer in number, clubName in varchar2)
is
begin
declare counter number;
select count(*) into counter from trianer tr where tr.id_trainer = id_trainer;
if counter = 0 then
DBMS_OUTPUT.PUT_LINE('Trainer with this ID not exists');
end if;
insert into club values(id_club, id_trainer, clubName);
exception
when dup_val_on_index then
DBMS_OUTPUT.PUT_LINE('Dup ID');
end;
/
There is some error in the procedure. Please run below code to create procedure:
create or replace procedure insertClub
(id_club in number, id_trainer in number, clubName in varchar2)
is
counter number;
begin
select count(*) into counter from trianer tr where tr.id_trainer = id_trainer;
if counter = 0 then
DBMS_OUTPUT.PUT_LINE('Trainer with this ID not exists');
end if;
insert into club values(id_club, id_trainer, clubName);
exception
when dup_val_on_index then
DBMS_OUTPUT.PUT_LINE('Dup ID');
end;
/

inserting into with an incrementing value

The best way I can think to explain this is: I'm trying to give each robot a unique id. Then, when I run the procedure, I only have to give the robot a name and id. The status are generated for me.
However, I am reaching a total roadblock because I can't find out the proper way to do this. I've tried counting the rows and putting it into tempid. That doesn't work. A new row is made every time I run it. So, I should always get a new id. I might have done it wrong though.
I get this error:
Error(23,44): PL/SQL: ORA-00984: column not allowed here
Here is the procedure:
CREATE OR replace PROCEDURE Checkbot
(nameinput IN VARCHAR2)
IS
partfound NUMBER(4);
foundall BOOLEAN;
tempid NUMBER;
BEGIN
--Where I am having issues:
SELECT Count(*)
INTO tempid
FROM robotinventory;
INSERT INTO robotinventory VALUES (tempid, nameinput, NULL);
foundall := TRUE;
FOR i IN 1..8 LOOP
partfound := 0;
SELECT Max(prtserial)
INTO partfound
FROM partinventory
WHERE parttypeid = i;
IF partfound > 0 THEN
DELETE FROM partinventory
WHERE prtserial = partfound;
INSERT INTO robotprt VALUES (tempid, idinput, i);
ELSE
foundall := FALSE;
END IF;
END LOOP;
IF foundall THEN
UPDATE robotinventory
SET status = 'ready for assembly'
WHERE robotid = tempid;
ELSE
UPDATE robotinventory
SET status = 'waiting on parts'
WHERE robotid = tempid;
END IF;
END;/
robot inventory table:
CREATE TABLE RobotInventory
(RobotID Number(4) PRIMARY KEY,
RobotName VARCHAR2(24),
Status VARCHAR2(64));
Previous version of my code, took out what I did wrong in mine, my goal is to replace idInput with an auto incrementing number:
create or replace procedure checkbot
(idInput in number, nameInput in varchar2)
is
partFound number(4);
foundAll boolean;
begin
insert into robotInventory values (idInput, nameInput, null);
foundAll := true;
for i in 1..8 loop
partFound := 0;
select max(prtSerial)
into partFound
from partInventory
where ParttypeID = i;
if partFound > 0 then
delete from partInventory
where prtSerial = partFound;
insert into robotPrt values (partFound, idInput, i);
else
foundAll := false;
end if;
end loop;
if foundAll then
update robotInventory
set status = 'ready for assembly'
where robotID = idInput;
else
update robotInventory
set status = 'waiting on parts'
where robotID = idInput;
end if;
END;
/
Create a sequence (called seq below) and use it in your INSERT SQL to populate robot_id.
INSERT INTO robotinventory VALUES (seq.nextval, nameinput, NULL);
Look at Oracle docs on how to create a sequence in more detail. Here is an example that you can change to fit your needs.
create sequence seq
start with 1
increment by 1
maxvalue 9999;
HTH

How to use `SELECT COUNT(*)` as a conditional statement in PL/SQL?

I have a stored procedure that updates records if they exist, or adds a new record if they don't exist.
SQL:
CREATE OR REPLACE PROCEDURE ADDRECORD(ihost VARCHAR, iip VARCHAR)
AS
rc VARCHAR(4000);
ROWCOUNT NUMBER;
BEGIN
rc := 'select count(0) from myTable where physical_host = ihost and primary_ip = iip';
ROWCOUNT := to_number(rc, '99');
IF ROWCOUNT = 1 THEN
UPSERTRECORD(ihost, iip);
ELSE
INSERT INTO myTable(PHYSICAL_HOST, PRIMARY_IP)
VALUES (ihost, iip);
INSERT INTO IP (IP, IP_IND) VALUES (iip, 'V');
END IF;
END ADDRECORD;
The UPSERTRECORD is another stored procedure that is being called. It works fine. In fact, the error is occurring on the line that contains the to_number. The error is :
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
Is there another way to do this? Sorry, I'm not super experienced with SQL, but I need to get this figured out.
Do
DECLARE rowcount As Number(38)
select count(*) INTO rowcount from myTable
where physical_host = ihost and primary_ip = iip

Error in plsql . here flag has value 1 or no data. when no data i have to insert somethings but error coming.suggestions please

CREATE OR REPLACE PACKAGE BODY BAS_NUMB_UPD AS
PROCEDURE BAS_NUM_UPDN AS
CURSOR cur IS
SELECT DISTINCT o.obj_id,LTRIM(substr(convert_171_to_711(cp.T_C_CP),1,7),'0') bas_n
FROM t_obj o, mat_tea_rel mpr, coorg_tea cp
WHERE o.obj_type = 'Resin'
AND o.obj_id = mpr.obj_id
AND mpr.p_k_id = cp.p_k_id;
l_b_num_attr_id number(10) := get_attribute_id('Ba Nu');
flag1 VARCHAR2(10);
BEGIN
FOR x IN cur LOOP
dbms_output.put_line(x.obj_id||'contains b n'||x.bas_n);
SELECT flag INTO flag1
FROM t_xc_s_values
WHERE attr_id = l_b_num_attr_id
AND Obj_id = x.obj_id
AND VALUE = x.bas_n;
EXCEPTION
WHEN NO_DATA_FOUND THEN
flag1 :='Nothing';
WHEN OTHERS THEN
raise_application_error(-20011,'Unknown Exception in PROCEDURE');
END;
IF flag1 = 1 THEN
dbms_output.put_line('flag equal to one');
ELSE
INSERT INTO t_xc_s_values (obj_id, at_id, VALUE,)
VALUES (x.obj_id, l_b_num_attr_id, x.bas_n);
END IF;
END LOOP;
END;
END BAS_NUM_UPDN;
END BAS_NUMB_UPD;
These are the errors
Error(28,1): PLS-00103: Encountered the symbol "EXCEPTION" when
expecting one of the following: begin case declare end exit for
goto if loop mod null pragma raise return select update while with
<< close current delete fetch lock insert open rollback
savepoint set sql execute commit forall merge pipe
Error(35,1): PLS-00103: Encountered the symbol "IF" when expecting one
of the following: end not pragma final instantiable order
overriding static member constructor map
Error(47,3): PLS-00103: Encountered the symbol "END" when expecting
one of the following: ;
Your code is not properly nested:
If you have an EXCEPTION part, you need to have the triple BEGIN / EXCEPTION / END on the same level. So you probably need to insert a BEGIN after LOOP.
Each END must match a BEGIN. Besides the missing BEGIN mention before, you have too many END statements at the end. Remove the one on the third last line.
These are just syntactical errors. I didn't check whether the procedure does what you intend.
The errors you get are quite straightforward. You have an EXCEPTION ... END but no matching BEGIN. You want to trap the no_data_found for your flag select, so start that block with a BEGIN
(Why would you use a WHEN OTHERS? Why would you trap it and then say it is 'Unknown'? The error is never unknown.)
BEGIN
FOR x IN cur LOOP
dbms_output.put_line(x.obj_id||'contains b n'||x.bas_n);
BEGIN -- added here
SELECT flag INTO flag1
FROM t_xc_s_values
WHERE attr_id = l_b_num_attr_id
AND Obj_id = x.obj_id
AND VALUE = x.bas_n;
EXCEPTION
WHEN NO_DATA_FOUND THEN
flag1 :='Nothing';
WHEN OTHERS THEN
raise_application_error(-20011,'Unknown Exception in PROCEDURE');
END;
Next error:
your insert statement has one comma too many:
INSERT INTO t_xc_s_values (obj_id, at_id, VALUE,)
Next error:
one END too many
END LOOP;
END; -- this will END your procedure block
END BAS_NUM_UPDN; -- this will complain it has to match the procedure begin
Your PLSQL code is also unnecessary. You could write your logic as a SQL statement, improving your performance.
PROCEDURE BAS_NUM_UPDN AS
IS
l_b_num_attr_id number(10) := get_attribute_id('Ba Nu');
BEGIN
INSERT INTO t_xc_s_values (obj_id, at_id, VALUE)
SELECT DISTINCT o.obj_id,
l_b_num_attr_id,
LTRIM(substr(convert_171_to_711(cp.T_C_CP),1,7),'0') bas_n
FROM t_obj o, mat_tea_rel mpr, coorg_tea cp
WHERE o.obj_type = 'Resin'
AND o.obj_id = mpr.obj_id
AND mpr.p_k_id = cp.p_k_id;
AND NOT EXISTS (SELECT flag
FROM t_xc_s_values
WHERE attr_id = l_b_num_attr_id
AND Obj_id = o.obj_id
AND VALUE = LTRIM(substr(convert_171_to_711(cp.T_C_CP),1,7),'0'));
END;