SQL Query for PL/SQL statement won't work - sql

I am trying to execute an SQL statement that I am planning on using with a PL/SQL cursor down the road. It will fetch an employees name and the projects they are working on if they are working on more than 1 project. For some reason, the "having count(pno)>1" stipulation will not work here. It just says "no data found"
Is there anything I'm doing wrong? I included my DB code below the query.
Query:
select pname, fname
from project, works_on, employee
where pno=pnumber and essn=ssn
group by pname, fname
having count(pno)>1;
Works_on table:
create table works_on (
Essn char(9) not null,
Pno int not null,
hours decimal(3,1),
primary key(essn, pno),
foreign key(Essn) references employee,
foreign key(pno) references project);
Project table:
create table project (
Pname varchar2(15) not null,
Pnumber int not null,
Plocation varchar2(15),
Dnum int not null,
primary key (Pnumber),
unique (Pname),
foreign key(Dnum) references department(Dnumber));
Employee table:
create table employee (
Fname varchar2(15) not null,
Minit char(1),
Lname varchar2(15) not null,
Ssn char(9),
Bdate date,
Address varchar2(30),
Sex char(1),
Salary decimal(10,2),
super_ssn char(9),
dno int,
primary key (Ssn),
foreign key (dno) references department(Dnumber));
EDIT
I managed to make this work instead:
select fname, pname
from employee, works_on, project
where essn=ssn and pno=pnumber
group by fname, pname
having count(pnumber) > 1
What made pnumber work in place of pno?

My expected output is a list of Employee First Names and Project names where the employee is working on more than 1 project.
Something like this:
SELECT e.fname, p.projects
FROM (
SELECT w.essn,
LISTAGG( p.pname, ',' ) WITHIN GROUP ( ORDER BY p.pname ) AS projects
FROM works_on w
INNER JOIN
project p
ON ( w.pno = p.pnumber )
GROUP BY w.essn
HAVING COUNT( DISTINCT w.pno ) > 1
) p
INNER JOIN
employee e
ON ( p.essn = e.ssn )
or:
SELECT e.fname,
p.pname
FROM (
SELECT w.*,
COUNT( pno ) OVER ( PARTITION BY essn ) AS num_projects
FROM works_on w
) w
INNER JOIN
employee e
ON ( e.ssn = w.essn )
INNER JOIN
project p
ON ( w.pno = p.pnumber )
WHERE w.num_projects > 1

Related

How to join multiple tables to get names of employees who earn more than their manager?

I have created two tables:
create table DEPARTMENTS
(
dept_id number,
dept varchar2(50) not null,
mgr_id number,
location varchar2(50),
constraint pk_departments primary key (dept_id)
);
create table EMPLOYEES
(
emp_id number,
fname varchar2(50) not null,
lname varchar2(50) not null,
email varchar2(50),
phone number,
hired date,
job_id varchar(20),
salary number(7,2),
commission_pct number(4,2),
dept_id number,
grade number,
constraint pk_employees primary key (emp_id),
constraint fk_employees_dept_id foreign key (dept_id)
references DEPARTMENTS (dept_id)
constraint fk_employees_grade foreign key (grade)
references SALGRADE (grade)
);
How go I get names of employees who earn more than their managers given that the mgr_id is in table departments and the employee is in employees table and they are linked by dept_id ?
I found that 'self-join' is needed. But this case is a bit difficult since referencing dept_id is needed to refer to the mgr_id then compare it to the emp_id and create join ?
For better clarity and self understanding, using ANSI joins:
SELECT * FROM
(SELECT E.SALARY AS MANAGER_SALARY, D.DEPT_ID AS DEPT_ID
FROM EMPLOYEES E
INNER JOIN
DEPARTMENTS D
ON E.DEPT_ID = D.DEPT_ID)
MGR_DET
INNER JOIN
EMPLOYEES EMP
ON MGR_DET.DEPT_ID = EMP.DEPT_ID
where EMP.SALARY > MGR_DET.MANAGER_SALARY;
You can do this with three joins. Start with the employees table, match to departments to get the manager. And then match back to employees using the manager.
This looks like:
select e.*, em.salary -- or whatever columns you want
from employees e join
departments d
on d.dept_id = e.dept_id join
employees em
on em.emp_id = d.mgr_id
where e.salary > em.salary
Answer with implicit joins:
SELECT E1.* FROM EMPLOYEES E1, DEPARTMENT D, EMPLOYEES E2
WHERE E1.DEPT_ID = D.DEPT_ID AND D.DEPT_ID = E2.DEPT_ID AND D.MGR_ID = E2.MGR_ID
AND E1.SALARY >= E2.SALARY;
Here the employee E1 is the employee whose salary is greater than that of his department manager.
To find that, we join the employee table E1 and department table D to get on dept_id and then fetch the mgr_id. Now the next step is to find the employee with the same ID as the mgr_id of the department. Hence we again join it with another employee table E2 on emp_id. Finally we compare the salary of employee e1 and the manager (employee e2).
Answer with explicit joins:
SELECT E1.* FROM EMPLOYEES E1 JOIN DEPARTMENT D ON E1.DEPT_ID = D.DEPT_ID
JOIN EMPLOYEES E2 ON (D.DEPT_ID = E2.DEPT_ID AND D.MGR_ID = E2.MGR_ID)
WHERE E1.SALARY >= E2.SALARY;

