Select records with highest salary from duplicate records - sql

Select employee record with highest salary from duplicate records with same name and different salary
id|name|salary
1 | A | 500
2 | B | 100
3 | A | 400
3 | B | 200
Output
1 | A | 500
3 | B | 200
Please post the generic sql that will work on all the databases.
I have tried the below query. But this does not return record if duplicate not exist.
select e.id,e.name,e.salary FROM employee e,
employee e1
WHERE
e.name = e1.name
AND e.salary > e1.salary

Using NOT EXISTS
SELECT s1.id, s1.name, s1.salary
FROM salaries s1
WHERE NOT EXISTS
(
SELECT *
FROM salaries s2
WHERE s1.name = s2.name AND s1.salary < s2.salary
)
Using ALL
SELECT s1.id, s1.name, s1.salary
FROM salaries s1
WHERE s1.salary >=
ALL(
SELECT salary
FROM salaries s2
WHERE s1.name = s2.name
)

Any reasonable modern database (mysql being the notable exception) should support window functions. rank() should do the trick:
SELECT id, name, salary
FROM (SELECT id, name, salary,
RANK() OVER (PARTITION BY name ORDER BY salary DESC) rk
FROM some_table) t
WHERE rk = 1

In SQL Server that works (I don't have another SGDB at this time, but it shouldn't be difficult to adapt it):
select s.*
from salaries s
join (
select name,MAX(salary) as maxsalary
from salaries
group by name) ms on s.name=ms.name and s.salary=ms.maxsalary
the subquery selects rows that represent maximum salaries. The main query filters according to both parameters: name and max salary.

Related

Write a SQL query to find employees who earn the top three salaries for the IT department

Write a SQL query to find employees who earn the top three salaries for the IT department.
My query:
SELECT TOP(3) WITH TIES
d.name AS department, e.name AS employee, e.salary
FROM
employee e, department d
WHERE
e.departmentid = d.id AND d.name ='IT'
ORDER BY
e.salary DESC
This is supposed to show me 4 results. Because I'm including the ties. But for some reason it is not. I don't understand why. I need the top 3 salaries including the ties.
I get this result:
Department
Employee
Salary
IT
Max
90000
IT
Randy
85000
IT
Joe
85000
Expected results:
Department
Employee
Salary
IT
Max
90000
IT
Randy
85000
IT
Joe
85000
IT
Will
70000
Employee table
Id
Name
Salary
DepartmentId
1
Joe
85000
1
2
Henry
80000
2
3
Sam
60000
2
4
Max
90000
1
5
Janet
69000
1
6
Randy
85000
1
7
Will
70000
1
Department table:
Id
Name
1
IT
2
Sales
You are going to need window functions for this. SELECT TOP (n) WITH TIES stops at row n and only includes more rows if there are ties.
So, in addition to proper, explicit, standard, readable JOIN syntax, you can learn about window functions:
SELECT e.*
FROM (SELECT d.name as department, e.name as employee, e.salary,
DENSE_RANK() OVER (ORDER BY e.salary DESC) as seqnum
FROM employee e JOIN
department d
ON e.departmentid = d.id and d.name = 'IT'
) e
WHERE seqnum <= 3
ORDER BY e.salary DESC

how to find the maximum salary of employees in a specific department

