Creating a Package with a Procedure - sql

I'm trying to create a procedure where the input is lname from table Employee and output is sal and average from table Information. Both outputs are numbers and average is a decimal number. This is what I have so far.
CREATE OR REPLACE PACKAGE getSalAvgPack IS PROCEDURE getSalAvg
(name IN Employee.lname%TYPE,
pSal OUT NUMBER,
pAvg OUT NUMBER);
END;
.
/
The package compiles fine.
CREATE OR REPLACE PACKAGE BODY getSalAvgPack IS PROCEDURE getSalAvg
(name IN Employee.lname%TYPE,
pSal OUT NUMBER,
pAvg OUT NUMBER)
IS
BEGIN
SELECT Information.sal, Information.average
INTO pSal, pAvg
FROM Information
WHERE Information.eid=Employee.eid AND name=lname;
END;
END;
.
/
When I try to compile the package body I get these errors
PL/SQL: SQL Statement ignored (for line 7, column 2)
PL/SQL: ORA-00904: "LNAME": invalid identifier

You need to join in the Employee table:
SELECT Information.sal, Information.average
INTO pSal, pAvg
FROM Information join
Employee
on Information.eid=Employee.eid
WHERE name=lname;

It appears that you are missing a join
SELECT Information.sal, Information.average
INTO pSal, pAvg
FROM Information
JOIN Employee ON (Information.eid = Employee.eid)
WHERE name = Employee.lname;
As a general principle, I would strongly suggest using a naming convention for your parameters that differentiates them from columns in your tables. If name is a column in either table that you're joining, your predicate will compare the Employee.lname against that column rather than using the name parameter from your procedure. A common convention is to prefix all parameters with p_ so that your query becomes
SELECT Information.sal, Information.average
INTO p_Sal, p_Avg
FROM Information
JOIN Employee ON (Information.eid = Employee.eid)
WHERE p_name = Employee.lname;

Related

PostgreSQL procedure: "No operator matches the given name and argument types"

In PostgreSQL database I created procedure which looks like this:
CREATE OR REPLACE PROCEDURE creator(ID uuid, EMPLOYEES VARCHAR[]) AS $FUNCTION$
BEGIN
DELETE FROM SURVEYS_EMPLOYEES_RELATIONSHIP
WHERE SURVEY_ID = ID
AND EMPLOYEE NOT IN (EMPLOYEES);
--
INSERT INTO SURVEYS_EMPLOYEES_RELATIONSHIP (SURVEY_ID, EMPLOYEE)
SELECT SURVEY_ID ID, EMPLOYEE FROM UNNEST(ARRAY[EMPLOYEES]) EMPLOYEE
ON CONFLICT ON CONSTRAINT unique_key
DO NOTHING;
END;
$FUNCTION$ LANGUAGE plpgsql;
As you can see in this procedure I am trying to DELETE some entries and then INSERT other entries. I call this procedure like this:
CALL creator('99c89a24-fff2-4cbc-a542-b1e956a352f9', ARRAY['NNogerbek#gmail.com', 'IKim#gmail.com'])
For some reason it raise error:
SQL Error [42883]: ERROR: operator does not exist: character varying <> character varying[]
No operator matches the given name and argument types. You might need to add explicit type casts.
PL/pgSQL function creator(uuid,character varying[]) line 3 at SQL statement
Where exactly in procedure I made error?
In the DELETE query, EMPLOYEE NOT IN (EMPLOYEES) isn't right. IN isn't "unnesting" the array here and you end up comparing a varchar against a varchar[].
You can try to replace it by
EMPLOYEE NOT IN (SELECT e FROM unnest(EMPLOYEES) u (e))
"manually" unnesting the array,
NOT EMPLOYEES #> ARRAY[EMPLOYEE]
using the array contains operator or
EMPLOYEE <> ALL (EMPLOYEES)
using ANY, where the array is "unnested" automatically. (And probably some more.)

Select statement inside an Oracle stored procedure

I am trying to self-teach myself SQL. I am working on calling a simple select statement from a stored procedure in oracle.
I have created an employee database with 2 tables; employees and department. I want a select statement which returns all employees from a certain department.
This is what I have so far and I can't figure out where I am going wrong
create or replace procedure user_empdepart (depart_name varchar(40))
AS
BEGIN
SELECT emp_name FROM employee JOIN department ON department.departmentID =
employee.departmentID
WHERE depart_name = 'research';
END;
And then I hope to call the above by;
exec user_empdepart(research);
I am using SQL Developer Oracle
I get the following error message:
Error(99,50): PLS-00103: Encountered the symbol "DEPARTMENT" when expecting one of the following: , ; for group having intersect minus order start union where connect
I think you need this -
create or replace procedure user_empdepart (depart_name varchar(40))
AS
DECLARE dept_name varchar(200);
dept_name := depart_name;
BEGIN
SELECT emp_name FROM employee JOIN department ON department.departmentID = employee.departmentID
WHERE depart_name := dept_name;
END;
In Oracle database, SQL statements executed directly in the client (like SQL-Developer or SQLPlus) are treated as plain, ordinary SQL.
But if you are going to use SQL commands in a procedure or a function, Oracle treats them as PL/SQL commands, not SQL.
For simple INSERT, DELETE and UPDATE commands the syntax in PL/SQL is the same as in SQL, however for SELECT the syntax is slightly different: PL/SQL SELECT INTO Statement
You must use SELECT expression-list INTO variables-list/record FROM ..... syntax. You cannot use SELECT expression-list FROM ....., this generates a syntax error.

