SQL Using an OBJECT as parameter for an ORDER mthod - sql

I have a TYPE called Employee which has an ORDER method which takes an Employee as a parameter. I have created a table of Employee and inserted some data. I am trying to test this ORDER method using SELECT however i am having difficulty satisfying the parameter. here is my code
CREATE OR REPLACE TYPE Employee AS OBJECT(
EmpID VARCHAR(15),
eName VARCHAR(30),
ePhone NUMBER,
eAddress VARCHAR(15),
ePosition VARCHAR(15),
eHireDt DATE,
salary NUMBER,
ORDER MEMBER FUNCTION orderSalary (e Employee) RETURN NUMBER)
NOT FINAL;
/
CREATE OR REPLACE TYPE BODY Employee AS
ORDER MEMBER FUNCTION orderSalary(e Employee) return number IS
BEGIN
IF(self.salary > e.salary) then
return(1);
ELSIF (self.salary < e.salary) then
return(-1);
ELSE
return(0);
END IF;
END;
END;
/
CREATE TABLE Emp OF Employee (EmpID PRIMARY KEY)
OBJECT IDENTIFIER PRIMARY KEY;
insert into Emp values('001','kabir',6477732272,'Pharmacy','clerk','2016-03-28',2000);
Here is the line that is giving me trouble:
SELECT p.orderSalary(Employee s) FROM Emp p;
All i need to do is test this method to make sure it works, is there any way i can create an instance of Employee to use as a parameter, or perhaps take a row from the Emp table? Thanks!

SELECT p.orderSalary( VALUE( p ) ) FROM Emp p;
Or:
SELECT p.orderSalary( DEREF( REF( p ) ) ) FROM Emp p;
Or:
SET SERVEROUTPUT ON;
DECLARE
e1 EMPLOYEE := NEW EMPLOYEE('001','kabir',6477732272,'Pharmacy','clerk',DATE '2016-03-28',2000);
e2 EMPLOYEE := NEW EMPLOYEE('002','bob',1234567890,'Library','assistant',DATE '2016-03-07',1000);
BEGIN
DBMS_OUTPUT.PUT_LINE( e1.orderSalary( e2 ) );
END;
/
Update - Select different rows:
insert into Emp values('001','kabir',6477732272,'Pharmacy','clerk','2016-03-28',2000);
insert into Emp values('002','bob',1234567890,'Library','assistant',DATE '2016-03-07',1000);
SELECT e1.ename AS name1,
e2.ename AS name2,
e1.orderSalary( VALUE( e2 ) )
FROM Emp e1
CROSS JOIN Emp e2;
Outputs:
NAME1 NAME2 E1.ORDERSALARY(VALUE(E2))
------ ------ -------------------------
kabir kabir 0
kabir bob 1
bob kabir -1
bob bob 0

Related

Procedure to insert data if not exist pl sql

