MySQL: ROW_NUMBER () counts duplicates as separate rows - mysql

I have two tables. Table of employees Employee and departments Department.
The Employee table looks like this:
+----+-------+--------+--------------+
| 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 |
+----+-------+--------+--------------+
The Department table looks like this:
+----+----------+
| Id | Name |
+----+----------+
| 1 | IT |
| 2 | Sales |
+----+----------+
I want to write a query that for each department will output employees and a number indicating the salary rating. (If the salary is the maximum in the department, the number is 1. If there are two identical salaries in the department, then their rating number must be the same).
I implemented this query using the window function ROW_NUMBER():
SELECT
d.Name Department,
e.Name Employee,
e.Salary,
ROW_NUMBER() OVER (
PARTITION BY d.Name
ORDER BY e.Salary DESC
) SalaryRating,
FROM
Employee e
LEFT JOIN Department d
ON e.DepartmentId = d.Id;
This query outputs the following data:
Department Employee Salary SalaryRating
IT Max 90000 1
IT Joe 85000 2
IT Randy 85000 3
IT Will 70000 4
IT Janet 69000 5
Sales Henry 80000 1
Sales Sam 60000 2
There is a mistake. Randy SalaryRating value is 3, but it should be 2, because he has the same salary as Joe.
What is the error in my query? How to fix it?

You can use DENSE_RANK
SELECT
d.Name Department,
e.Name Employee,
e.Salary,
DENSE_RANK() OVER (
PARTITION BY d.Name
ORDER BY e.Salary DESC
) SalaryRating,
FROM
Employee e
LEFT JOIN Department d
ON e.DepartmentId = d.Id;

Related

SELECT, GROUP BY and COUNT query