How to use in statement with nested table

Hey there I have a function, and part of the function is to make sure that the selected value is within the passed in table of varchar2s. To start I declare a varchar2 table type like so.
create or replace type Varchar2Table is table of varchar2(200)
Then I have the function which accepts the nested table parameter and has a select statement on them.
function SelectPeople(inputNames Varchar2Table) return People
begin
--stuff
select * from person_table where name in inputNames; --line of interest
--more stuff
end;
This doesn't seem to work though, I get the following error:
ORA-00932: inconsistent datatypes: expected NUMBER got
ENGSPL5.VARCHAR2TABLE
Any suggestions?
The TABLE operator allows nested tables to be used in SQL statements. The function was also missing an IS and an INTO.
create or replace type Varchar2Table is table of varchar2(200);
create table person_table(id number, name varchar2(100));
create or replace function SelectPeople(inputNames Varchar2Table) return number
is --Missing "IS".
type numberTable is table of number; --Need a collection to store results.
numbers numberTable;
begin
select id
bulk collect into numbers --Missing "INTO".
from person_table
where name in (select column_value from table(inputNames)); --Missing "TABLE".
--Alternatively a multiset condition can be used.
--where name member of inputNames;
--Dummy return value to make the function compile.
return 1;
end;
/

Function returning TABLE and the JOIN in SELECT query?

I want to create a local FUNCTION which returns result as NESTED TABLE in my PROCEDURE. Then, I wish to JOIN the nested table with another table in a SELECT query like this:
PROCEDURE TEST_DEPID (SOR IN OUT SYS_REFCURSOR) AS
TYPE TAB IS TABLE OF HR.EMPLOYEES.SALARY%TYPE
INDEX BY BINARY_INTEGER;
FUNCTION GET_SALARY (P_DEPARTMENT_ID NUMBER)
RETURN TAB
IS RETURN_TBL TAB;
BEGIN
SELECT SALARY
BULK COLLECT INTO RETURN_TBL
FROM HR.EMPLOYEES WHERE DEPARTMENT_ID = TRIM(P_DEPARTMENT_ID);
RETURN RETURN_TBL;
END GET_SALARY;
BEGIN
OPEN SOR FOR
SELECT * FROM HR.EMPLOYEES JOIN TABLE (GET_SALARY('60')) B ON A.SALARY = B.SALARY;
END;
And the errors I got are:
[1]:(Error): PLS-00231: function 'GET_SALARY' may not be used in SQL
[2]:(Error): PL/SQL: ORA-00904: : invalid identifier
[3]:(Error): PL/SQL: SQL Statement ignored
Please give me some advice.
A local function cannot be used inside a SELECT. Only global functions will work.
Oracle uses 2 engines running SQL and PL/SQL. Running your SELECT the SQL engine can access all functions, but only if they are public known. The local function is not known to the public.

PL/SQL error question

