I have two table one is employee and one is department. I am creating the dynamic view that will rank all departments by salary. The view should pull information from Department and Employee, sum the salary by department, and rank the department by salary.
CREATE TABLE DEPARTMENT
(DEPARTMENT_ID NUMBER PRIMARY KEY,
DEPARTMENT_NAME VARCHAR(30) NOT NULL
);
CREATE TABLE JOBS
(JOB_ID NUMBER PRIMARY KEY,
JOB_TITLE VARCHAR(35) NOT NULL,
MIN_SALARY DECIMAL NOT NULL,
MAX_SALARY DECIMAL NOT NULL
);
CREATE TABLE EMPLOYEES
(EMPLOYEE_ID NUMBER PRIMARY KEY,
FIRST_NAME VARCHAR(20) NOT NULL,
LAST_NAME VARCHAR(25) NOT NULL,
EMAIL VARCHAR(25) NOT NULL,
PHONE_NUMBER VARCHAR(20) NOT NULL,
HIRE_DATE DATE NOT NULL,
JOB_ID NUMBER NOT NULL,
SALARY DECIMAL NOT NULL,
DEPARTMENT_ID NUMBER NOT NULL,
CONSTRAINT emp_job_fk FOREIGN KEY(JOB_ID) REFERENCES JOBS(JOB_ID),
CONSTRAINT emp_department_fk FOREIGN KEY(DEPARTMENT_ID) REFERENCES DEPARTMENT(DEPARTMENT_ID)
);
INSERT INTO DEPARTMENT (DEPARTMENT_ID,DEPARTMENT_NAME)
VALUES(1,'IT');
INSERT INTO DEPARTMENT (DEPARTMENT_ID,DEPARTMENT_NAME)
VALUES(2,'Sales');
INSERT INTO JOBS (JOB_ID,JOB_TITLE,MIN_SALARY,MAX_SALARY)
VALUES (1,'IT Administrator',250000.00,50000.00);
INSERT INTO JOBS (JOB_ID,JOB_TITLE,MIN_SALARY,MAX_SALARY)
VALUES (2,'Salesman',200000.00,40000.00);
Here is I create so far but it give me a error
ORA-00979: not a GROUP BY expression
00979. 00000 - "not a GROUP BY expression"
*Cause:
*Action:
Error at Line: 4 Column: 9
Here is my code
select department_id,department_name,total_salary
from(
select department_id,department_name, SALARY, count(*) as total_salary from(
select dep.department_id , dep.department_name ,emp.SALARY,
DENSE_RANK() OVER (PARTITION BY department_name ORDER BY salary)
from departments dep
inner join employees emp on dep.DEPARTMENT_ID = emp.DEPARTMENT_ID
)
GROUP BY SALARY)
Your query needs to join EMPLOYEES (to get the salaries) to DEPARTMENT (to get the DEPARTMENT_NAME). Calculate the total salary for each department by summing the employee salaries, not counting them. The GROUP BY needs to include the non-aggregated columns.
Then you need to rank the departments by the total salary per department. This query ranks the departments with highest salary = 1. It uses a left join to cater for departments with no employees.
select department_id
, department_name
, total_salary
, rank() over (order by total_salary desc) as dept_rank
from (
select d.department_id
, d.department_name
, sum(e.SALARY) as total_salary
from department d
left join employees e
on e.department_id = d.department_id
group by d.department_id
, d.department_name
)
/
Create an anonymous PL/SQL block, which looks for an employee by last name based on a SQL parameter (e.g. &variable) that the user responds to with a valid last_name in the EMPLOYEES table (i.e. King, or Kochhar, or De Haan, or Hunold, or Ernst, etc….…). If the employee last_name exists in the EMPLOYEES table, then insert into the OUTPUT_LOG table, the following string:
'Employee is is Found'. Test your PL/SQL block by checking the content of the OUTPUT_LOG table. You should find the string the you inserted in the OUTPUT_LOG table.
employee table has these columns
EMPLOYEE_ID ,
FIRST_NAME ,
LAST_NAME ,
EMAIL ,
PHONE_NUMBER ,
HIRE_DATE ,
JOB_ID ,
SALARY ,
COMMISSION_PCT ,
MANAGER_ID ,
DEPARTMENT_ID
output_log table only has one column VARCHAR2(250) called data and id column as PRIMARY KEY
I am struggling to figure check if the name exist in the table. I thought about using
begin
SELECT e.LAST_NAME as LAST_NAME, o.data
INTO OUTPUT_LOG
FROM EMPLOYEES as e, OUTPUT_LOG as O
WHERE e.employee_id = o.id
end;
but i dont think it would be wise and make sense to do so
Since this appears to be a homework question, I'm not going to solve it all for you but (to help you on your way) you could just use SELECT ... INTO and if a row is not found it will raise a NO_DATA_FOUND exception which you can catch (and use this to bypass the INSERT). Something like this:
DECLARE
p_found NUMBER(1,0);
BEGIN
SELECT 1
INTO p_found
FROM Employees
WHERE ROWNUM = 1
AND last_name = 'King'; -- replace with your where condition
-- then do the insert here (since it will only reach this point if
-- a row was found).
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
END;
/
I have difficulty writing a PL/SQL Statement to display the following output.
These are my tables:
Employee (E#, FName, LName,...)
SkillPossessed (E#, SName, Level) (Employee may possessed more than 1 skills)
I need to display the output as follow:
1 Mary Loo:Sewing,Dancing,Cooking
2 Peter Pan:Painting, Singing
...
How do I do it?
Thanks
No need for PL/SQL, just use LISTAGG.
create table employee (id number, fname varchar2(20), lname varchar2(20), primary key (id));
create table skillpossessed(employee_id number, sname varchar2(20), lvl number, constraint fk_employee_id foreign key(employee_id) references employee(id));
insert into employee values (1, 'Mary', 'Loo');
insert into employee values (2, 'Peter', 'Pan');
insert into skillpossessed values (1, 'Sewing', 1);
insert into skillpossessed values (1, 'Dancing', 2);
insert into skillpossessed values (1, 'Cooking', 5);
insert into skillpossessed values (2, 'Painting', 3);
insert into skillpossessed values (2, 'Singing', 1);
select e.id, e.fname, e.lname, listagg (s.sname, ',')
within group (order by s.sname) as skills
from employee e, skillpossessed s
where e.id = s.employee_id group by e.id, e.fname, e.lname;
ID FNAME LNAME SKILLS
---------- ------ ----- ----------------------
1 Mary Loo Cooking,Dancing,Sewing
2 Peter Pan Painting,Singing
Let's say I have a collection that contains nested tables:
CREATE TYPE address_type AS OBJECT (
address_code VARCHAR2(1),
address VARCHAR2(30),
city VARCHAR2(30),
state VARCHAR2(3),
zip VARCHAR2(10));
CREATE TYPE addresses_type AS TABLE OF address_type;
-- You can see here that the person may have multiple addresses (addrs)
CREATE TYPE person_type AS OBJECT (
personID NUMBER,
name VARCHAR2(30),
birthdate DATE,
gender VARCHAR2(1),
addrs addresses_type);
CREATE TYPE people_type as TABLE OF person_type;
If I had a PLSQL block and I wanted to create and load an object of people with their address from the tables below, what is the easiest way to do this? Would I have to perform multiple queries?
DECLARE
the_people people_type;
BEGIN
-- want to Query and load "the_people" with everybody in the tables below:
..
END;
Tables: Foreign key is PERSON_ID
PERSON
------
PERSON_ID
NAME
BIRTHDATE
GENDER
ADDRESSES
---------
PERSON_ID
ADDRESS_CODE
ADDRESS
CITY
STATE
ZIP
You can do it in one query:
CREATE TABLE person (
person_ID NUMBER,
name VARCHAR2(30),
birthdate DATE,
gender VARCHAR2(1)
);
CREATE TABLE addresses (
person_id NUMBER,
address_code VARCHAR2(1),
address VARCHAR2(30),
city VARCHAR2(30),
state VARCHAR2(3),
zip VARCHAR2(10)
);
INSERT INTO person VALUES (1, 'Brown', SYSDATE, 'M');
INSERT INTO person VALUES (2, 'Smith', SYSDATE, 'M');
INSERT INTO addresses VALUES (1, 'A', 'B', 'C', 'D', '1');
INSERT INTO addresses VALUES (1, 'A', 'BB', 'CC', 'DD', '1');
INSERT INTO addresses VALUES (2, 'B', 'E', 'F', 'G', '1');
COMMIT;
DECLARE
the_people people_type;
BEGIN
SELECT
person_type(
person_id,
name,
birthdate,
gender,
(SELECT CAST(MULTISET (
SELECT
address_code,
address,
city,
state,
zip
FROM addresses
WHERE person_id = p.person_id) AS addresses_type
)
FROM dual
)
)
BULK COLLECT INTO the_people
FROM
person p
;
dbms_output.put_line(the_people.COUNT);
dbms_output.put_line(the_people(1).name);
dbms_output.put_line(the_people(1).addrs(1).address);
dbms_output.put_line(the_people(1).addrs(2).address);
dbms_output.put_line(the_people(2).name);
dbms_output.put_line(the_people(2).addrs(1).address);
END;
Sample output produced by the block to test if the solution works:
2
Brown
B
BB
Smith
E
I'm trying to make a Procedure that will duplicate multiple rows of a table (or only one single row) and incrementing the ID for each row insertion.
My problem is that inside my procedure I used a cursor to select the rows to duplicate, when i select all rows without WHERE condition in that cursor everything works fine.
But when i set a WHERE condition to select only one row... nothing happens
Here is my procedure
CREATE OR REPLACE PROCEDURE DuplicateEmployee (p_EmployeeID IN Employee.id%TYPE)
AS
p_New_EmployeeID Employee.id%TYPE;
CURSOR c_DuplicateEmployee IS
SELECT *
FROM Employee
WHERE Employee.id = p_EmployeeID; -- if this line is deleted all content is duplicated
row_Employee c_DuplicateEmployee%ROWTYPE;
BEGIN
FOR myEmployee IN c_DuplicateEmployee LOOP
p_New_EmployeeID := employee_seq.NEXTVAL;
INSERT INTO Employee(id, first_name, last_name, start_date, end_date, salary, city, description)
VALUES(p_New_EmployeeID, myEmployee.first_name, myEmployee.last_name, myEmployee.start_date, myEmployee.end_date, myEmployee.salary, myEmployee.city, myEmployee.description);
END LOOP;
COMMIT;
END DuplicateEmployee;
I know in this example having a procedure selecting a primary key to duplicate is pointless but in my production base it will be used to select a Foreign key.
Bellow is the code require to create a the test table and SEQUENCE I used for this procedure
CREATE TABLE Employee
(
ID VARCHAR2(4 BYTE) NOT NULL,
First_Name VARCHAR2(10 BYTE),
Last_Name VARCHAR2(10 BYTE),
Start_Date DATE,
End_Date DATE,
Salary NUMBER(8,2),
City VARCHAR2(10 BYTE),
Description VARCHAR2(15 BYTE)
);
INSERT
INTO Employee
(ID, First_Name, Last_Name, Start_Date, End_Date, Salary, City, Description)
VALUES
('01', 'Jason', 'Martin', to_date('19960725','YYYYMMDD'), to_date('20060725','YYYYMMDD'), 1234.56, 'Toronto', 'Programmer');
INSERT
INTO Employee
(ID, First_Name, Last_Name, Start_Date, End_Date, Salary, City, Description)
VALUES
('02', 'Alison', 'Mathews', to_date('19760321','YYYYMMDD'), to_date('19860221','YYYYMMDD'), 6661.78, 'Vancouver', 'Tester');
INSERT
INTO Employee
(ID, First_Name, Last_Name, Start_Date, End_Date, Salary, City, Description)
VALUES
('03', 'James', 'Smith', to_date('19781212','YYYYMMDD'), to_date('19900315','YYYYMMDD'), 6544.78, 'Vancouver', 'Tester');
INSERT
INTO Employee
(ID, First_Name, Last_Name, Start_Date, End_Date, Salary, City, Description)
VALUES
('04', 'Celia', 'Rice', to_date('19821024','YYYYMMDD'), to_date('19990421','YYYYMMDD'), 2344.78, 'Vancouver', 'Manager');
INSERT
INTO Employee
(ID, First_Name, Last_Name, Start_Date, End_Date, Salary, City, Description)
VALUES
('05', 'Robert', 'Black', to_date('19840115','YYYYMMDD'), to_date('19980808','YYYYMMDD'), 2334.78, 'Vancouver', 'Tester');
INSERT
INTO Employee
(ID, First_Name, Last_Name, Start_Date, End_Date, Salary, City, Description)
VALUES
('06', 'Linda', 'Green', to_date('19870730','YYYYMMDD'), to_date('19960104','YYYYMMDD'), 4322.78, 'New York', 'Tester');
INSERT
INTO Employee
(ID, First_Name, Last_Name, Start_Date, End_Date, Salary, City, Description)
VALUES
('07', 'David', 'Larry', to_date('19901231','YYYYMMDD'), to_date('19980212','YYYYMMDD'), 7897.78, 'New York', 'Manager');
INSERT
INTO Employee
(ID, First_Name, Last_Name, Start_Date, End_Date, Salary, City, Description)
VALUES
('08', 'James', 'Cat', to_date('19960917','YYYYMMDD'), to_date('20020415','YYYYMMDD'), 1232.78, 'Vancouver', 'Tester');
Here for the Sequence that will manage Primary key (ID)
CREATE SEQUENCE "TEST"."EMPLOYEE_SEQ" MINVALUE 1 MAXVALUE 999999999999999999999999999 INCREMENT BY 1 START WITH 1 NOCACHE ORDER NOCYCLE ;
And here the code to execute the procedure
BEGIN
employeepackage.duplicateemployee(5);
END;
I really don't understand why it doesn't properly work for a single row when it's working to plicate all rows ? It there a limitation for cursors having less than 2 rows ?
Any help would be much appreciated ;)
Why do you need a cursor? You can do this with SQL directly:
INSERT INTO Employee(id, first_name, last_name,
start_date, end_date,
salary, city, description)
SELECT employee_seq.NEXTVAL, e.first_name, e.last_name,
e.start_date, e.end_date,
e.salary, e.city, e.description
FROM Employee e
WHERE e.id = p_EmployeeID;
Anyway, the actual problem is that your ID is a VARCHAR2(4), whereas you think it is a NUMBER. You actually do not have an employee with ID = 5, but you do have one with ID = '05'. So without changing anything, your procedure already works:
BEGIN
employeepackage.duplicateemployee('05');
END;
Of course, it would make sense to change the data type of ID.
Solution from Lukas if fine for my first and last table that will not need to call others PROCEDURE to duplicate multiple children, though for intermediate table I used :
PROCEDURE Duplicate_Company (p_IdCity IN City.IdCity%TYPE) AS
p_New_IdCompany Company.IdCompany%TYPE;
CURSOR c_DuplicateCompany IS
SELECT *
FROM Company c
WHERE c.IdCity = p_IdCity;
row_Company c_DuplicateCompany%ROWTYPE;
BEGIN
FOR c1 IN c_DuplicateCompany LOOP
p_New_IdCompany := company_seq.NEXTVAL;
INSERT INTO Company(IdCompany, IdCity, Name, CreationDate)
VALUES(p_New_IdCompany, c1.IdCity, c1.Name, c1.CreationDate);
-- Call the procedure to duplicate current employee
Duplicate_Employee(c1.IdCompany);
END LOOP;
END Duplicate_Company;
Is it a good approach ?