Query to join 4 tables to get employee assigned to projects outside of their dept

DEPARTMENT(ID integer PRIMARY KEY,
NAME text);
EMPLOYEE(ID integer PRIMARY KEY,
NAME text,
DEPARTMENT_ID integer,
FOREIGN KEY(DEPARTMENT_ID) REFERENCES DEPARTMENT(ID));
PROJECT(ID integer PRIMARY KEY,
NAME text,
DEPARTMENT_ID integer,
FOREIGN KEY(DEPARTMENT_ID) REFERENCES DEPARTMENT(ID));
SCHEDULE(EMPLOYEE_ID integer,
PROJECT_ID integer,
FOREIGN KEY(EMPLOYEE_ID) REFERENCES EMPLOYEE(ID),
FOREIGN KEY(PROJECT_ID) REFERENCES PROJECT(ID));
Write a query to print the names of employees with the names of the project they are assigned (or have worked) outside of their department.
this is what I have tried so far-
select distinct e.Name, p.Name
from SCHEDULE s
left join EMPLOYEE e on e.id = s.EMPLOYEE_ID
inner join DEPARTMENT d on d.id = e.department_id
left join PROJECT p on p.department_id = d.id
left join p.id = s.PROJECT_ID
This looks like home work.
You don't need the department table as long you don't need the name.
Second you onpy choose those projects that have not the employees Departmentid
And finally you can use inner join, , because you are only interested in schedule, that have a project and employee
SELECT distinct e.Name, p.Name
FROM SCHEDULE s
INNER JOIN EMPLOYEE e on e.id = s.EMPLOYEE_ID
INNER JOIN PROJECT p ON p.department_id = d.id AND p.DEPARTMENT_ID <> e.DEPARTMENT_ID

Oracle - Find Max and Least records from table

I have the following patients and appointments tables.
Patient
CREATE TABLE Patient
(
patientID number(10),
firstName varchar2(50) NOT NULL,
middleName varchar2(50),
surName varchar2(50) NOT NULL,
p_age number(10) NOT NULL,
p_gender char(1),
p_address varchar2(200),
p_contact_no number(10),
medicalHistory varchar2(500),
allergies varchar2(200),
CONSTRAINT PK_Patient PRIMARY KEY (patientID)
);
Appointment
CREATE TABLE Appointment
(
appID number(10),
patientId number(10),
staffId number(10),
appDateTime TIMESTAMP(3),
CONSTRAINT PK_Appointment PRIMARY KEY (appID),
CONSTRAINT FK_Appointment_Patient FOREIGN KEY (patientId) REFERENCES Patient(patientID) ON DELETE CASCADE,
CONSTRAINT FK_Appointment_Staff FOREIGN KEY (staffId) REFERENCES Staff(staffID) ON DELETE CASCADE
);
I want to get the patient details of patients having most and least appointments.
I have written the query in SQL server before and now I want to change it to oracle. Can anyone help me?
This is what I have so far.
SELECT p.patientId, p.firstName,
Count(a.appId) AS Count,
MAX(Count(a.appId)) OVER () AS MaxMyGroup,
MIN(Count(a.appId)) OVER () AS MinMyGroup
FROM Patient p INNER JOIN Appointment a ON p.patientID = a.patientId
GROUP BY p.patientId, p.firstName
SQL Query
WITH s
AS (SELECT p.patientId, p.firstName,
Count(a.appId) AS [Count],
MAX(Count(a.appId)) OVER () AS [MaxMyGroup],
MIN(Count(a.appId)) OVER () AS [MinMyGroup]
FROM Patient p INNER JOIN Appointment a ON p.patientID = a.patientId
GROUP BY p.patientId, p.firstName)
SELECT patientId AS ID,
firstName AS 'First Name',
V.[Count] AS 'Appointment Count',
Agg AS 'MAX/MIN'
FROM s
CROSS APPLY (VALUES ( 'Most', CASE WHEN [Count] = [MaxMyGroup] THEN [Count] END),
('Least', CASE WHEN [Count] = [MinMyGroup] THEN [Count] END))
V(Agg, [Count])
WHERE V.[Count] IS NOT NULL
You are almost there with your query - you just need to then filter on whether the number of appointments is equal to either the minimum or maximum number of appointments. (You also probably want to use LEFT OUTER JOIN rather than INNER JOIN.)
SELECT patientId,
firstName,
NumAppt,
CASE NumAppt
WHEN MinAppt
THEN 'Least'
ELSE 'Most'
END AS category
FROM (
SELECT p.patientId,
p.firstName,
Count(a.appId) AS NumAppt,
MAX(Count(a.appId)) OVER () AS MaxAppt,
MIN(Count(a.appId)) OVER () AS MinAppt
FROM Patient p
LEFT OUTER JOIN Appointment a
ON ( p.patientID = a.patientId )
GROUP BY p.patientId, p.firstName
)
WHERE NumAppt IN ( MinAppt, MaxAppt );
Check if this helps.
SELECT *
FROM PATIENT P
WHERE EXISTS
(SELECT PATIENTID,
COUNT(*)
FROM APPOINTMENT A
GROUP BY PATIENTID
HAVING P.PATIENTID = A.PATIENTID
AND (COUNT(*) >=
(SELECT MAX(COUNT(*)) FROM APPOINTMENT A2 GROUP BY A2.PATIENTID
)
OR COUNT(*) <=
(SELECT MIN(COUNT(*)) FROM APPOINTMENT A3 GROUP BY A3.PATIENTID
) )
)