I have a sample table below,
+-------------+-------------+-------------+----------+------------+---------------------------------+----------+---------------+
| employee_id | first_name | last_name | email | joined_date| title | salary | supervisor_id |
+-------------+-------------+-------------+----------+------------+---------------------------------+----------+---------------+
| 100 | John | King | EM1 | 1984-06-17 | CEO | 14000.00 | NULL |
| 101 | Leona | Kochhar | EM2 | 1993-09-21 | COO | 10000.00 | 100 |
| 102 | Lex | De Haan | EM3 | 1992-01-13 | CFO | 9000.00 | 100 |
| 103 | Alexander | Hunold | EM4 | 2001-04-03 | Gamer | 5000.00 | 102 |
| 104 | Dave | William | EM5 | 2002-05-21 | Gamer | 2000.00 | 103 |
| 105 | David | Austin | EM6 | 2002-06-25 | Gamer | 2800.00 | 103 |
| 106 | Valli | Longwind | EM7 | 2002-02-43 | Gamer | 2800.00 | 103 |
Certain employees are supervisors to other employees in this table. Do note that supervisor_id is the employee_id.
I am tasked to only use SELECT statement to get the employee_id, first_name, salary of the supervisors and the total number of employee under the supervisor.
In my mind, I know that I will need to use some sort of grouping and count. First, COUNT number of employees under each supervisor and second to GROUP BY supervisor ID. I managed to get the output using the simple COUNT and GROUP BY using this query:
SELECT employee_id, COUNT(supervisor_id)
FROM EMPLOYEE
GROUP BY supervisor_id;
This output the supervisor_id and number of employees under the supervisor_id. Which is:
+---------------+----------------------+
| supervisor_id | COUNT(supervisor_id) |
+---------------+----------------------+
| NULL | 0 |
| 100 | 2 |
| 101 | 0 |
| 102 | 0 |
| 103 | 3 |
*Table above slightly modified - this is a sample output
As mentioned, supervisor_id is the employee_id of the employee within the same table. My problem is that I am unable to get this table to display the employee_id, first_name and salary together with the COUNT column. The end result must show the employee_id (linked to supervisor_id), first_name, salary, COUNT.
I have tried this
SELECT employee_id, first_name, salary, COUNT(supervisor_id)
FROM EMPLOYEE
GROUP BY employee_id, first_name, salary, supervisor_id;
But this just returns the original table.
Also when I tried this
SELECT employee_id, first_name, salary, COUNT(supervisor_id)
FROM EMPLOYEE
GROUP BY supervisor_id;
It returns error "Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column" which according to online is how I craft my query (can't mix aggregated and non-aggregated column?).
Can anyone guide me on this? Thanks.
I guess you just need self LEFT JOIN here:
https://www.db-fiddle.com/f/miLeekBXxoiVV1SxuTYc2c/0
SELECT s.employee_id, s.first_name, s.last_name, s.salary, COUNT(e.employee_id)
FROM employee s
LEFT JOIN employee e
ON e.supervisor_id = s.employee_id
GROUP BY s.employee_id, s.first_name, s.last_name, s.salary;
By the way, you've mentioned in comment that you want to use HAVING COUNT() = 0. You should better not use HAVING for this purpose but just change this query to INNER JOIN. It will do the same but more efficient way.
https://www.db-fiddle.com/f/gM6q1UyagrZ4e1EHRvJ1NU/0
Please try this
SELECT E1.supervisor_id,count(E2.supervisor_id) FROM `EMPLOYEE` AS E1
LEFT JOIN (select supervisor_id FROM EMPLOYEE GROUP BY
supervisor_id) AS E2 ON E1.supervisor_id = E2.supervisor_id
GROUP BY E1.supervisor_id

find sum of value with the same ID with condition in sql

This is the salary table
COMPANY_ID | Salary |
=========================
1 | 20000 |
2 | 10000 |
3 | 50000 |
4 | 13000 |
2 | 8000 |
3 | 20000 |
5 | 20000 |
1 | 10000 |
4 | 40000 |
This is the company table
ID | Comapny |
=========================
1 | Apple |
2 | Facebook |
3 | Google |
4 | Microsoft |
5 | Oracle |
my expected output is to find companies that has a average salary > 20000.
Google
Microsoft
select company_name ,avg(tblSalary.salary) average_salary
from tblCompany
Inner join tblSalary on tblcompany.id = tblsalary.company_id
group by company_name
having avg(tblSalary.salary) > 20000
select company from company a, salary b where a.id = b.id and b.salary >= 20000 group by a.company
or
select company from company a inner join salary b on a.id = b.id and b.salary >= 20000 group by a.company
Like others said read your book, this is as basic as it gets.

Unable to identify syntax issue with using MAX and OVER PARTITION BY

I'm trying to answer a SQL Leetcode question on Medium Difficulty. The question is asking me to find the people that have the highest salary per department.
I tried using GROUP BY but wasn't able to figure out that approach so I tried to use the PARTITION BY in order to find the max salary for each department. Afterwards, I would add a final WHERE statement where I could filter down to people with a salary that equals the max salary.
This is the query I have so far:
SELECT
Department.Name AS Department,
Employee.Name AS Employee,
Employee.Salary,
MAX(Employee.Salary) OVER (PARTITION BY Department.Name) AS MaxSalary
FROM Employee
LEFT JOIN Department ON Department.id = Employee.DepartmentId
;
There's two tables.
EMPLOYEE TABLE:
+----+-------+--------+--------------+
| Id | Name | Salary | DepartmentId |
+----+-------+--------+--------------+
| 1 | Joe | 70000 | 1 |
| 2 | Jim | 90000 | 1 |
| 3 | Henry | 80000 | 2 |
| 4 | Sam | 60000 | 2 |
| 5 | Max | 90000 | 1 |
+----+-------+--------+--------------+
DEPARTMENT TABLE:
+----+----------+
| Id | Name |
+----+----------+
| 1 | IT |
| 2 | Sales |
+----+----------+
and the correct output should look like this.
CORRECT EXPECTED OUTPUT:
+------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT | Max | 90000 |
| IT | Jim | 90000 |
| Sales | Henry | 80000 |
+------------+----------+--------+
Running my current query leads to a runtime error with this message:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(PARTITION BY Department.Name) AS MaxSalary
FROM Employee
LEFT JOIN Department O' at line 7
I've looked over the syntax multiple times so I assume it's a incorrect understanding of how PARTITION BY works. I was expecting that I would see the max salary for each department listed on the right for each individual. Something like this:
+----+-------+--------+--------------+-----------+
| Id | Name | Salary | DepartmentId | MaxSalary |
+----+-------+--------+--------------+-----------+
| 1 | Joe | 70000 | 1 | 90000 |
| 2 | Jim | 90000 | 1 | 90000 |
| 3 | Henry | 80000 | 2 | 80000 |
| 4 | Sam | 60000 | 2 | 80000 |
| 5 | Max | 90000 | 1 | 90000 |
+----+-------+--------+--------------+ -----------+
After achieving that, I was going to add this line:
WHERE Employee.Salary = MaxSalary;
What is the proper way to do this?
You can consider using one of the following:
SELECT D.Name AS Department, E.Name AS Employee, E.Salary
FROM Employee AS E LEFT JOIN Department AS D ON E.DepartmentId = D.id
WHERE (E.Salary = (SELECT MAX(X.Salary) FROM Employee AS X WHERE (X.DepartmentId = E.DepartmentId)));
SELECT D.Name AS Department, E.Name AS Employee, E.Salary
FROM Employee AS E LEFT JOIN Department AS D ON E.DepartmentId = D.id
INNER JOIN (SELECT DepartmentId, MAX(Salary) AS MaxSalary FROM Employee GROUP BY DepartmentId) AS X ON E.DepartmentId = X.DepartmentId AND E.Salary = X.MaxSalary;

Mysql Cross Joining

I have three Tables
Countries
id | name
1 | USA
2 | UAE
3 | UK
4 | INDIA
Users
id | name | countryid
1 | Philip| 1
2 | Aby | 3
3 | Sam | 3
Happiness
id | userid | mark
1 | 1 | 10
1 | 2 | 50
1 | 3 | 70
I need to get a result of happiness country ranking as
Rank | Country | Total
1 | UK | 120
2 | UAE | 10
3 | USA | 0
4 | INDIA | 0
I need a mysql query for this solution..
Maybe something like this:
SET #rank=0;
SELECT
#rank:=#rank+1 AS rank,
tbl.*
FROM
(
SELECT
Countries.name,
COALESCE(SUM(Happiness.mark),0) AS Mark
FROM
Countries
LEFT JOIN Users
on Countries.id=Users.countryid
LEFT JOIN Happiness
ON Happiness.userid=Users.id
GROUP BY
Countries.Name
) AS tbl
ORDER BY tbl.Mark DESC
References:
MySql - Get row number on select
How do I get SUM function in MySQL to return '0' if no values are found?
SQL fiddle here

MySQL5 Query Help to find non-validating rows of join

Find below the test tables:
SELECT * FROM tbl_emp; // There must be few employee with no dept id (did)
+------+-------+------+
| eid | ename | did |
+------+-------+------+
| 1 | SCOTT | 2 |
| 2 | JAMES | 4 |
| 3 | BOND | 1 |
| 4 | TIGER | 5 |
| 5 | CHIP | 0 |
| 6 | DALE | 0 |
| 7 | MARY | 0 |
+------+-------+------+
SELECT * FROM tbl_dept;// There must be few depts which have no employee.
+-------+-------------+
| dptid | dname |
+-------+-------------+
| 1 | HR |
| 2 | IT |
| 3 | ADMIN |
| 4 | TRAVEL |
| 5 | SALES |
| 6 | FINANCE |
| 7 | ENGINEERING |
+-------+-------------+
I want to list all the employee name from tbl_emp which does not have dept and all the dname from tbl_dept which have no employee in a SINGLE QUERY in following manner:
DESIRED RESULTSET:
-------------------
ename dname
CHIP
DALE
MARY
ADMIN
FINANCE
ENGINEERING
-------------------
All I could do is:
SELECT ename FROM tbl_emp WHERE did NOT IN (SELECT dptid FROM tbl_dept);
and
SELECT dname FROM tbl_dept WHERE dptid NOT IN (SELECT did FROM tbl_emp);
Please help in select both dname and ename and that too in single query.
you can use UNION
SELECT ename, NULL dname
FROM tbl_emp
WHERE did NOT IN (SELECT dptid FROM tbl_dept)
UNION
SELECT NULL ename, dname
FROM tbl_dept
WHERE dptid NOT IN (SELECT did FROM tbl_emp);
SQLFiddle Demo
UPDATE 1
you can also use LEFT JOIN instead of using NOT IN (I'd rather use this one)
SELECT a.ename, b.dname
FROM tbl_emp a
LEFT JOIN tbl_dept b
ON a.did = b.dptid
WHERE b.dptid IS NULL
UNION
SELECT c.ename, d.dname
FROM tbl_dept d
LEFT JOIN tbl_emp c
ON d.dptid = c.did
WHERE c.did IS NULL
SQLFiddle Demo