Analytic functions and plain SQL equivalent - sql

I'm using Oracle and SQL Developer. I have downloaded HR schema and need to do some queries with it. Now I'm working with table Employees. As an user I need to see the list of employees with lowest salary in each department. I need to provide different solutions by means of plain SQL and one of analytic functions. About analytic functions, I have used RANK():
SELECT *
FROM
(SELECT
employee_id,
first_name,
department_id,
salary,
RANK() OVER (PARTITION BY department_id
ORDER BY salary) result
FROM
employees)
WHERE
result = 1
AND department_id IS NOT NULL;
The result seems correct:
but when I try to use plain SQL I actually get all employees with their salaries.
Here is my attempt with GROUP BY:
SELECT
department_id, MIN(salary) AS "Lowest salary"
FROM
employees
GROUP BY
department_id;
This code seems good, but I need to also get columns first_name and employee_id.
I tried to do something like this:
SELECT
employee_id,
first_name,
department_id,
MIN(salary) result
FROM
employees
GROUP BY
employee_id,
first_name,
department_id;
and this:
SELECT
employee_id,
first_name,
salary,
departments.department_id
FROM
employees
LEFT OUTER JOIN
departments ON (employees.department_id = departments.department_id)
WHERE
employees.salary = (SELECT MIN(salary)
FROM departments
WHERE department_id = employees.department_id)
These seem wrong. How can I change or modify my queries to get the same result as when I'm using RANK() by means of plain SQL (two solutions at least)?

One of the options could be like here (with old EMP table)...
SELECT EMPNO, ENAME, DEPTNO, SAL
FROM EMP e
WHERE SAL = (Select MIN_SAL From (SELECT DEPTNO, Min(SAL) "MIN_SAL"
FROM EMP
GROUP BY DEPTNO)
Where DEPTNO = e.DEPTNO)
ORDER BY DEPTNO, SAL;
Second option could be...
SELECT EMPNO, ENAME, DEPTNO, SAL
FROM (SELECT e.EMPNO, e.ENAME, e.DEPTNO, e. SAL, (Select Min(SAL) "MIN_SAL" From EMP Where DEPTNO = e.DEPTNO) "MIN_SAL" From EMP e)
WHERE SAL = MIN_SAL
ORDER BY DEPTNO, SAL;
Regards...

You can use a subquery to find the lowest salary per employee and use the main query to only show the information of those employees that are selected by this subquery:
SELECT
employee_id,
first_name,
department_id,
salary
FROM employees e1
WHERE salary =
(SELECT MIN(e2.salary)
FROM employees e2
WHERE e1.employee_id = e2.employee_id);
This will produce exactly the same outcome as your query with RANK.
I think it would make sense to apply some sorting which is missing in your query. I don't know how you want to sort, but here an example to sort by the employee's name:
SELECT
employee_id,
first_name,
department_id,
salary
FROM employees e1
WHERE salary =
(SELECT MIN(e2.salary)
FROM employees e2
WHERE e1.employee_id = e2.employee_id)
ORDER BY first_name;
Since you asked for at least two solutions, let's have a look on another option:
SELECT
e1.employee_id,
e1.first_name,
e1.department_id,
e1.salary
FROM employees e1
JOIN (
SELECT employee_id, MIN(salary) salary
FROM employees
GROUP BY employee_id ) e2
ON e1.employee_id = e2.employee_id AND e1.salary = e2.salary
ORDER BY first_name;
As you can see, this differs since the sub query will apply a GROUP BY clause and it can be successfully executed as own query which is not possible for the sub query used in the previous query.
The JOIN to the main query will then make sure to get again the desired result.

Here are some options to get the employees with the minimum salary in their department:
With MIN (salary) OVER (...)
select employee_id, first_name, department_id, salary
from
(
select e.*, min(salary) over (partition by department_id) as min_sal
from employees e
)
where sal = min_sal;
With RANK and FETCH FIRST
select *
from employees
order by rank() over (partition by department_id order by salary)
fetch first row with ties;
With IN
select *
from employees
where (department_id, salary) in
(
select department_id, min(salary)
from employees
group by department_id
);
With NOT EXISTS
select *
from employees e
where not exists
(
select null
from employees other
where other.department_id = e.department_id
and other.salary < e.salary
);