Oracle SQL querying for average across several tables

Here are the relations:
CREATE TABLE employee (
name varchar2(15) not null,
ssn char(9),
sex char,
salary number(10,2),
dno number(4),
primary key (ssn),
foreign key (dno) references department(dnumber)
);
CREATE TABLE department (
dname varchar2(15) not null,
dnumber number(4),
primary key (dnumber),
unique (dname),
);
Q1: For each department whose average employee salary is more than $30,000, retrieve the department name and the number of employees working for that department.
Q2: Suppose that we want the number of male employees in each department rather than all employees (as in Q1) to calculate the departmental averages and number of employees. Can we specify this query in SQL? Why or why not.
Thanks!
Your first query might look like
SELECT d.dname,
COUNT(*) total_employees
FROM department d LEFT JOIN
employee e ON d.dnumber = e.dno
GROUP BY d.dname
HAVING AVG(e.salary) > 30000;
And the second one
SELECT d.dname,
COUNT(*) male_employees
FROM department d LEFT JOIN
employee e ON d.dnumber = e.dno
WHERE e.sex = 'M'
GROUP BY d.dname
HAVING AVG(e.salary) > 30000;
SQLFiddle
If you want to get the number of all employees and the number of male employees in one SQL,you can try this.
SELECT B.DNAME,
COUNT(*) AS TOTAL_EMPLOYEES,
COUNT(CASE WHEN A.SEX='M' THEN A.SEX END) AS MALE_EMPLOYEES,
AVG(A.SAL) AS ALL_AVG,
AVG(CASE WHEN A.SEX='M' THEN A.SAL END) AS MALE_AVG
FROM EMPLOYEE A JOIN DEPARTMENT B
ON A.DNO=B.DNUMBER
GROUP BY B.DNAME
SELECT dname, COUNT(*) AS Num_of_Employees
FROM employee, department
WHERE dno = dnumber AND EXISTS (
SELECT AVG(Salary)
FROM employee
GROUP BY dno
HAVING AVG(Salary)>30000)
GROUP BY dname;

How to use count statement to get information from multiple tables in sql server?

How can I make a query for getting the number of employees on each department using a count function with the fields Dept_id, Dept_name, Number of employees using the data structure below?
create database Emp_det
create table Dept
(
Dept_ID int not null primary key,
Dept_name varchar(255)
)
create table Emp_table
(
Emp_no int not null primary key,
Emp_name varchar(255) not null,
Dept_ID int foreign key references Dept(Dept_ID),
Emp_status varchar(255)
)
create table Salary_table
(
Emp_no int not null foreign key references Emp_table(Emp_no),
From_date datetime,
End_date datetime,
Basic int,
HRA int,
Others int,
Total int,
Emp_status varchar(255),
constraint pk_emp_det primary key (emp_no,From_date)
);
insert into Dept
values(10, 'I.T'), (11, 'H.R'),(12, 'Procurement'),(13, 'QS');
insert into Emp_table
values(1111,'Manivannan','10','A'),
(1222,'Faizal','10','A'),
(4122,'Marzook','10','A'),
(1223,'Venu','11','A');
insert into Salary_table
values(1111,'01/09/2012','1/10/2012',10000,10000,2000,22000,'A'),
(1222,'01/09/2012','1/10/2012',5000,5000,1000,11000,'A'),
(4122,'01/09/2012','1/10/2012',1000,1000,5000,2500,'A'),
(1223,'01/09/2012','1/10/2012',10000,10000,2000,22000,'A')
SELECT D.Dept_ID, D.Dept_Name, COUNT(*) NumberOfEmployees
FROM Dept D
LEFT JOIN dbo.Emp_Table ET
ON D.Dept_ID = ET.Dept_ID
GROUP BY D.Dept_ID, D.Dept_Name
This will list all departments, even if there are no employees. You can change the LEFT JOIN with an INNER JOIN if you only want departments that have employees.
select
Dept_id, Dept_name,count(distinct Emp_no) as Number_of_employees
from
Dept d inner join
Emp_table e on
d.Dept_ID = e.Dept_ID
group by
Dept_id, Dept_name
sorry, did it off the top of my head. forgot the group by.