I am trying to write a stored procedure that inserts a row into an employee table. If the department does not exist, that department needs to be inserted into the departments table. I have the following code:
drop table employees;
drop table departments;
create table departments(
dept varchar2(30),
dept_number number,
dept_city varchar2(30),
CONSTRAINT pk_dept PRIMARY KEY(dept)
);
create table employees(
dept varchar2(30),
employee_name varchar2(40),
employee_id number,
CONSTRAINT pk_id PRIMARY KEY(employee_id),
CONSTRAINT fk_dept FOREIGN KEY (dept) REFERENCES departments(dept)
);
CREATE OR REPLACE PROCEDURE employeeadd(
a_dept IN VARCHAR2,
a_employee_name IN VARCHAR2,
a_employee_id IN NUMBER)
as
li_count NUMBER;
BEGIN
sp_check_dept(a_dept, li_count);
if li_count = 0 then
INSERT INTO departments (dept) values (a_dept);
return;
end if;
INSERT INTO employee values (a_dept, a_employee_name, a_employee_id);
end;
/
create or replace procedure sp_check_dept(a_dept IN NUMBER,
a_count OUT NUMBER)
as
begin
select count(*)
into a_count
from departments
where dept_number = a_dept;
end;
/
When I run my execute statement as execute employeeadd('marketing', 'john', 10); I get the following errors. I can't seem to figure out how to get past the errors and/or write this correctly:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at "employeeadd", line 8
ORA-06512: at line 1
Why is li_count declared outside the BEGIN...END block? Do you need to assign it before sending it as an argument to sp_check_dept()?
Edit: Just saw your followup comment: sp_check_dept is expecting a number as its first parameter; you have declared a_dept as VARCHAR.
sp_check_dept takes a department number an input parameter (a NUMBER) and returns a count as an ouput parameter. employeeadd is passing a department name (a VARCHAR2) as the first parameter to sp_check_dept. There are a couple of ways to fix this. In general, you'll want a more consistent method of naming parameters to make it easier to identify these problems.
Option 1: Use the department name for both functions
create or replace procedure sp_check_dept(p_dept_name IN departments.dept%type,
p_count OUT NUMBER)
as
begin
select count(*)
into p_count
from departments
where dept = p_dept_name;
end;
/
CREATE OR REPLACE PROCEDURE employeeadd(
p_dept_name IN departments.dept%type,
p_employee_name IN employees.employee_name%type,
p_employee_id IN employees.employee_id%type)
as
li_count NUMBER;
BEGIN
sp_check_dept(p_dept_name, li_count);
if li_count = 0 then
INSERT INTO departments (dept)
VALUES (p_dept_name);
end if;
INSERT INTO employee(dept, employee_name, employee_id)
VALUES (p_dept, p_employee_name, p_employee_id);
end;
/
Option 2: Convert the department name in employeeAdd to the department number before passing it to sp_check_dept
create or replace procedure sp_check_dept(p_dept_number IN departments.dept_number%type,
p_count OUT NUMBER)
as
begin
select count(*)
into p_count
from departments
where dept_number = p_dept_number;
end;
/
CREATE OR REPLACE FUNCTION get_dept_number( p_dept_name IN departments.dept%tyep )
RETURN departments.dept_number%type
IS
l_dept_number departments.dept_number%type;
BEGIN
SELECT dept_number
INTO l_dept_number
FROM departments
WHERE dept = p_dept_name;
RETURN l_dept_number
END;
/
CREATE OR REPLACE PROCEDURE employeeadd(
p_dept_name IN departments.dept%type,
p_employee_name IN employees.employee_name%type,
p_employee_id IN employees.employee_id%type)
as
li_count NUMBER;
BEGIN
sp_check_dept( get_dept_number(p_dept_name), li_count);
if li_count = 0 then
INSERT INTO departments (dept)
VALUES (p_dept_name);
end if;
INSERT INTO employee(dept, employee_name, employee_id)
VALUES (p_dept, p_employee_name, p_employee_id);
end;
/
A couple of other observations
I removed the RETURN statement from your IF statement in employeeAdd. You almost certainly do not want to exit the procedure after inserting a row into the DEPARTMENTS table before inserting the row into the EMPLOYEE table.
Your table definition used the plural EMPLOYEES. Your procedure used the singular EMPLOYEE. I did not correct that because I wasn't sure whether the DDL you posted was incorrect or whether the procedure you posted was incorrect.
It would, in general, make far more sense for sp_check_dept to be implemented as a function that returned the count rather than as a procedure with an OUT parameter. If a piece of code simply exists to return data to the caller, it should be declared as a function.
From a data model standpoint, the column name DEPT isn't particularly good. It would be far more appropriate to use something like DEPARTMENT_NAME that conveys what the column actually represents.
From a data model standpoint, having the VARCHAR2 column DEPT (even if it is renamed to DEPARTMENT_NAME) as the primary key of DEPARTMENTS and the foreign key in EMPLOYEES does not make much sense. The primary key should be immutable. However the name of the department will change over time. It would make far more sense for the DEPARTMENT_NUMBER to be the primary key and for the DEPARTMENT_NAME to simply be marked as unique. That will make it far easier when the Marketing department gets renamed Advertising in the future because you won't have to chase down all the child tables to update them.
You should pick a naming convention for procedures and stick with that. I would prefer check_dept and add_employee (verb followed by subject, underscores separating words, no prefix). But if you wanted sp_check_dept and sp_add_employee or checkDept and addEmployee or even sp_dept_check and sp_employee_add that would be fine. But you'll drive yourself, and the other developers, crazy if there is no pattern to your procedure naming conventions.
2 possibilities I can see:
1. the employee table has columns in a different order than your insert statement and it's trying to convert dept or name to the id
2. the value set into li_count isn't a number so it's trying to convert the return value to a number and giving you the error