If you will only ever have one person with the minimum salary per department then you can use KEEP:
SELECT department_id,
MIN(employee_id) KEEP (DENSE_RANK FIRST ORDER BY salary) AS employee_id,
MIN(first_name) KEEP (DENSE_RANK FIRST ORDER BY salary, employee_id) AS first_name,
MIN(salary) AS min_salary
FROM employees
GROUP BY department_id
Which, for the sample data:
CREATE TABLE employees (employee_id, department_id, first_name, salary) AS
SELECT 1, 1, 'Alice', 1000 FROM DUAL UNION ALL
SELECT 2, 1, 'Betty', 2000 FROM DUAL UNION ALL
SELECT 3, 2, 'Carol', 3000 FROM DUAL UNION ALL
SELECT 4, 2, 'Debra', 3000 FROM DUAL UNION ALL
SELECT 5, 2, 'Emily', 4000 FROM DUAL;
Outputs:
DEPARTMENT_ID
EMPLOYEE_ID
FIRST_NAME
MIN_SALARY
1
1
Alice
1000
2
3
Carol
3000
Note: this will not match Debra, even though she also has the lowest salary in department 2, as it will only find a single employee with the minimum salary and the minimum employee id.
If you can have multiple employees with the same minimum-per-department then you can use a correlated sub-query:
SELECT department_id,
employee_id,
first_name,
salary
FROM employees e
WHERE EXISTS(
SELECT 1
FROM employees x
WHERE e.department_id = x.department_id
HAVING MIN(x.salary) = e.salary
);
Which, for the sample data, outputs:
DEPARTMENT_ID
EMPLOYEE_ID
FIRST_NAME
SALARY
1
1
Alice
1000
2
3
Carol
3000
2
4
Debra
3000
Which does return Debra.
fiddle

Related

How to handle ties in SQL

SELECT
employee_id,
department_id,
first_name,
last_name,
hire_date,
job_id
FROM employees e
WHERE hire_date IN
(
SELECT max(hire_date)
FROM employees
WHERE e.department_id = department_id
)
ORDER BY hire_date ASC
Result of the query
So this is my query and the result of it. There are two tied results for SA_REP department and I should have only one result - for instance the one with the lower employee_id. I've googled the problem but can't seem to find any related results...
Thanks for any help!
You can use a not exists query which gives you more flexibility:
SELECT *
FROM employees e
WHERE NOT EXISTS ( -- no x exists that...
SELECT *
FROM employees x
WHERE x.department_id = e.department_id -- belongs to same department
AND (
x.hire_date > e.hire_date OR -- but hired later than e
x.hire_date = e.hire_date AND x.employee_id < e.employee_id -- hired on same date but has lesser employee id than e
)
)
You may use the RANK analytic function here:
WITH cte AS (
SELECT e.*, RANK() OVER (PARTITION BY department_id
ORDER BY hire_date DESC, employee_id) rnk
FROM employees e
)
SELECT employee_id, department_id, first_name, last_name, hire_date, job_id
FROM cte
WHERE rnk = 1;

SQL COUNT modify

