Oracle HR Schema. Selecting of max salary from Employees - oracle

I need to select city, max salary in the city and employee name whose salary is max from Oracle HR Schema.
I try to do below code, but city name repeats:
select l.city, e.last_name, e.salary from locations l
inner join departments d on l.location_id = d.location_id
inner join employees e on d.department_id = e.department_id
and e.salary = (select max(salary) from employees where department_id = d.department_id)
group by l.city, e.last_name, e.salary
order by e.salary;
What is wrong with my code?
I've attached result, which I need.Correct SQL result

You can use the DENSE_RANK analytic function (which will return all employees with the maximum salary per city):
SELECT city,
last_name,
salary
FROM (
SELECT l.city,
e.last_name,
e.salary,
DENSE_RANK() OVER (
PARTITION BY l.location_id
ORDER BY e.salary DESC
) AS rnk
FROM locations l
INNER JOIN departments d
ON l.location_id = d.location_id
INNER JOIN employees e
ON d.department_id = e.department_id
)
WHERE rnk = 1;
or aggregation with KEEP (which will only return one employee with the maximum salary and the maximum last name per location):
SELECT MAX(l.city) AS city,
MAX(e.last_name) KEEP ( DENSE_RANK LAST ORDER BY e.salary ) AS last_name,
MAX(e.salary) AS salary
FROM locations l
INNER JOIN departments d
ON l.location_id = d.location_id
INNER JOIN employees e
ON d.department_id = e.department_id
GROUP BY
l.location_id
What is wrong with my code?
You are correlating on department_id = d.department_id and not on the location (or city name; however, don't aggregate on the city name as there could be two different locations with the same name).

Related

Report Job difference in HR schema

I'm new to Oracle and try to practice with HR schema. For example I want to report of those whose job is different from the previous job.
Employee name in employees table and job history in job_history table.
I think the following query will help. (I am considering that current JOB_ID is present in the EMPLOYEES table and you want to compare it with the latest previous JOB_ID from JOB_HISTORY table for the employee)
SELECT E.*, JH.LATEST_PREV_JOB_ID
FROM EMPLOYEES E
JOIN (SELECT FIRST_VALUE(JH.JOB_ID) OVER (PARTITION BY JH.EMPLOYEE_ID
ORDER BY JH.START_DATE DESC NULLS LAST) AS LATEST_PREV_JOB_ID,
JH.EMPLOYEE_ID
FROM JOB_HISTORY JH) JH
ON E.EMPLOYEE_ID = JH.EMPLOYEE_ID
WHERE E.JOB_ID <> JH.LATEST_PREV_JOB_ID
----- Update
You want the query without partition by clause (i.e. without WINDOWS function), We can use the NOT EXISTS as follows:
SELECT E.*, JH.LATEST_PREV_JOB_ID
FROM EMPLOYEES E
JOIN (SELECT JH.JOB_ID AS LATEST_PREV_JOB_ID,
JH.EMPLOYEE_ID
FROM JOB_HISTORY JH
WHERE NOT EXISTS (SELECT 1 FROM JOB_HISTORY JHIN
WHERE JHIN.EMPLOYEE_ID = JH.EMPLOYEE_ID
AND JHIN.START_DATE > JH.START_DATE)) JH
ON E.EMPLOYEE_ID = JH.EMPLOYEE_ID
WHERE E.JOB_ID <> JH.LATEST_PREV_JOB_ID
If I understood right your question, then the answer maybe something like this:
select
e.first_name,
e.last_name,
e.job_id as prev_job,
jh.job_id as last_lob
from
employees e,
job_history jh,
(select
employee_id,
max(end_date) as max_end_date
from
job_history
group by
employee_id
) t
where
(jh.employee_id = e.employee_id) and
(jh.job_id <> e.job_id) and
(jh.end_date = t.max_end_date) and
(t.employee_id = jh.employee_id)

Missing Function Error in Count

I have 2 data tables, Employees and Departments. I want to show the department numbers, department names, the number of employees in each department, the average salary of each department, the employee names, their salaries, and jobs IDs of the employees. Here's what I wrote for my code:
select d.department_id, d.department_name, e.count(*) Employees,
avg(e.salary) Avg_salary, e.last_name, e.salary, e.job_id
from departments d join employees e
on d.department_id = e.department_id
group by d.department_id, d.department_name, e.last_name, e.salary, e.job_id
order by d.department_id;
However, the error "Missing Function" appears when I run the code. How do I fix it?
try this:
SELECT d.department_id,
d.department_name,
Count(*) Employees,
Avg(e.salary) Avg_salary,
e.last_name,
e.salary,
e.job_id
FROM departments d
JOIN employees e
ON d.department_id = e.department_id
GROUP BY d.department_id,
d.department_name,
e.last_name,
e.salary,
e.job_id
ORDER BY d.department_id;
e.count(*) should just be count(*).
EDIT:
Is this what you need?
COUNT(*) OVER (PARTITION BY e.department_id) DeptCt

ORA-01427 while UPDATE self-join ORACLE

two tables, each has employee_id and manager_id which links to employee_id Tables have different employee_id.
UPDATE employee u
SET u.manager_id = (SELECT m.id
FROM employee e
JOIN old_db.employees oe ON e.last_name = oe.last_name
JOIN old_db.employees om ON oe.manager_id = om.employee_id
INNER JOIN employee m ON m.last_name = om.last_name
WHERE e.id = u.id)
WHERE manager_id IS NULL;
gives
ERROR at line 2:
ORA-01427: single-row subquery returns more than one row
I've tried rownum=1 but this makes all manager_id same.
Select query with one given employee_id returns one value
SQL> SELECT m.id
2 FROM employee e
3 JOIN old_db.employees oe ON e.last_name = oe.last_name
4 JOIN old_db.employees om ON oe.manager_id = om.employee_id
5 INNER JOIN employee m ON m.last_name = om.last_name
6 WHERE e.id = 1805;
ID
----------
1804
Well the root cause of problem is in table employees or in table employee last_name are not unique. And when you join table on last name you get more then one row. You may check it with next query:
SELECT e.id, count(*), count(distinct m.id)
FROM employee e
JOIN old_db.employees oe ON e.last_name = oe.last_name
JOIN old_db.employees om ON oe.manager_id = om.employee_id
INNER JOIN employee m ON m.last_name = om.last_name
GROUP BY e.id;
May be exists more suitable column to join it for example employee_id if so you query may be rewritten to:
UPDATE employee u
SET u.manager_id = (SELECT oe.manager_id
FROM old_db.employees oe
WHERE oe.employee_id = u.id)
WHERE manager_id IS NULL;

How to use group function in self join?

I want to convert Subquery into Join, following is the subquery
SELECT employee_id, last_name, salary
FROM employees
WHERE salary > (SELECT AVG(salary)
FROM employees);
I wrote following join, but I am getting "ORA-00934: group function is not allowed here" error
SELECT e.employee_id,
e.last_name,
e.salary
FROM employees e
INNER JOIN employees average
ON(e.salary>AVG(average.salary));
You can use this like
SELECT e.employee_id,
e.last_name,
e.salary
FROM employees e
INNER JOIN (select AVG(salary) salary from employees ) average
ON (e.salary > average.salary)
Group by e.employee_id;

Oracle PL/SQL group by issue

I want to do this excercise for Oracle 10g Express
"Write an SQL query to retrieve the department name, firstname,
lastname, salary of the employee who earns the maximum salary for that
department."
I tried this code but it is not working for me.
There are two tables named employees, departments
ERROR is: ORA-00979: not a GROUP BY expression
SELECT first_name, last_name, departments.department_name, salary
FROM employees, departments
where employees.department_id = departments.department_id
group by salary
Output must be like that.
You will want to use an aggregate function to get the result. In this case you will use max() to get the highest salary for each department.
There are several ways that this can be written.
You can use a subquery in a join:
select d.department_name,
e.first_name,
e.last_name,
e.salary
from employees e
inner join
(
select max(salary) MaxSalary, department_id
from employees
group by department_id
) e1
on e.department_id = e1.department_id
and e.salary = e1.maxsalary
inner join departments d
on e.department_id = d.department_id;
Since you are using Oracle, you can use windowing functions to get the result:
select department_name, first_name, last_name, salary
from
(
select d.department_name,
e.first_name,
e.last_name,
e.salary,
row_number() over(partition by d.department_id order by e.salary desc) rn
from employees e
inner join departments d
on e.department_id = d.department_id
) d
where rn = 1
Or you can even use a WHERE clause to filter the data:
select d.department_name,
e.first_name,
e.last_name,
e.salary
from employees e
inner join departments d
on e.department_id = d.department_id
where salary in (select max(salary)
from employees e1
where e.department_id = e1.department_id
group by department_id)
I understand the question that you are asking is in reference to using GROUP BY but I would take a closer look at the question.
"Write an SQL query to retrieve the department name, firstname, lastname, salary of the employee who earns the maximum salary for that department."
I think you would also benefit from looking at the Maximum function since you are looking for the employee with the Maximum salary.
You need an aggregate function to be able to use GROUP BY. Aggregate functions are functions that perform a task over several records. These are functions like SUM, AVG, and COUNT. When you have an aggregate function, you group by whatever is not in the function.
In your example, MAX would be your aggregate function and you can use your GROUP BY successfully.
Here is a link for Aggregate Functions

Resources