PL/SQL Package and Package Body - sql

I've created a function inside my package body that counts the number of current customers in the customer database and I've created a function that creates a customer. But I am unsure to what i need to put inside the package itself. Here is the code that is inside the package body.
FUNCTION count_customer
RETURN NUMBER is
total NUMBER;
BEGIN
SELECT COUNT(*) into total FROM customer;
RETURN (total);
END;
FUNCTION CREATE_CUSTOMER(
Country IN VARCHAR2
,First_Name IN VARCHAR2
,Last_Name IN VARCHAR2
,Birth_Date IN VARCHAR2
,Customer_Type IN VARCHAR2
,Address IN VARCHAR2
) return VARCHAR2 IS
new_customer_id VARCHAR2(8);
BEGIN
SELECT custid_seq.NEXTVAL
INTO new_customer_id
FROM DUAL;
INSERT INTO customer (Customer_id, Country, First_Name, Last_name, Birth_date, Customer_Type, Address)
VALUES (new_customer_id, Country, First_Name, Last_name, Birth_date, Customer_Type, Address);
total_customers := total_customers + 1;
RETURN (new_customer_id);
end;
And then later in the package body I have this to calculate the new count.
BEGIN
total_customers := count_customer();
END;
The problem I am having is what to put in the actual package. I've got the create customer working inside the package it is just the customer count im having problems with.
Any ideas anyone?

You need to declare any procedures or functions that you want to be accessible form outside the package. They are private by default, meaning that they can only be called within the same package.
To expose both of those functions:
CREATE PACKAGE my_package AS
FUNCTION count_customer
RETURN NUMBER;
FUNCTION CREATE_CUSTOMER(
Country IN VARCHAR2
,First_Name IN VARCHAR2
,Last_Name IN VARCHAR2
,Birth_Date IN VARCHAR2
,Customer_Type IN VARCHAR2
,Address IN VARCHAR2
) return VARCHAR2;
END my_package;
CREATE PACKAGE BODY ...
There is much more in the documentation.
From what you have, though, I suspect you don't really want count_customers() to be exposed; you want a separate public function that returns the current value of total_customers. Although that assumes that all inserts and deletions always go through the package, otherwise the count will get out of step.

Put declarative part of any function or procedure that you want to accessed from "outside" like:
FUNCTION count_customer RETURN NUMBER;

Related

Function Oracle PL SQL bulk collect error PLS-00382: expression is of wrong type