I have a table named SalaryTable containing salaries of employee in various departments:
dept_id name salary
12 a 100
13 b 200
12 c 300
14 d 400
12 e 500
13 f 600
I need to find the maximum salary of each department with given department id AND the name of that person along with maximum salary.
I am using the following sql query for this
select dept_id, name, max(salary)
from SalaryTable
group by salary
But the above code is giving me error:
dept_id must be an aggregate expression or appear in GROUP BY clause
I am able to get the following table easily with this below query:
select dept_id, max(salary) as max_salary
from SalaryTable
group by salary
dept_id max_salary
12 500
13 600
14 400
but I also need the name of that person as:
REQUIRED OUTPUT
dept_id name max_salary
12 e 500
13 f 600
14 d 400
You appear to be learning SQL, so you can build on what you have. The following gets the maximum salary:
select dept_id, max(salary)
from SalaryTable
group by dept_id;
You can use this as a subquery, to get all matching names:
select st.*
from SalaryTable st join
(select dept_id, max(salary) as max_salary
from SalaryTable
group by dept_id
) std
on st.dept_id = std.dept_id and
st.salary = std.max_salary
use correlated subquery
select dept_id, name, salary
from SalaryTable a
where salary =(select max(salary) from SalaryTable b where a.dept_id=b.dept_id)
To be exact:
SELECT dept_id, NAME, salary FROM SalaryTable a
WHERE salary =(SELECT MAX(salary) FROM SalaryTable b WHERE a.dept_id=b.dept_id)
ORDER BY dept_id;
Also see try by joins because see this
Remember: Whatever you put in between select and from in single sql statement that must be used in the group by clause (That's what your error says!).
You can do it with NOT EXISTS:
select s.* from SalaryTable s
where not exists (
select 1 from SalaryTable
where dept_id = s.dept_id and salary > s.salary
)
order by s.dept_id
See the demo.
Results:
> dept_id | name | salary
> ------: | :--- | -----:
> 12 | e | 500
> 13 | f | 600
> 14 | d | 400

Trying to find an Oracle query to find nth highest salary and among them one with highest experience

I need to find name of the person with 3rd highest salary
The table in question is as follows:
Name Salary Experience
-------------------------------
Den 11000 114
Gerald 11000 148
Ellen 11000 174
Eleni 10500 149
Clara 10500 162
Janette 10000 156
Peter 10000 150
Hermann 10000 204
Harrison 10000 169
I need to find name of the person having max experience and in the 3rd highest salary bracket.
So evidently, 3rd highest salary is 10000 and max experience among those having 3rd highest salary is Hermann with exp of 204.
I have query to find the 3rd highest salary:
select name, salary, experience
from sal s1
where 3 - 1 = (select count(distinct salary)
from sal s2
where s2.salary > s1.salary);
But this query returns 4 rows and I need to know how I can filter it even further in this same query to find Hermann with exp of 204.
Use the DENSE_RANK analytic function to find the 3rd highest salary and the ROW_NUMBER (or RANK or DENSE_RANK) analytic function with a PARTITION BY clause to find the highest experience per salary. This only requires a single table/index scan.
Oracle Setup:
CREATE TABLE table_name ( Name, Salary, Experience ) AS
SELECT 'Den', 11000, 114 FROM DUAL UNION ALL
SELECT 'Gerald', 11000, 148 FROM DUAL UNION ALL
SELECT 'Ellen', 11000, 174 FROM DUAL UNION ALL
SELECT 'Eleni', 10500, 149 FROM DUAL UNION ALL
SELECT 'Clara', 10500, 162 FROM DUAL UNION ALL
SELECT 'Janette', 10000, 156 FROM DUAL UNION ALL
SELECT 'Peter', 10000, 150 FROM DUAL UNION ALL
SELECT 'Hermann', 10000, 204 FROM DUAL UNION ALL
SELECT 'Harrison', 10000, 169 FROM DUAL
Query: If you want to find "the person having max experience in the 3rd highest salary bracket":
SELECT Name, Salary, Experience
FROM (
SELECT t.*,
DENSE_RANK() OVER ( ORDER BY Salary DESC ) AS s_rank,
ROW_NUMBER() OVER ( PARTITION BY Salary ORDER BY Experience DESC )
AS Exp_rownum
FROM table_name t
)
WHERE s_rank = 3
AND Exp_rownum = 1;
If you swap the ROW_NUMBER() analytic function for either RANK() or DENSE_RANK() then this will return multiple people if they are tied with the joint highest experience in the 3rd highest salary bracket.
Output:
NAME | SALARY | EXPERIENCE
:------ | -----: | ---------:
Hermann | 10000 | 204
Query: If you want to find "the person having max experience and (also) in the 3rd highest salary bracket":
Just take the query above and remove the PARTITION BY clause.
SELECT Name, Salary, Experience
FROM (
SELECT t.*,
DENSE_RANK() OVER ( ORDER BY Salary DESC ) AS s_rank,
ROW_NUMBER() OVER ( ORDER BY Experience DESC ) AS Exp_rownum
FROM table_name t
)
WHERE s_rank = 3
AND Exp_rownum = 1;
Output:
(Note: if Herman's experience was 173 then this would not return any rows as Ellen would have the highest experience but she would not be in the 3rd highest salary bracket and Herman would be in the 3rd highest salary bracket but would only have the 2nd highest experience.)
NAME | SALARY | EXPERIENCE
:------ | -----: | ---------:
Hermann | 10000 | 204
db<>fiddle here
This query makes use of ROWNUM and MAX to find the right row. In the innermost sub-query the max experience is retreived for each salary level, ordered by salary descending, and then in the outer sub-query row numbers are added and this is joined with the original table to find the correct row(s)
SELECT s.name, s.salary, s.experience
FROM sal s
JOIN (SELECT s2.*, ROWNUM rnum
FROM (SELECT salary, max(experience) AS m_exp
FROM sal
GROUP BY salary
ORDER BY salary DESC) s2) s3 ON s3.salary = s.salary AND
s3.m_exp = s.experience AND
rnum = 3
You don't query on the experience. So you've to add a where-clause:
select name, salary, experience
from sal s1
where 3 - 1 = (select count(distinct salary)
from sal s2
where s2.salary > s1.salary)
and experience = (select max(experience) from sal)
UPDATE
The alternative (max experience within 3rd highest salary) should be:
select name, salary, experience
from sal s1
where 3 - 1 = (select count(distinct salary)
from sal s2
where s2.salary > s1.salary)
and experience = (select max(experience) from sal s3
where 3 - 1 = (select count(distinct salary)
from sal s4
where s4.salary > s3.salary)
)

Removing duplicates by adding them up [SQL]

I have a query like this:
select employee_id, salary
from salary
left join employee on salary.employee_id=employee.id_employee;
It returns me these results
EMPLOYEE ID | SALARY
-------------|-------
1 | 50
2 | 50
3 | 50
1 | 30
How do I remove duplicates by adding them up, like this:
EMPLOYEE ID | SALARY
------------|--------
1 | 80
2 | 50
3 | 50
There is no reason for a left join from salary to employee. Presumably, every employee_id in salary refers to a valid employee. So, this should do what you want:
select s.employee_id, sum(s.salary) as salary
from salary s
group by s.employee_id;
If you want all employees, even those who are not in the salary table, then an outer join is appropriate -- but employee should be first:
select e.id_employee, sum(s.salary) as salary
from employee e left join
salary s
on s.employee_id = e.id_employee
group by e.id_employee;
Employees not in salary will have a value of NULL.
Note that the group by condition in this query is on employee, the first table in the left join.
select employee_id, SUM(salary) as salary
from salary
left join employee on salary.employee_id=employee.id_employee
group by emplyee_id;
This is exactly what a group by clause is for. You'll have to group by the emplopyee_id and specify how you want to aggregate the salary:
SELECT employee_id, SUM(salary)
FROM salary
GROUP BY employee_id

Get top results for each group (in Oracle)

How would I be able to get N results for several groups in
an oracle query.
For example, given the following table:
|--------+------------+------------|
| emp_id | name | occupation |
|--------+------------+------------|
| 1 | John Smith | Accountant |
| 2 | Jane Doe | Engineer |
| 3 | Jack Black | Funnyman |
|--------+------------+------------|
There are many more rows with more occupations. I would like to get
three employees (lets say) from each occupation.
Is there a way to do this without using a subquery?
I don't have an oracle instance handy right now so I have not tested this:
select *
from (select emp_id, name, occupation,
rank() over ( partition by occupation order by emp_id) rank
from employee)
where rank <= 3
Here is a link on how rank works: http://www.psoug.org/reference/rank.html
This produces what you want, and it uses no vendor-specific SQL features like TOP N or RANK().
SELECT MAX(e.name) AS name, MAX(e.occupation) AS occupation
FROM emp e
LEFT OUTER JOIN emp e2
ON (e.occupation = e2.occupation AND e.emp_id <= e2.emp_id)
GROUP BY e.emp_id
HAVING COUNT(*) <= 3
ORDER BY occupation;
In this example it gives the three employees with the lowest emp_id values per occupation. You can change the attribute used in the inequality comparison, to make it give the top employees by name, or whatever.
Add RowNum to rank :
select * from
(select emp_id, name, occupation,rank() over ( partition by occupation order by emp_id,RowNum) rank
from employee)
where rank <= 3
tested this in SQL Server (and it uses subquery)
select emp_id, name, occupation
from employees t1
where emp_id IN (select top 3 emp_id from employees t2 where t2.occupation = t1.occupation)
just do an ORDER by in the subquery to suit your needs
I'm not sure this is very efficient, but maybe a starting place?
select *
from people p1
join people p2
on p1.occupation = p2.occupation
join people p3
on p1.occupation = p3.occupation
and p2.occupation = p3.occupation
where p1.emp_id != p2.emp_id
and p1.emp_id != p3.emp_id
This should give you rows that contain 3 distinct employees all in the same occupation. Unfortunately, it will give you ALL combinations of those.
Can anyone pare this down please?