Oracle Stored Procedure with IN parameter Issue - oracle

I have created a stored procedure in Oracle - see below
create or replace PROCEDURE REMOVE_CUSTOMER
(
cus_id IN NUMBER
)
AS
BEGIN
DELETE FROM CUSTOMER WHERE CUSTOMER.CUS_ID = cus_id;
END;
I executed it like below.
DECLARE
CUS_ID NUMBER;
BEGIN
CUS_ID := 192981;
REMOVE_CUSTOMER(CUS_ID => CUS_ID);
END;
It supposes to delete customer 192981 only. However, all customers in the table got deleted. Can anybody tell me what it was wrong with the pl/sql?

The statement within the procedure:
DELETE FROM CUSTOMER WHERE CUSTOMER.CUS_ID = cus_id;
The query parser must identify what cus_id is on the right-hand side. If the table has a column by that name, that's the first choice. This is why everything is deleted; the RHS is interpreted to mean customer.cus_id.
If no such column existed in the table, the second guess would be a variable defined in the procedure. But that is only the second choice, not the first.
Best practice is to use DIFFERENT names for procedure variables, perhaps prefix the column name with p_ (for parameter) or i_ (for input): p_cus_id.
You CAN use the same name for your procedure variable, but then you must fully qualify it in the SQL statement:
where customer.cus_id = remove_customer.cus_id
Actually you don't need to qualify in the left-hand side; this will work:
where cus_id = remove_customer.cus_id
By contrast, what you do in the anonymous block (when you call the procedure) doesn't cause problems. It is still a bad practice to use the column name as the name of the variable declared in the anonymous block, but when you call the stored procedure from the anonymous block, there can be no confusion as to which CUS_ID is the input to the stored procedure; it can't be a column name from a table, and it can't be the variable from the SP (which is "in scope" only in the SP, it is not visible to the caller - the anonymous block).

Related

What is the purpose of this package?

I am new to Oracle PL/SQL. I have found this package and it is called from a trigger. I just cannot figure exactly what this simple looking package code is doing.
It is called from a trigger as below:
IF INSERTING THEN
i := STATE_PKG_OVERRIDE_CN.AffectedRows.COUNT+1;
STATE_PKG_OVERRIDE_CN.AffectedRows(i).IDU := :new.IDU;
STATE_PKG_OVERRIDE_CN.AffectedRows(i).cn := :new.cn;
This is the package. Can somebody please explain the basics of what is it doing? does it return a value? change a value? what is AffectedRows RIDARRAY or EMPTY ?
create or replace PACKAGE STATE_PKG_OVERRIDE_CN
AS
TYPE rowid_cn IS RECORD
(
idu dirxml.USR.IDU%TYPE,
cn dirxml.USR.CN%TYPE
);
TYPE RIDARRAY IS TABLE OF rowid_cn INDEX BY BINARY_INTEGER;
AffectedRows RIDARRAY;
EMPTY RIDARRAY;
END;
I have googled EMPTY but found nothing, i believe it is creating a table of type record. The trigger is passing in a value of cn or IDU am i am familiar with these two values. But what is the package doing? or returning ? im confused.
Cheers
This is a bespoke package belonging to your organisation. (That's why Google wasn't helpful for you.) We cannot tell you for sure what it does or how it's used. But we can guess.
The package has no procedures or functions, it just defines array variables of a bespoke type, which can be used by other program units such as triggers. The trigger you posted assigns values to the array AffectedRows. Presumably this trigger fires FOR EACH ROW. Likely there is another trigger on the same table firing AFTER STATEMENT which reads that array and does some processing, then assigns AffectedRows := EMPTY to reset the array.
The purpose of this infrastructure is to pass state across trigger actions. A common reason for doing this is work around a mutating table exception. They are risky because state cannot be guaranteed; for instance if a insert fails before the AFTER STATEMENT trigger fires the AffectedRows array is not re-initialised, so subsequent processing will be incorrect (or will fail).
Since 11g Oracle provides compound triggers which remove the need for this sort of package. Find out more.
To investigate further, first you want to check USER_TRIGGERS to find other triggers on the table which owns the trigger you mentioned. If that doesn't help or you want to see whether other tables also use this package run this query:
select *
from user_dependencies
where referenced_type = 'PACKAGE'
and referenced_name = 'STATE_PKG_OVERRIDE_CN'
It begins with the package which
declares a type ROWID_CN as a record that contains two values: IDU and CN
syntax requires to create another type (RIDARRAY) which is based on previously declared ROWID_CN
affectedrows and empty are arrays whose type is RIDARRAY. Basically, you can imagine them as a table that has two columns, IDU and CN
Now, a trigger: piece of code you posted says that those 3 lines are executed when someone inserts a row into a table the trigger is based on. For example,
create or replace trigger trg_biu_emp
before insert or update on emp
for each row
declare
i number;
begin
if inserting then ...
Apparently, there's some code that is executed when updating, or even deleting rows.
Anyway:
i := ... line counts number of elements in affectedrows array (which is declared in the package) and adds 1 to that number. For example, if there were 3 elements, i would be 4.
...affectedrows(i).idu := :new.idu enters a new row into the array. Its ordinal number is i (4 in our example). As you're inserting a row into the table, trigger knows IDU column's :new value and puts it into the array. For example, if you used
insert into emp (idu, cn) values (100, 'A')
then affectedrows(4).idu = 100, while affectedrows(4).cn = 'A'
It is, probably, something similar with EMPTY array. Google can't return anything useful, it is just a custom-made array.