I have a table with these columns (id,first_name,birth). I want to create a procedure that insert a new customer only if the id inserted doesn't exist in the table. If it already exist, then don't insert it. This is my code so far, but I got an error 'line 3 sql statement ignored'. Any idea? I need to use procedure and pl sql in oracle. Thanks!
CREATE OR REPLACE PROCEDURE add_emp(v_id IN int,
v_name IN varchar2,
v_bday IN date) IS
BEGIN
INSERT INTO Employees
(Id, First_name, Birth)
SELECT *
FROM (SELECT v_id, v_name, v_bday) AS tmp
WHERE NOT EXISTS (SELECT Id FROM Employees WHERE Id = v_id);
END;
/
DECLARE
m_id int := 3;
m_name varchar2 := 'John';
m_bday date := '16-Dec-1990';
BEGIN
add_cust(m_id, m_name, m_bday);
END;
/
Your procedure has some syntax issue which is fixed in following code:
CREATE OR REPLACE PROCEDURE ADD_EMP (
V_ID IN INT,
V_NAME IN VARCHAR2,
V_BDAY IN DATE
) IS
BEGIN
INSERT INTO EMPLOYEES (
ID,
FIRST_NAME,
BIRTH
)
SELECT V_ID,
V_NAME,
V_BDAY
FROM DUAL -- FROM clause was missing
WHERE NOT EXISTS (
SELECT ID
FROM EMPLOYEES
WHERE ID = V_ID
);
END;
/
Also, Your calling PL/SQL block has some issues which are corrected in the following code:
DECLARE
M_ID INT := 3;
M_NAME VARCHAR2(10) := 'John'; -- varchar2 must be declared with size
M_BDAY DATE := DATE '1990-12-16'; -- added date literal to convert string to date
BEGIN
ADD_CUST(M_ID, M_NAME, M_BDAY);
END;
/
In Oracle SELECT does not work without a FROM clause (other DBMS products are different). So you need to provide a table; you can use DUAL, which is a dummy table provided by Oracle which is guaranteed to return one row.
INSERT INTO Employees(Id,First_name,Birth)
SELECT v_id, v_name, v_bday
from dual
WHERE NOT EXISTS (
SELECT Id FROM Employees WHERE Id = v_id
);
Your INSERT statement would work with a slight change
CREATE OR REPLACE PROCEDURE add_emp(v_id Employees.Id%type,
v_name Employees.First_name%type,
v_bday Employees.Birth%type) IS
BEGIN
INSERT INTO Employees
(Id, First_name, Birth)
SELECT v_id, v_name, v_bday
FROM dual
WHERE NOT EXISTS (SELECT * FROM Employees WHERE Id = v_id);
END;
/
You can replace INSERT statement with MERGE as an alternative DML such as
MERGE INTO Employees e1
USING
(SELECT v_id AS id, v_name AS First_name, v_bday AS birth
FROM dual) e2
ON ( e1.id = e2.id )
WHEN MATCHED THEN UPDATE SET e1.First_name = e2.First_name,
e1.Birth = e2.Birth -- If you need to change for the matching case
WHEN NOT MATCHED THEN INSERT( e1.id, e1.First_name, e1.birth )
VALUES( e2.id, e2.First_name, e2.birth );

PL/SQL procedure that should output the following