I have this function:
CREATE OR REPLACE TYPE products_type AS OBJECT
(
products VARCHAR2 (50),
price VARCHAR2 (50)
);
CREATE OR REPLACE TYPE results_type AS TABLE OF products_type;
create or replace get_sum ( l_products in varchar2(50)
RETURN results_type
IS
l_result results_type;
begin
SELECT distinct products, count(price)
BULK COLLECT INTO l_result
FROM products;
RETURN l_result;
END;
/
DECLARE
l_result varchar2(50) := '0';
BEGIN
l_result := get_sum ('apple')
DBMS_OUTPUT.PUT_LINE('Price total ' || l_result);
END;
I have this table and I want to know what the sum of a product is.
I try to learn how a function works when I want to display more than one column. I found on the internet related to bulk collect and I tried to do so, but I encounter this error:
PLS-00382: expression is of wrong type
What am I doing wrong?
The problem is that in your function GET_SUM, you are returning l_result which is of type results_type. In your anonymous block, you are calling GET_SUM and attempting to assign it to a VARCHAR2(50) variable. The variable you are trying to assign the value to needs to match the return type of the function.

Pl SQL Oracle PLS-00103: Encountered the symbol "CREATE"

this code takes input for zipcode, city, and state and then inserts that into the table created Address. Prior to inserting data it will check if the zipcode is already in the table, if so calling procedure(error) to display an error code.
Im getting an error code pls-00103: encountered the symbol "CREATE" when trying to execute the code. Here is my code so far. Thanks for any help in advance.
drop table address;
create table address(zipcode NUMBER, city varchar2(30), state varchar2(20));
create or replace procedure error as
begin
dbms_output.put_line('Error Zip Code already found in table');
end error;
declare
zzip number;
ccity varchar2(30);
sstate varchar2(30);
create or replace procedure location(p_zipcode NUMBER,
p_city varchar2,
p_state varchar2) is
zip address.zipcode%type;
cit address.city%type;
st address.state%type;
begin
select count(*) from address into zip where zipcode = zip;
if any_rows_found then
error;
else
Insert into address values(zip, cit, st);
end if;
end location;
begin
select &zipcode into zzip from dual;
select &city into ccity from dual;
select &state into sstate from dual;
procedure location(zzip, ccity, sstate);
end;
/
I'm not sure what you're trying to do, but the following may be closer to what you had in mind:
drop table address;
create table address(zipcode NUMBER, city varchar2(30), state varchar2(20));
declare
zzip number;
ccity varchar2(30);
sstate varchar2(30);
procedure error is
begin
dbms_output.put_line('Error Zip Code already found in table');
end error;
procedure location(p_zipcode NUMBER, p_city varchar2, p_state varchar2) is
zip_count NUMBER;
begin
select count(*)
into zip_count
from address
where zipcode = p_zipcode;
if zip_count > 0 then
error;
else
Insert into address
(zipcode, city, state)
values
(p_zipcode, p_city, p_state);
end if;
end location;
begin
select &zipcode into zzip from dual;
select &city into ccity from dual;
select &state into sstate from dual;
location(zzip, ccity, sstate);
end;
/
Best of luck.
I dont know if I understand your problem correctly, but there are certain correction I'd like to address to answer your problem
First, if you are going to create a procedure/function, do it in a separate worksheet then compile it. Dont compile it together with other anonymous blocks because most of the time, if you dont end your other blocks with '/', errors will surely generate.
Second, your DECLARE statement is misplaced, if you are going to make an anonymous block, make sure DECLARE, BEGIN and END are in line, dont create a procedure/function inside an anonymous block.
Third, you are declaring variables in your procedures and using them but doesnt have an initial value, so it will just pass a null value to the DML statement in your procedure. just use the parameter directly.
Fourth, avoid creating a procedure that only contains dbms_output.put_line. Its silly.
Lastly, your anonymous block that should be calling your procedure, uses '&', please avoid using '&' inside pl/sql as '&' is a feature in SQL*Plus and doesnt have any meaning in PL/SQL, instead, you can use ':' as for binding variables. But you use '&' not in binding variables so you should remove that;
Try this:
drop table address;
/
create table address(zipcode NUMBER, city varchar2(30), state varchar2(20));
/
create or replace procedure location(p_zipcode NUMBER,
p_city varchar2,
p_state varchar2) is
zip address.zipcode%type;
begin
select count(*)
from address
into zip
where zipcode = p_zipcode
and city =p_city
and state = p_state;
if zip > 0 then
dbms_output.put_line('Error Zip Code already found in table');
else
Insert into address values(p_zipcode, p_city, p_state);
end if;
end location;
/
begin
location(:zzip, :ccity, :sstate);
end;

PLS-00201: identifier 'NO_EMPLOYEES' must be declared

Hi Please i need assistance as im stuggling with the following error:
PLS-00201: identifier 'NO_EMPLOYEES' must be declared
CREATE or replace PACKAGE police_employee_mgmt AS
no_employees NUMBER;
FUNCTION insert_employee
(emp_id NUMBER, emp_name VARCHAR2, emp_grade NUMBER, emp_password VARCHAR2, emp_username VARCHAR2)
RETURN NUMBER;
PROCEDURE delete_employee(emp_id NUMBER);
PROCEDURE change_employee_grade(emp_id NUMBER, emp_grade NUMBER);
END police_employee_mgmt;
then a function is created to give the variable a value:
CREATE OR REPLACE FUNCTION number_of_employees
RETURN NUMBER IS
total_employees NUMBER;
BEGIN
SELECT COUNT(*)
INTO total_employees
FROM pl_police_employee;
RETURN(total_employees);
END;
Then...
BEGIN
no_employees := number_of_employees();
END;
As the error states, you need to declare no_employees. I asume your last code block is outside the package where you did declare it. I think you can use anonymous declares as well:
DECLARE
num_employees NUMBER;
BEGIN
num_employees := number_of_employees();
...
END

Calling a persistent package variable

Ive created a package and a package body that run but I'm unsure of how i can test the value held in my persistent package variable.
Here is the package code, its fairly simple just a delete procedure and create function which are in the package body.
CREATE OR REPLACE PACKAGE customers AS
FUNCTION create_customer (Country VARCHAR2, First_Name VARCHAR2, Last_name VARCHAR2,
Birth_date VARCHAR2, Customer_Type VARCHAR2, Address VARCHAR2)
RETURN VARCHAR2;
PROCEDURE remove_customer (customer_id VARCHAR2);
FUNCTION count_customer RETURN NUMBER;
END customers;
And now here is the package body
create or replace package body customers AS
total_customers NUMBER;
FUNCTION CREATE_CUSTOMER(
Country IN VARCHAR2 ,First_Name IN VARCHAR2 ,Last_Name IN VARCHAR2 ,Birth_Date IN VARCHAR2 ,Customer_Type IN VARCHAR2 ,Address IN VARCHAR2
) return VARCHAR2 IS
new_customer_id VARCHAR2(8);
BEGIN
SELECT custid_seq.NEXTVAL
INTO new_customer_id
FROM DUAL;
INSERT INTO customer (Customer_id, Country, First_Name, Last_name, Birth_date, Customer_Type, Address)
VALUES (new_customer_id, Country, First_Name, Last_name, Birth_date, Customer_Type, Address);
total_customers := total_customers + 1;
RETURN (new_customer_id);
end;
PROCEDURE remove_customer (customer_id VARCHAR2) IS
BEGIN
DELETE FROM customer
WHERE customer.customer_id = remove_customer.customer_id;
total_customers := total_customers - 1;
END;
BEGIN
select count(*) into total_customers from customer;
END;
The total_customers is my persistent package variable and only local to this package I just want to know how i can test to see the value currently held in the variable.
Thanks for the help
In your package specification add:
function get_total_customers return number;
In your package body add:
function get_total_customers return number is
begin
return total_customers;
end get_total_customers;
Then from SQL you can select customers.get_total_customers from dual.
The alternative is to have the package variable defined in the specification rather than the body, but then it could be changed externally as well as read.
Either way, the value will only be meaningful within a session; if you set it in one session then that won't be visible in another session. Both can calculate it, but then if you call create_customer() in one session, the other will still report the old total.
Put your variable to package spec, test it. Then put it back into the body or leave it in the spec. Keep it simple...

Stored Function in Oracle not Inserting Values into the desired table

Here is my Code given below. I am trying to add 5 parameters which the function takes into the table Employee. But I am not successful in doing it and have tried a lot of things.
Error
ORA-01858: a non-numeric character was found where a numeric was
expected ORA-06512: at "xxxxxxx.A1SF_ADDEMP", line 14
01858. 00000 - "a non-numeric character was found where a numeric was expected"
*Cause: The input data to be converted using a date format model was
incorrect. The input data did not contain a number where a number was
required by the format model.
*Action: Fix the input data or the date format model to make sure the
elements match in number and type. Then retry the operation.
Plus how do I test a Stored function that has a Insert/Update or Delete Statement in it?
Execution Statement
Select A1SF_ADDEMP('Adesh', '33', 'M', 8000, '26/03/1990')
From dual;
Code
CREATE OR REPLACE Function A1SF_ADDEMP
(pEmpName In Varchar2,
pTaxFileNo In Varchar2,
pGender In Varchar2,
pSalary In Number,
pBirthdate In Varchar2
) Return Varchar2
Is
tEmpId Number(38,0);
tBirthDate Date;
BEGIN
tEmpId := A1Seq_Emp.nextval;
tBirthdate := to_date('pBirthdate','dd/mm/yyyy');
Insert Into Employee(EmpId, EmpName, TaxFileNo, Gender, Salary, Birthdate)
Values (tEmpId, pEmpName, pTaxFileNo, pGender, pSalary, tBirthdate);
Commit;
Return null;
END;
Firstly you cannot call a function with DML in it in a select statement. You have to assign the output to a variable in a PL/SQL block, something like:
declare
l_output number;
begin
l_output := my_function(variable1, variable2);
end;
It's bad practice to do DML in a function; partly because it causes the errors you're coming across. You should use a procedure as detailed below. The other reason for this is that you're as always returning null there's no need to return anything at all!
create or replace procedure my_procedure ( <variables> ) is
begin
insert into employees( <columns> )
values ( <values > );
end;
The specific reason for your error is this line:
tBirthdate := to_date('pBirthdate','dd/mm/yyyy');
pBirthdate is already a string; by putting a ' around it you're passing the string 'pBirthdate' to the function to_date and Oracle can't convert this string into a day, month or year so it's failing.
You should write this as:
tBirthdate := to_date(pBirthdate,'dd/mm/yyyy');
You also don't need to specify number(38,0), you can just write number instead.
It is possible to return a value from a procedure using the out keyword. If we assume that you want to return empid you could write is as something like this:
create or replace procedure A1SF_ADDEMP (
pEmpName in varchar2
, pTaxFileNo in varchar2
, pGender in varchar2
, pSalary in number
, pBirthdate in varchar2
, pEmpid out number
) return varchar2 is
begin
pempid := A1Seq_Emp.nextval;
Insert Into Employee(EmpId, EmpName, TaxFileNo, Gender, Salary, Birthdate)
Values ( pEmpId, pEmpName, pTaxFileNo, pGender
, pSalary, to_date(pBirthdate,'dd/mm/yyyy');
end;
To just execute the procedure call it like this:
begin
A1SF_ADDEMP( EmpName, TaxFileNo, Gender
, Salary, Birthdate);
commit;
end;
If you want to return the empid then you can call it like this:
declare
l_empid number;
begin
l_empid := A1SF_ADDEMP( EmpName, TaxFileNo, Gender
, Salary, Birthdate);
commit;
end;
Notice how I've moved the commit to the highest level, this is to avoid committing stuff in every procedure when you might have more things you need to do.
Incidentally, if you're using Oracle 11g then there's no need to assign the value A1Seq_Emp.nextval to a variable. You can just insert it directly into the table in the values list. You, of course won't be able to return it, but you could return A1Seq_Emp.curval, as long as there's nothing else getting values from the sequence.
You should use a procedure (instead of a function) if you are not returning any values.
If you look at the line mentioned in the error message you can spot your error:
tBirthdate := to_date('pBirthdate','dd/mm/yyyy');
You are passing the string literal 'pBirthdate' to the to_date() call. But you want to pass the parameter, so it should be
tBirthdate := to_date(pBirthdate,'dd/mm/yyyy');
(note the missing single quotes arount pBirthdate).
So as a procedure the whole thing would look like this:
CREATE OR REPLACE PROCEDURE A1SF_ADDEMP
(pEmpName In Varchar2,
pTaxFileNo In Varchar2,
pGender In Varchar2,
pSalary In Number,
pBirthdate In Varchar2
)
IS
BEGIN
Insert Into Employee(EmpId, EmpName, TaxFileNo, Gender, Salary, Birthdate)
Values (A1Seq_Emp.nextval, pEmpName, pTaxFileNo, pGender, pSalary, to_date(pBirthdate,'dd/mm/yyyy'));
Commit;
END;
To run it:
execute A1SF_ADDEMP('Adesh', '33', 'M', 8000, '26/03/1990');
In your situation you need to use procedure with out parameter where your out param is your returning param that contains your desirable value. This is I think best practice in this situation when you want to use DML in select statement.
Second way to do it but its not a good practice is to use one function like yours but before return a value you need to use if statement to check what is the value and if value is what you desire to call in this if statement PRAGMA AUTONOMOUS_TRANSACTION procedure which will do your DML independently of function calling to it. For more information about pragma transactions your can read here:
http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/autonotransaction_pragma.htm
Best Regards and hope I was helpful