From the SQL Oracle HR scheme I used the folowing:
SELECT DEPARTMENT_ID, ROUND(AVG(SALARY),2)
FROM EMPLOYEES
WHERE DEPARTMENT_ID IS NOT NULL
GROUP BY DEPARTMENT_ID
ORDER BY DEPARTMENT_ID
To get:
DEPARTMENT_ID ROUND(AVG(SALARY),2)
10 4400
20 9500
30 4150
40 6500
50 3475,56
60 5760
...
How do I change it so: it only count the departments that have the max avg salary (in my case 1) and show also the max avg salary?
Thank you for your time!
If I understood you, this is one possible way:
SELECT DEPARTMENT_ID, ROUND(AVG(SALARY),2) AS AVG_SALARY
FROM EMPLOYEES
WHERE DEPARTMENT_ID IS NOT NULL
AND ROUND(AVG(SALARY),2) = (
SELECT MAX(T.AVG_SALARY)
FROM (
SELECT DEPARTMENT_ID, ROUND(AVG(SALARY),2) AS AVG_SALARY
FROM EMPLOYEES
WHERE DEPARTMENT_ID IS NOT NULL
GROUP BY DEPARTMENT_ID) AS T)
GROUP BY DEPARTMENT_ID
ORDER BY DEPARTMENT_ID
This will show you ALL THE DEPARTMENTS that have max avg salary. If you want only the count:
SELECT COUNT(A.*), AVG(A.AVG_SALARY)
FROM (
SELECT DEPARTMENT_ID, ROUND(AVG(SALARY),2) AS AVG_SALARY
FROM EMPLOYEES
WHERE DEPARTMENT_ID IS NOT NULL
GROUP BY DEPARTMENT_ID) A
WHERE A.AVG_SALARY = (
SELECT MAX(T.AVG_SALARY) AS MAX_AVG_SALARY
FROM (
SELECT DEPARTMENT_ID, ROUND(AVG(SALARY),2) AS AVG_SALARY
FROM EMPLOYEES
WHERE DEPARTMENT_ID IS NOT NULL
GROUP BY DEPARTMENT_ID) AS T)
Another way that should work, using joins:
SELECT t1.DEPARTMENT_ID, ROUND(AVG(t1.SALARY),2) AS AVG_SALARY
FROM EMPLOYEES t1
LEFT JOIN (SELECT MAX(AVG_SALARY) AS MAX_AVG_SALARY FROM EMPLOYEES) t2
ON AVG_SALARY=t2.MAX_AVG_SALARY
WHERE t1.DEPARTMENT_ID IS NOT NULL
AND AVG_SALARY=t2.MAX_AVG_SALARY
ORDER BY t1.DEPARTMENT_ID ASC;
I tested the idea on a sample table of mine using oracle, and on a w3schools sql testing page with this code:
SELECT Customers.CustomerName, Orders.maxid
FROM Customers
LEFT JOIN (select max(Orders.CustomerID) as maxid from Orders) orders
ON Customers.CustomerID=Orders.maxid
where customers.customername is not null and customers.customerid=orders.maxid
ORDER BY orders.maxid desc;
It should grab only the departments that match their average salary with the max average salary that was selected in the join statement.
If you are only looking for the count of the departments, and not a list of the department names, then this slight modification should work for you:
SELECT COUNT(t1.DEPARTMENT_ID) as Num_Of_Depts, ROUND(AVG(t1.SALARY),2) AS AVG_SALARY
FROM EMPLOYEES t1
LEFT JOIN (SELECT MAX(AVG_SALARY) AS MAX_AVG_SALARY FROM EMPLOYEES) t2
ON AVG_SALARY=t2.MAX_AVG_SALARY
WHERE t1.DEPARTMENT_ID IS NOT NULL
AND AVG_SALARY=t2.MAX_AVG_SALARY
GROUP BY AVG_SALARY;

sql query group by statement suggestion