Just cant get the output correct, maybe a loop problem ?
Have tried a few things but
Just cant get the output correct
should look like
Department 1 SALES managed by Alvin
Total number of employees: 5
00110 Alvin
00103 Ami
00109 Michael
00101 Peter
00107 Wendy
Department 2 ACCOUNTING managed by Alice
Total number of employees: 2
00120 Alice
00125 Angela
Department 3 GAMES managed by Bob
Total number of employees: 2
00150 Bob
00105 Robert
CREATE TABLE Department (
D# NUMBER(5) NOT NULL, /* Department number */
DName VARCHAR2(30) NOT NULL, /* Department name */
Manager# CHAR(5) NOT NULL, /* Department manager number */
MSDate DATE, /* Manager start date */
CONSTRAINT Department_PK PRIMARY KEY(D#),
CONSTRAINT Department_CK UNIQUE(DName)
);
CREATE TABLE Employee (
E# CHAR(5) NOT NULL, /* Employee number */
Name VARCHAR2(30) NOT NULL, /* Employee name */
DOB Date, /* Date of birth */
Address VARCHAR2(50), /* Home address */
Sex CHAR, /* M-Male, F-Female */
Salary NUMBER(7,2), /* Salary */
Super# CHAR(5), /* Supervisor number */
D# NUMBER(5), /* Department number */
CONSTRAINT Employee_PK PRIMARY KEY(E#),
CONSTRAINT Employee_FK1 FOREIGN KEY (Super#) REFERENCES Employee(E#),
CONSTRAINT Employee_FK2 FOREIGN KEY (D#) REFERENCES Department (D#)
);
ARE THE TABLES
THE CODE I HAVE BEEN TRYING IS
CREATE OR REPLACE PROCEDURE
INSERT_MANAGER IS
MANAGER_NAME VARCHAR(40);
DEPT_# NUMBER(5);
DEPT_NAME VARCHAR(40);
EMP_# NUMBER(5);
EMP_NAME VARCHAR(40);
EMP_TOTAL NUMBER(6);
CURSOR MANAGER IS
SELECT Name, Department.D#,DName
INTO MANAGER_NAME, DEPT_#, DEPT_NAME
FROM Employee
JOIN Department
ON
Department.D# = Employee.D#
WHERE E# = Manager#
ORDER BY DEPT_# ASC;
CURSOR EMPLOYEE IS
SELECT COUNT(NAME),NAME,E#
INTO EMP_TOTAL,EMP_NAME,EMP_#
FROM Employee
JOIN Department ON
Department.D# = Employee.D#
WHERE E# = Manager#
GROUP BY NAME,E#;
BEGIN
OPEN MANAGER;
OPEN EMPLOYEE;
LOOP
FETCH MANAGER INTO MANAGER_NAME, DEPT_#, DEPT_NAME;
FETCH EMPLOYEE INTO EMP_TOTAL,EMP_NAME,EMP_#;
EXIT WHEN MANAGER%notfound;
DBMS_OUTPUT.PUT_LINE('Department ' || DEPT_# || ' ' || DEPT_NAME || ' Managed By: ' || MANAGER_NAME );
DBMS_OUTPUT.PUT_LINE('Total Number Of Emploees ' || EMP_TOTAL);
DBMS_OUTPUT.PUT_LINE(EMP_# || ' ' || EMP_NAME);
END LOOP;
CLOSE MANAGER;
END;
/
My current output lhas the correct manager
but the incorrect TOTAL NUMBER OF EMPLOYEES
and is only showing 1 of the Employees under that manager
Department 1 SALES Managed By: Alvin
Total Number Of Emploees 1
150 Bob
Department 2 ACCOUNTING Managed By: Alice
Total Number Of Emploees 1
338 Helmus
Department 3 GAMES Managed By: Bob
Total Number Of Emploees 1
110 Alvin
THANKS IN ADVANCE FOR ANY HELP.
LUKE
The following produces your desired output while for the most part maintaining your logic flow (except for a 2nd loop for the Employee cursor). But I have made a couple syntactical changes.
First, names are important. Attempt to name objects according to the function being preformed. For example the MANAGER cursor really gets nothing about the manager (except for name). What is gets is information about the department; also
renamed the employee cursor (just because 2 objects with the same name - cursor and table). Structurally, I moved getting the employee count to the department cursor (manager) as it pertains to the department not the employee. As far as naming goes the procedure name itself is misleading as the procedure has nothing to do with inserting managers (but I let it); unless of course it's just part of a larger process.
Definition consistence is also important, and there are problems with that here. For example: Look at table column employee.name vs. the variable manager_name. And what's with employee.e# vs. emp_#? A method to stay consistent is to anchor procedure variables to the table columns they represent.
So instead of "manager_name varchar(40)" use "manager_name employee.name%type"
BTW: Varchar is frowned upon use varchar2.
But I left these with your original definitions. (Just for fun I played around this the data values a little - just values not structure.) The result then:
create or replace procedure insert_manager is
-- department cursor variables
manager_name varchar(40);
dept_# number(5);
dept_name varchar(40);
emp_total number(6);
-- employee cursor variables
emp_# number(5);
emp_name varchar(40);
-- cursors definitions
cursor department_cur is
select name, d#, dname, demps
from ( select e.name
, d.d#
, d.dname
, count(e.e#) over (partition by d.d#) demps
, e.e#
, d.manager# m#
from employee e
join department d on d.d# = e.d#
)
where e# = m#
order by d# asc;
cursor employee_cur(ed# integer) is
select e.name, e.e#
from employee e
where e.d# = ed#;
begin
dbms_output.put_line( 'Chipmunks Gaming, LLC: Department Employee List as of ' || to_char(sysdate, 'dd-Mon-yyyy'));
open department_cur;
loop
fetch department_cur into manager_name, dept_#, dept_name, emp_total;
exit when department_cur%notfound;
dbms_output.put_line('Department ' || dept_# || ' ' || dept_name || ' Managed By: ' || manager_name);
dbms_output.put_line('Total Number Of Employees ' || emp_total);
open employee_cur (dept_#);
loop
fetch employee_cur into emp_name, emp_#;
exit when employee_cur%notfound;
dbms_output.put_line(emp_# || ' ' || emp_name);
end loop;
close employee_cur;
end loop;
close department_cur;
end insert_manager;
-- Generate test data
insert into Department (D#, DName, Manager#)
select 1, 'SALES', '00150' from dual union all
select 2, 'ACCOUNTING','00120' from dual union all
select 3, 'GAMES', '00110' from dual ;
insert into employee(E#, Name ,D# )
select '00150', 'Simon' ,1 from dual union all
select '00103', 'Ami' ,1 from dual union all
select '00109', 'Michael' ,1 from dual union all
select '00101', 'Peter' ,1 from dual union all
select '00107', 'Wendy' ,1 from dual union all
select '00120', 'Theodore' ,2 from dual union all
select '00125', 'Angela' ,2 from dual union all
select '00110', 'Alvin' ,3 from dual union all
select '00105', 'Robert' ,3 from dual ;
---------------------------------------------------------------------------------
-- run it
begin
insert_manager;
end;
The next big step would be using Implicit cursors (cursor for loops) instead of Explicit cursors. That would take of Opening, Looping through, and Closing the cursors. I'll leave that for you.
Good Luck. Hope this helps.

How to solve single-row subquery returns more than one row

I have an employee table and it contains salary table. I want to give %10 increase to all current employees. I tried to update all employees' salary dates to specific date but I encountered problem with single-row subquery.
My database like this:
CREATE TYPE TEMPORAL_VARCHAR AS OBJECT (
VALID_TIME_LOWER_BOUND DATE,
VALID_TIME_UPPER_BOUND DATE,
VALUE_PART VARCHAR2(50) );
CREATE TYPE TEMPORAL_NUMBER AS OBJECT (
VALID_TIME_LOWER_BOUND DATE,
VALID_TIME_UPPER_BOUND DATE,
VALUE_PART NUMBER );
CREATE TYPE NAME_TYPE AS TABLE OF TEMPORAL_VARCHAR;
CREATE TYPE ADDRESS_TYPE AS TABLE OF TEMPORAL_VARCHAR;
CREATE TYPE DEPARTMENT_TYPE AS TABLE OF TEMPORAL_VARCHAR;
CREATE TYPE MANAGER_TYPE AS TABLE OF TEMPORAL_VARCHAR;
CREATE TYPE SALARY_TYPE AS TABLE OF TEMPORAL_NUMBER;
CREATE TABLE EMPLOYEE (
SSN NUMBER primary key,
NAME NAME_TYPE,
ADDRESS ADDRESS_TYPE ,
BIRTH_DATE DATE,
MANAGER MANAGER_TYPE ,
DEPARTMENT DEPARTMENT_TYPE,
SALARY SALARY_TYPE
)
NESTED TABLE NAME STORE AS NAME_TABLE,
NESTED TABLE ADDRESS STORE AS ADDRESS_TABLE,
NESTED TABLE MANAGER STORE AS MANAGER_TABLE,
NESTED TABLE DEPARTMENT STORE AS DEPARTMENT_TABLE,
NESTED TABLE SALARY STORE AS SALARY_TABLE
;
How to solve this problem? I tried to do this
UPDATE TABLE(
SELECT E.SALARY
FROM EMPLOYEE E
) SAL
SET SAL.VALID_TIME_UPPER_BOUND = '11.16.2015'
WHERE SAL.VALID_TIME_UPPER_BOUND = TO_DATE('12.31.9999','MM.DD.YYYY');
1st it can be duplicate
2nd see sample here
3rd in your code you need bring the where condition into select
UPDATE TABLE(
SELECT E.SALARY
FROM EMPLOYEE E
WHERE ssn in (SELECT ssn FROM EMPLOYEE e
WHERE to_date('01.01.2015','mm.dd.yyyy') in (
SELECT VALID_TIME_UPPER_BOUND FROM TABLE(e.salary)
)
)
) SAL
SET SAL.VALID_TIME_UPPER_BOUND = to_date('01.01.9999','mm.dd.yyyy')
test data
INSERT INTO EMPLOYEE(SSN, salary) values (1, SALARY_TYPE ());
INSERT INTO EMPLOYEE(SSN, salary) values (2, SALARY_TYPE ());
INSERT INTO EMPLOYEE(SSN, salary) values (3, SALARY_TYPE ());
INSERT INTO TABLE(SELECT salary FROM EMPLOYEE
WHERE ssn = 1)
VALUES (to_date('01.01.2005','mm.dd.yyyy'), to_date('01.01.2015','mm.dd.yyyy'), 1);
INSERT INTO TABLE(SELECT salary FROM EMPLOYEE
WHERE ssn = 2)
VALUES (to_date('02.02.2005','mm.dd.yyyy'), to_date('02.02.2015','mm.dd.yyyy'), 2);
INSERT INTO TABLE(SELECT salary FROM EMPLOYEE
WHERE ssn = 3)
VALUES (to_date('03.03.2005','mm.dd.yyyy'), to_date('03.03.2015','mm.dd.yyyy'), 3);
p.s do you really need the complexity?
I solved my problem using iteration like this
BEGIN
FOR employees IN (SELECT SSN FROM EMPLOYEE)
LOOP
UPDATE TABLE(
SELECT E.SALARY
FROM EMPLOYEE E
WHERE E.SSN = employees.SSN
) SAL
SET SAL.VALID_TIME_UPPER_BOUND = '11.16.2015'
WHERE SAL.VALID_TIME_UPPER_BOUND = TO_DATE('12.31.9999','MM.DD.YYYY');
END LOOP;
END;

PL/SQL MERGE Using Collection

I am having trouble merging a table with a collection.
Let's say I have a table emp.
Here is my PL/SQL code snippet.
TYPE empcol is table of emp%ROWTYPE INDEX BY BINARY_INTEGER;
tmpemp empcol;
-- Code here to load data from a CSV file into tmpemp collection
-- tmpemp(1).emp_id := parsedstring
-- etc.
MERGE INTO emp A using tmpemp B ON A.emp_id = B.emp_id
WHEN MATCHED THEN UPDATE SET A.fname = B.fname, A.lname = B.lname
WHEN NOT MATCHED THEN INSERT (emp_id, fname, lname) VALUES (b.emp_id, b.fname, b.lname);
Compiler doesn't like it. Its throwing ORA-0942 - Table or View doesn't exist.
What am I doing wrong? or How can I do this better.
Thanks a lot for any help you can provide.
PL/SQL types like emp%ROWTYPE or TABLE OF ... INDEX BY ... cannot be used in SQL queries.
The type must be declared as SQL type (not as PL/SQL type) to be used in SQL query.
Try this approach (example):
create table emp(
firstname varchar2(100),
salary number
);
insert into emp values( 'John', 100 );
commit;
create type my_emp_obj is object(
firstname varchar2(100),
salary number
);
/
create type my_emp_obj_table is table of my_emp_obj;
/
declare
my_emp_tab my_emp_obj_table;
begin
null;
my_emp_tab := my_emp_obj_table( my_emp_obj( 'John', 200 ), my_emp_obj( 'Tom', 300 ));
MERGE INTO emp
USING ( SELECT * FROM TABLE( my_emp_tab )) src
ON ( emp.firstname = src.firstname )
WHEN MATCHED THEN UPDATE SET salary = src.salary
WHEN NOT MATCHED THEN INSERT VALUES( src.firstname, src.salary );
end;
/
select * from emp;
FIRSTNAME SALARY
----------------------- ----------
John 200
Tom 300

PL/SQL creating a trigger problems

I have to create a trigger on an Employee table. If an INSERT or UPDATE statement is issued for the Employee table the trigger launches and makes sure that the value of 'salary' field meets the criteria in the job_min_sal table. After trying over and over I got a mutating table error and now am very frustrated and don't know what to do.
JOB_MIN_SALARY TABLE:
JOB VARCHAR2(50) PRIMARY KEY
MIN_SAL NUMBER(7,2) NOT NULL
The JOB_MIN_SAL table is populated with a variety of job titles and salaries. I am confused working with my trigger and wondering if I could get some assistance where to go from here
CREATE OR REPLACE TRIGGER employee_job_salary
BEFORE INSERT OR UPDATE OF SALARY on employee
FOR EACH ROW
DECLARE
v_salary NUMBER;
BEGIN
SELECT minimum_salary
INTO v_salary
FROM job_min_salary
WHERE UPPER(job) = UPPER(:NEW.job);
I know I am really far off I am just looking for help as for what this requires and what steps I need to take to get this. Thanks!
The EMPLOYEE table:
(
EMPLOYEE_ID NUMBER(4)
EMPLOYEE_NAME VARCHAR2(20)
JOB VARCHAR2(50)
MANAGER_ID NUMBER(4)
HIRE_DATE DATE
SALARY NUMBER(9)
COMMISION NUMBER(9)
DEPARTMENT_ID NUMBER(4)
);
i am supposing you are doing something like
comparing new salary with min salary criterion and update only if :new.SALARY >= v_salary
what are you doing if this is not met, are u trapping an exception or just ignoring the error or returning an error code to debug.
post more info
CREATE TABLE job_min_salary
(
job VARCHAR2(50) PRIMARY KEY,
min_sal NUMBER(7,2) NOT NULL
);
INSERT INTO job_min_salary VALUES('CEO','100');
-- 1 rows inserted.
CREATE TABLE employee
(
employee_id NUMBER(4),
employee_name VARCHAR2(20),
job VARCHAR2(50),
manager_id NUMBER(4),
hire_date DATE,
salary NUMBER(9),
commision NUMBER(9),
department_id NUMBER(4)
);
INSERT INTO employee VALUES(1, 'Name', 'CEO', 1, TO_DATE('2000-01-01', 'YYYY-MM-DD'), 80, 80, 1);
-- 1 rows inserted.
CREATE OR REPLACE TRIGGER employee_job_salary
BEFORE INSERT OR UPDATE OF salary ON employee
FOR EACH ROW
DECLARE
v_salary NUMBER(1);
BEGIN
SELECT 1
INTO v_salary
FROM job_min_salary
WHERE UPPER(job) = UPPER(:NEW.job)
AND :NEW.salary >= min_sal;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20999, 'Salary value is too low for given job');
END;
-- TRIGGER EMPLOYEE_JOB_SALARY compiled
SELECT * FROM employee;
-- 1 Name CEO 1 2000-01-01 00:00:00 80 80 1
UPDATE employee
SET salary = 10
WHERE job = 'CEO';
-- ORA-20999: Salary value is too low for given job
UPDATE employee
SET salary = 100
WHERE job = 'CEO';
-- 1 rows updated.
SELECT * FROM employee;
-- 1 Name CEO 1 2000-01-01 00:00:00 100 80 1