I'm trying to create a procedure in Pl/Sql and I'm getting this error

https://imgur.com/a/ll59wjx - Picture
I'm trying to calculate the current product stock.
I have product_reception table in which I can calculate my stock.
And sales (vanzari) table.
Posting code as an image is usually a bad idea.
Anyway: quite a few objections:
don't enclose procedure (or table, column, ...) name into double quotes
name parameters so that their name is different from column names, for example: not cod_pds in number but par_cod_pds in number
declare variable in the declaration section, which is between is and begin. Don't use var (key)word. It is a good idea to name them so that the name reflects the fact that these are local variables, such as l_stoc_pds number
don't use exec within a PL/SQL procedure, especially not to run a select statement. exec is used in SQL*Plus to run a PL/SQL procedure, such as exec calculstoclazi
don't select into a variable preceded by a colon, but simply name the variable
those two select statements are exactly the same; I have no idea what you meant to do by doing that. Besides, you preceded cod_pds with stoc_pds which is a variable name (so that's totally wrong); if a column name is to be preceded by something, that's table name (or its alias)
Therefore, code that might look like something valid is this; obviously, the result will be 0 (zero). As I told you, those two selects are exactly the same.
create or replace procedure calculstoclazi
(par_cod_pds in number,
par_rezultat out number
)
is
l_stocs_pds number;
l_total_vanzare number;
begin
select sum(cantitate)
into l_stoc_pds
from receptie_marfa
where cod_pds = par_cod_pds;
select sum(cantitate)
into l_total_vanzare
from receptie_marfa
where cod_pds = par_cod_pds;
par_rezultat := l_stoc_pds - l_total_vanzare;
end;

PL/SQL : Can we have same name for variable and column

In the tutorial i learned that we cannot have same name of variable and column
Here is the link
so i tried this
set SERVEROUTPUT ON;
declare
n1 number;
name varchar(10);
begin
null;
DBMS_OUTPUT.PUT_LINE('sparsh' || TO_CHAR(45));
select name into name from Employee;
DBMS_OUTPUT.PUT_LINE(name);
END;
/
Here i have name column in the table and with the same name i declare the variable.
As per the tutorials it should give some error. But it is working fine
Could you please help me in order to clarify.
So can we have variable with same name of column?
The tutorial is slightly wrong. It is possible to have variables with the same name in SQL and PL/SQL but it is almost certainly a bad idea to do so.
Using variables with the same name will lead to confusing scope issues. The PL/SQL block will correctly use the PL/SQL name for the INTO clause, but it would also use it other places you might not expect. For example, if you tried to use it in the WHERE clause the SQL name would be used instead.
drop table employee;
create table employee(id number, name varchar2(100));
insert into employee values (1, 'Alice');
declare
name varchar(10) := 'Bob';
begin
select name into name from Employee where name = name;
dbms_output.put_line(name);
END;
/
OUTPUT:
Alice
This is the real reason why it's a good practice to name local variables V_ for "variable", and P_ for "parameter". It's not because of some stupid Systems Hungarian notation rules. (So don't also start naming your variables _IN_, _NUM_, etc!) It's because in practice you want to use the same names in PL/SQL and SQL but you don't want to get them confused.
declare
name varchar(10) := 'Bob';
begin
select name into name from Employee where **id= id**;
dbms_output.put_line(name);
END;
/
I am using the same example given by Jon.
See the condition where id = id.
If you use a variable with a name same as of the column name.It would not throw any error but it would certainly astonish you with a huge and unexpectedly large dataset.
In Conditional predicates column names get prioritized over variable names.
Hence in this case id = id both would be considered as table columns and condition will be evaluated into TRUE for all rows.
There is no such rule while choosing the name of the variables.but it's better to keep the standards.
Should this be something SQL tools like Toad should be picking up on? e.g. if you try to run a query like this it would fail with a message to say something like 'Declared variable name should not be the same as Column name in query'? Or should it be a SQL error thrown? I can't think of any situation where you would want to do this and want the outcome so surely it shouldn't be possible to do it?

how to insert in oracle 10g database and returns the ID generated using stored procedure

I am very new to oracle's sql developer (since we've studied mysql) as well as in programming. I've searched in this website the answer to my question but I really can't understand the solutions provided.
What I want is to return the ID generated after inserting an object from java into the database. I'm using mybatis and oracle 10g database. I've already created the table and its columns.
Here's my code for the mapper
<insert id="addUser" parameterType="User" statementType="CALLABLE">
{ CALL addUserSP(
#{user.surname, javaType=String, jdbcType=VARCHAR, mode=IN},
#{user.firstName, javaType=String, jdbcType=VARCHAR, mode=IN},
#{userId, javaType=Integer, jdbcType=NUMBER, mode=OUT}
)}
</insert>
Here's my stored procedure (and I've already create a package named 'CREATEUSER')
PROCEDURE ADDUSERSP
( surname IN VARCHAR2,
firstName IN VARCHAR2,
userId OUT NUMBER
) AS
BEGIN
INSERT INTO users("surname", "first_name")
VALUES (surname, firstName);
RETURNING user_id INTO userId;
END ADDUSERSP;
According to what I've found here, it seems that I need to create a trigger(?) and sequence(?) to make the user_id auto increment whenever I add new data into the table. However, I have no idea how to do it.
Here are my questions:
Is my stored procedure right? Are the codes incomplete? I mean, I have not declared the package in the mapper and I've seen that it is needed (?), something like this { CALL [CreateUser].[addUserSP]( blah blah.... Should I write a sequence and trigger or there is an easy way to make the primary key user_id to be auto incremented? Kindly also check the syntax. I have a lot of problems in syntax.
Thank you so much!
To emulate MySQL AUTO_INCREMENT in Oracle, that pattern (as you found) does use a SEQUENCE object and a BEFORE INSERT trigger.
As a demonstration, something like this for the sequence object:
CREATE SEQUENCE myseq START WITH 1 INCREMENT BY 1 ;
And something like this for the before insert trigger:
CREATE TRIGGER users_bi
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
IF :NEW.id IS NULL THEN
SELECT myseq.NEXTVAL INTO :NEW.id FROM DUAL;
END IF;
END
As far as the procedure, I'm not a big fan of extra PL/SQL blocks that wrap a SQL INSERT statement.
It looks like you have an extra semicolon, before RETURNING. That clause is part of the INSERT statement, not a separate statement.
One big gotcha to be aware of is that SQL statements within a PL/SQL block can reference both columns and PL/SQL variables. When variables have the same names as columns, you will likely encounter behavior you didn't expect.
Typically PL/SQL author use a naming convention for variables that reduces the likelihood of name collisions. We frequently see variables with names like v_surname. (Personally, I use a slightly different convention, but the variable names "look like" variable names, not column references. And I don't name columns following the pattern I use for variables.)
The double quotes around the identifiers are acceptable, but this does make the identifiers case sensitive. When identifiers aren't enclosed in double quotes, Oracle treats them as if they were UPPER CASE. Just make sure that your table was defined with lower case column names.

Calling a Procedure inside a Function PL/SQL

I am trying to figure out how to call the following procedure from a function using Oracle 11g 11.2.0.2.0. Does anyone have some ideas? :
-- CREATES or REPLACES insert_into_table_a Procedure.
CREATE OR REPLACE PROCEDURE insert_into_table_a
-- Declares variable to be inserted into tables.
(test_insert_a VARCHAR2)
IS
-- Begins Procedure.
BEGIN
-- Creates a savepoint.
SAVEPOINT all_or_none;
-- Inserts test_insert_a into the CONTACT_ID column of the CONTACT table.
INSERT INTO CONTACT (CONTACT_ID)
VALUES (test_insert_a);
-- Inserts test_insert_a into the ADDRESS_ID column of the ADDRESS table.
INSERT INTO ADDRESS (ADDRESS_ID)
VALUES (test_insert_a);
-- Inserts test_insert_a int the TELEPHONE_ID column of the TELEPHONE table.
INSERT INTO TELEPHONE (TELEPHONE_ID)
VALUES (test_insert_a);
--Commits inserts.
COMMIT;
-- Creates exception, incase any errors occur, all changes are rolled back.
EXCEPTION
WHEN OTHERS THEN
ROLLBACK TO all_or_none;
-- Ends procedure.
END;
/
-- Shows any errors created.
SHOW ERRORS
You can call the procedure from a function just as you'd call any other PL/SQL block
CREATE OR REPLACE FUNCTION my_function
RETURN integer
IS
l_parameter VARCHAR2(100) := 'foo';
BEGIN
insert_into_table_a( l_parameter );
RETURN 1
END;
That being said, it doesn't make sense to call this procedure from a function. Procedures should manipulate data. Functions should return values. If you call a procedure that manipulates data from a function, you can no longer use that function in a SQL statement, which is one of the primary reasons to create a function in the first place. You also make managing security more complicated-- if you use functions and procedures properly, DBAs can give read-only users execute privileges on the functions without worrying that they'll be giving them the ability to manipulate data.
The procedure itself seems highly suspicious as well. A column named address_id in the address table should be the primary key and the name implies that it is a number. The same applies for the contact_id column in the contact table and the telephone_id column in the telephone table. The fact that you are inserting a string rather than a number and the fact that you are inserting the same value in the three tables implies that neither of these implications are actually true. That's going to be very confusing for whoever has to work with your system in the future.

Resources