I am learning database query and want to find out the following SQL query from the HR Schema in Oracle database.
Find number of employees in each salary group. Salary groups are considered as follows.
Group 1: 0k to <5K, 5k to <10k, 10k to <15k, and so on.
what will be the possible query code ??
Tables are as follows: There are seven tables in total
REGIONS: REGION_ID, REGION_NAME
COUNTRIES: COUNTRY_ID, COUNTRY_NAME, REGION_ID
LOCATIONS: LOCATION_ID, STREET_ADDRESS, POSTAL_CODE, CITY, STATE_PROVINCE, COUNTRY_ID
DEPARTMENTS: DEPARTMENT_ID, DEPARTMENT_NAME, MANAGER_ID, LOCATION_ID
EMPLOYEES: EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB_ID, SALARY, COMMISSION_PCT, MANAGER_ID, DEPARTMENT_ID
JOB_HISTORY: EMPLOYEE_ID, START_DATE, END_DATE, JOB_ID, DEPARTMENT_ID
JOBS: JOB_ID, JOB_TITLE, MIN_SALARY, MAX_SALARY
I first use a common table expression (CTE) to calculate the different groups using a case statement...
WITH CTE AS (
SELECT Emp_ID,
case when salary >= 0 salary < 5000 then "<5K"
when salary >= 5000 and salary < 10000 then "<10k"
when salary >= 10000 and salary < 15000 then "<15k"
else "UDF" end as SalaryGroup
FROM Employees)
SELECT count(Emp_ID) as Cnt, SalaryGroup
FROM CTE
GROUP BY SalaryGroup;
I then select from that table to give you the counts by the salary group calculated.
select trunc(salary/5000,00) e,
count(*)
from EMPLOYEES
group by trunc(SALARY/5000,0)
ORDER by e asc

ORA-00934: Group function not allowed here || Selecting MIN(Salary) of highest paid dept

O community, do you know how I could select the department_ID, and lowest salary of the department with the highest average salary? Or how to eliminate the'ORA-00934: group function not allowed here' issue? Would I need to use two subqueries?
So far, this is what I've come up with, trying to get the department_ID of the highest paid department:
SELECT department_ID, MIN(salary
FROM employees
WHERE department_ID = (SELECT department_ID
FROM employees WHERE salary = MAX(salary));
Thank you, your assistance is greatly appreciated.
I can't test this, but it should work:
;WITH DepartmentsSalary AS
(
SELECT department_ID, AVG(Salary) AvgSalary, MIN(Salary) MinSalary
FROM employees
GROUP BY department_ID
)
SELECT department_ID, MinSalary
FROM ( SELECT department_ID, AvgSalary, MAX(AvgSalary) OVER() MaxSalary, MinSalary
FROM DepartmentsSalary) D
WHERE MaxSalary = AvgSalary
You can use join (then you have just one sub query)
select e1.department_ID, min(e1.salary)
from employees e1
join (
select avg_query.department_ID, max(avg_query.avg_value)
from (
select department_ID, avg(salary) as avg_value
from employees
group by department_ID
) avg_query
) e2 on e2.department_ID = e1.department_ID
;
First sub-query returned average salary for all departments
Next sub-query based on first sub-query returned highest average
salary and related department_ID
Main query returned min salary for department_ID with highest average
salary

Highest Salary in each department

I have a table EmpDetails:
DeptID EmpName Salary
Engg Sam 1000
Engg Smith 2000
HR Denis 1500
HR Danny 3000
IT David 2000
IT John 3000
I need to make a query that find the highest salary for each department.
SELECT DeptID, MAX(Salary) FROM EmpDetails GROUP BY DeptID
The above query is the accepted answer but it will not work for the following scenario. Let's say we have to find the employees with the highest salary in each department for the below table.
DeptID
EmpName
Salary
Engg
Sam
1000
Engg
Smith
2000
Engg
Tom
2000
HR
Denis
1500
HR
Danny
3000
IT
David
2000
IT
John
3000
Notice that Smith and Tom belong to the Engg department and both have the same salary, which is the highest in the Engg department. Hence the query "SELECT DeptID, MAX(Salary) FROM EmpDetails GROUP BY DeptID" will not work since MAX() returns a single value. The below query will work.
SELECT DeptID, EmpName, Salary FROM EmpDetails
WHERE (DeptID,Salary) IN (SELECT DeptID, MAX(Salary) FROM EmpDetails GROUP BY DeptID)
Output will be
DeptID
EmpName
Salary
Engg
Smith
2000
Engg
Tom
2000
HR
Danny
3000
IT
John
3000
Assuming SQL Server 2005+
WITH cteRowNum AS (
SELECT DeptID, EmpName, Salary,
DENSE_RANK() OVER(PARTITION BY DeptID ORDER BY Salary DESC) AS RowNum
FROM EmpDetails
)
SELECT DeptID, EmpName, Salary
FROM cteRowNum
WHERE RowNum = 1;
If you want to show other parameters too along with DeptId and Salary like EmpName, EmpId
SELECT
EmpID
, Name,
, Salary
, DeptId
FROM Employee
where
(DeptId,Salary)
in
(select DeptId, max(salary) from Employee group by DeptId)
SELECT empName,empDept,EmpSalary
FROM Employee
WHERE empSalary IN
(SELECT max(empSalary) AS salary
From Employee
GROUP BY EmpDept)
Select empname,empid,Sal,DeptName from
(Select e.empname,e.empid,Max(S.Salary) Sal,D.DeptName, ROW_NUMBER() Over(partition by D.DeptName order by s.salary desc) Rownum
from emp e inner join Sal S
on e.empid=s.empid
inner join Dept d on e.Deptid=d.Deptid
group by e.empname,e.empid,D.DeptName,s.Salary
) x where Rownum = 1
This will work if the department, salary and employee name are in the same table.
select ed.emp_name, ed.salary, ed.dept from
(select max(salary) maxSal, dept from emp_dept group by dept) maxsaldept
inner join emp_dept ed
on ed.dept = maxsaldept.dept and ed.salary = maxsaldept.maxSal
Is there any better solution than this?
ermn, something like:
select
d.DeptID,
max(e.Salary)
from
department d
inner join employees e on d.DeptID = e.DeptID
group by
d.DeptID
WITH cteRowNum AS (
SELECT DeptID, EmpName, Salary,
ROW_NUMBER() OVER(PARTITION BY DeptID ORDER BY Salary DESC) AS RowNum
FROM EmpDetails
)
SELECT DeptID, EmpName, Salary,Rownum
FROM cteRowNum
WHERE RowNum in(1,2);
SELECT Employee_ID
, First_name
, last_name
, department_id
, Salary
FROM (SELECT Employee_ID
, First_name
, last_name
, department_id
, Salary
, MAX(salary) OVER (PARTITION BY department_id) dept_max_sal
FROM EMPLOYEES) AS Emp
WHERE salary = dept_max_sal;
Use following command;
SELECT A.*
FROM #EmpDetails A
INNER JOIN ( SELECT DeptID ,
MAX(salary) AS salary
FROM #EmpDetails
GROUP BY DeptID
) B ON A.DeptID = B.DeptID
AND A.salary = B.salary
ORDER BY A.DeptID
SELECT DeptID, MAX(Salary)
FROM EmpDetails
GROUP BY DeptID
This query will work fine, but the moment if you want to fetch some others details related to the employee having the highest salary will contradict.
You can use :
SELECT DepatID, a , b, c
FROM EmpDetails
WHERE Salary IN (
SELECT max(Salary)
FROM EmpDetails
GROUP BY DeptID
);
if you will use the previous query it will only reflects the records of the min val except the salary as you have used the max function.
SELECT
DeptID,
Salary
FROM
EmpDetails
GROUP BY
DeptID
ORDER BY
Salary desc
***
> /*highest salary by each dept*/
***
select d.Dept_Name,max(e.salary)
from emp_details as e join Dept_Details as d
on e.d_id=d.Dept_Id
group by d.Dept_Name
select distinct e.d_id,d.Dept_Name
from emp_details as e join Dept_Details as d
on e.d_id=d.Dept_Id
select e.salary,d.Dept_Name,d.Dept_Id
from emp_details as e join Dept_Details as d
on e.d_id=d.Dept_Id
/////simplest query for max salary dept_wise////
Use the below quesry:
select employee_name,salary,department_id from emp where salary in(select max(salary) from emp group by department_id);
select empno
from EMP e
where salary=(select max(sal)
from EMP w
where groupby w.deptno having e.deptno=w.deptno)
I hope it will work...
Use correlated subquery:
SELECT DeptID, EmpName, Salary
FROM EmpDetails a
WHERE Salary = (SELECT MAX(Salary)
FROM EmpDetails b
WHERE a.DeptID = b.DeptID)
This is the best possible solution for ORACLE:
Select * from (select customerid, city, freight,
row_number() over (partition by customerid order by freight desc) Row_Number from
(select orders.orderId, customers.CUSTOMERID, customers.city, orders.FREIGHT from orders inner join customers on orders.customerid = customers.customerid where customers.country='Germany' order by customers.customerid, orders.freight desc)
order by customerid, freight desc) where Row_Number<=2;
Notice here I have used partition by clause for marking row number, this is majorly because we need to partition the records grouping them according to customer id. I have used two inner queries here. The inner most query is to give a view which is sorted according to customer ID and decreasing order of cost. Now from that we need to obtain always top two records so firstly we need to name them and then we need to filter them according to rownum. Second level query is to mark rownum according to customer ID. And final query will filter the result according to rownum. For every partition.
select deptid, empname, salary from
(Select deptid, empname,salary,
rank() Over(Partition by deptid order by salary desc)as rank from
EmpDetails) emp
where emp.rank = 1
First ranks each employee by salary in descending order having highest
rank 1 and then selects only deptid, empname, salary. You can do this for
all Nth member of the group.
SELECT empname
FROM empdetails
WHERE salary IN(SELECT deptid max(salary) AS salary
FROM empdetails
group by deptid)
select a.*
from EmpDetails a
inner join
(
select DeptID,max(Salary) as Salary
from EmpDetails group by DeptID
)b
on a.DeptID = b.DeptID and a.Salary = b.Salary
Here is a way to get maximum values and names on any version of SQL.
Test Data:
CREATE TABLE EmpDetails(DeptID VARCHAR(10), EmpName VARCHAR(10), Salary DECIMAL(8,2))
INSERT INTO EmpDetails VALUES('Engg','Sam',1000)
INSERT INTO EmpDetails VALUES('Engg','Smith',2000)
INSERT INTO EmpDetails VALUES('HR','Denis',1500)
INSERT INTO EmpDetails VALUES('HR','Danny',3000)
INSERT INTO EmpDetails VALUES('IT','David',2000)
INSERT INTO EmpDetails VALUES('IT','John',3000)
Example:
SELECT ed.DeptID
,ed.EmpName
,ed.Salary
FROM (SELECT DeptID, MAX(Salary) MaxSal
FROM EmpDetails
GROUP BY DeptID)AS empmaxsal
INNER JOIN EmpDetails ed
ON empmaxsal.DeptID = ed.DeptID
AND empmaxsal.MaxSal = ed.Salary
Not the most elegant, but it works.
SELECT D.DeptID, E.EmpName, E.Salary
FROM Employee E
INNER JOIN Department D ON D.DeptId = E.DeptId
WHERE E.Salary IN (SELECT MAX(Salary) FROM Employee);
select * from (
select a.* from EmpDetails a
right join (select DeptID,max(salary) as Salary from EmpDetails group by DeptID) b
on b.DeptID=a.DeptID and b.salary=a.salary ) as c group by c.DeptID;
The below query will display employee name with their respective department name in which that particular employee name is having highest salary.
with T as
(select empname, employee.deptno, salary
from employee
where salary in (select max(salary)
from employee
group by deptno))
select empname, deptname, salary
from T, department
where T.deptno=department.deptno;
I executed the above query successfully on Oracle database.
If you just want to get the highest salary from that table, by department:
SELECT MAX(Salary) FROM TableName GROUP BY DeptID
IF you want Department and highest salary, use
SELECT DeptID, MAX(Salary) FROM EmpDetails GROUP BY DeptID
if you want more columns in employee and department, use
select Department.Name , emp.Name, emp.Salary from Employee emp
inner join (select DeptID, max(salary) [salary] from employee group by DeptID) b
on emp.DeptID = b.DeptID and b.salary = emp.Salary
inner join Department on emp.DeptID = Department.id
order by Department.Name
if use salary in (select max(salary...)) like this, one person have same salary in another department then it will fail.
The below listed query will list highest salary in each department.
select deptname, max(salary) from department, employee where
department.deptno=employee.deptno group by deptname;
I executed this query successfully on Oracle database.
with ctesal as (
select DepartmentId , Name , Salary, ROW_Number() OVER (partition by DepartmentId
order by Salary desc) as RowNum
from dbo.Employee
)
select DepartmentId , Name , Salary , RowNum from ctesal where RowNum =2;
This is applicable to SQL server.
ROW_Number is a inbuilt function in SQL server .It gives count starting from 1 based on partition by and order by clause. At the end, We can write where condition based on our requirements.
I have like 2 approaches using one with Rank and the other with ROW_NUMBER
This is my sample data
Age Name Gender Salary
----------- -------------------------------------------------- ---------- -----------
1 Mark Male 5000
2 John Male 4500
3 Pavan Male 5000
4 Pam Female 5500
5 Sara Female 4000
6 Aradhya Female 3500
7 Tom Male 5500
8 Mary Female 5000
9 Ben Male 6500
10 Jodi Female 7000
11 Tom Male 5500
12 Ron Male 5000
13 Ramani Female 7000
So here is my first query to find max salary and the person with that max salary for each Gender
with CTE as(
select RANK() over(partition by Gender Order by Salary desc) as [Rank],* from employees)
select * from CTE where [Rank]=1
Rank Age Name Gender Salary
-------------------- ----------- -------------------------------------------------- ---------- -----------
1 10 Jodi Female 7000
1 13 Ramani Female 7000
1 9 Ben Male 6500
So in this case, we can see there is a tie between these 2 female employees "Jodi" and "Ramani". In that case, As a tie-breaker I want to make use of Age as a deciding factor and person with more age is supposed to be displayed
with CTE as(
select RANK() over(partition by Gender Order by Salary desc,age desc) as [Rank],* from employees)
select * from CTE where [Rank]=1
Rank Age Name Gender Salary
-------------------- ----------- -------------------------------------------------- ---------- -----------
1 13 Ramani Female 7000
1 9 Ben Male 6500
Usually, in this case for finding the highest salary, it doesn't make much difference even if
Rank, Dense_Rank, or Row_Number() are used. But they have some impact in other cases.
Thank you #JoeStefanelli for his answer (https://stackoverflow.com/a/8477083/4691279). He provided SQL Server 2005+ version and I used the same to create the Oracle version:
WITH cteRowNum(dep_id, emp_id, Salary, RowNums) AS (
SELECT dep_id, emp_id, Salary,
DENSE_RANK() OVER(PARTITION BY dep_id ORDER BY Salary DESC) AS RowNums
FROM employee
)
SELECT cteRowNum.dep_id, cteRowNum.emp_id, cteRowNum.Salary
FROM cteRowNum
WHERE cteRowNum.RowNums = 1;
You can test this using livesql.oracle.com, below are my DDLs and DMLs you can use:
create table employee (
emp_id varchar2(50) NOT NULL,
dep_id varchar2(50) NOT NULL,
salary number not null
);
create table department (
dep_id varchar2(50) NOT NULL,
dep_name varchar2(50) NOT NULL
);
insert into employee values (100, 5000, 1000000);
insert into employee values (200, 5000, 2000000);
insert into employee values (300, 5000, 3000000);
insert into employee values (400, 6000, 1500000);
insert into employee values (500, 6000, 1500000);
insert into employee values (600, 7000, 1000000);
insert into employee values (700, 7000, 1000000);
insert into employee values (800, 7000, 2000000);
insert into department values (5000, 'dep 1');
insert into department values (6000, 'dep 2');
insert into department values (7000, 'dep 3');
And below is the success screenshot of the query: