MySQL self join if null function - mysql

I'm learning through MySQL Ver.5.7.17, please clarify on my below query.
I have a table employees which contains employee ids and corresponding manager ids, but few employees do not have manager ids as they belong to manager roles.
The table columns are employee id, first name, last name, manager id. I have designed below self join query which gives me employees with manager ids but I am looking for additional details of manager (who are also employees) who do not have manager ids (null).
SELECT E.EMPLOYEE_ID AS EMP_ID, CONCAT(E.FIRST_NAME,' ',E.LAST_NAME
) AS EMP_NAME,
M.EMPLOYEE_ID AS MGR_ID
,CONCAT(M.FIRST_NAME,' ',M.LAST_NAME) AS MGR_NAME
FROM EMPLOYEES AS E JOIN EMPLOYEES AS M
ON (E.MANAGER_ID =M.EMPLOYEE_ID) ORDER BY MGR_ID
I tried with IF NULL function in query as below and got below error message.
Error Code: 1064. 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 'NULL(M.EMPLOYEE_ID,'NO MGR') AS MGR_ID ,CONCAT(M.FIRST_NAME,' ',M.LAST_NAME) AS ' at line 2
SELECT E.EMPLOYEE_ID AS EMP_ID, CONCAT(E.FIRST_NAME,' ',E.LAST_NAME) AS EMP_NAME,
IF NULL(M.EMPLOYEE_ID,'NO MGR') AS MGR_ID
,CONCAT(M.FIRST_NAME,' ',M.LAST_NAME) AS MGR_NAME
FROM EMPLOYEES AS E JOIN EMPLOYEES AS M
ON (E.MANAGER_ID =M.EMPLOYEE_ID) ORDER BY MGR_ID

You can use COALESCE. Also, I think you need a left join in order to get those records as well which don't have a manager:
select E.EMPLOYEE_ID as EMP_ID,
concat (E.FIRST_NAME,' ',E.LAST_NAME) as EMP_NAME,
coalesce(M.EMPLOYEE_ID, 'NO MGR') as MGR_ID,
concat (M.FIRST_NAME,' ',M.LAST_NAME) as MGR_NAME
from EMPLOYEES as E
left join EMPLOYEES as M on (E.MANAGER_ID = M.EMPLOYEE_ID)
order by MGR_ID

Related

What is the difference between JOIN through ON and Using in MYSQL

I am trying to write a query to display the last name, department
number, and department name for all the employees.
And this my working code:
SELECT
last_name,
department_id,
department_name
FROM
employees
JOIN departments USING(DEPARTMENT_ID);
When I was trying to make the query using JOIN ON ,I faced an error saying that
Column 'department_id' in field list is ambiguous through this code
query using JOIN ON:
SELECT
last_name,
department_id,
department_name
FROM
employees
JOIN departments ON(
employees.DEPARTMENT_ID = departments.DEPARTMENT_ID
)
I want to know why it is not working .
The ambiguity should be resolved in select clause
SELECT
last_name,
employees.department_id,
department_name
FROM employees
JOIN departments USING(DEPARTMENT_ID);

JOIN subquery and different reault

I was writing an exercise about "Write a query to find the names (first_name, last_name) of the employees who are not supervisors"
I write it on my own and when i check the result or both, mine has less rows than the other.
I was using the JOIN function and the other doesn't.
I want help to know why two results are so different.
Thanks
the one i use join
SELECT
first_name, last_name
FROM
employees AS E
JOIN
departments AS D ON E.department_id = D.department_id
WHERE
NOT EXISTS( SELECT
0
FROM
departments
WHERE
E.manager_id = D.manager_id)
order by last_name;
the one doesn't use join
SELECT
b.first_name, b.last_name
FROM
employees b
WHERE
NOT EXISTS( SELECT
0
FROM
employees a
WHERE
a.manager_id = b.employee_id);
The big problem is that the NOT EXISTS subquery is referencing rows from the joined table. The manager_id column is qualified with D., and that's a reference to the joined table, not the table in the FROM clause of the subquery.
E.manager_id = D.manager_id
^^^^^^^ ^
We also suspect that an employee's supervisor is recorded in the employee row, as a reference to another row in the the employee table. But we don't have the schema definition or any example data, so we're just guessing.
It seems like there would be a supervisor_id in the employee table...
SELECT e.first_name
, e.last_name
, e.department_id
, d.department_id
FROM employees e
WHERE NOT EXISTS
( SELECT 1
FROM employees s
WHERE s.id = e.supervisor_id
)
ORDER
BY e.last_name
, e.first_name
It's also possible that some rows in employee have a value in the department_id column that don't have a matching row in the department table. If there is no matching row in department, the inner join will prevent the row from employee from being returned.
We can use an outer join when we want to return rows even when no matching row is found in the joined table. If we want to involve the departments table, because a "supervisor" is defined to be an employee that is the manager of a department, we can employ an anti-join pattern...
SELECT e.first_name
, e.last_name
FROM employees e
LEFT
JOIN departments d
ON d.manager_id = e.employee_id
WHERE d.manager_id IS NULL
ORDER
BY e.last_name
, e.first_name
Again, without a schema and some sample data, we're just guessing.
This query can be written using only Employees table, but in your query you have joined the employees table with department table. A query should be written with the minimal amount of tables that suffice your expected output, joining unnecessary tables may result in wrong out puts.
In this case you are joining Employees with department here what if there is no Department_ID in employee table for some employees, so those data will be dropped in the join and result won't be the expected.

Can't Figure Out how to join tables

SELECT e.ManagerID, count(*) as NumberOfDepartments
From HumanResources.Employee e, Person.Contact c
where e.ContactID = c.ContactID
group by e.ManagerID;
The Goal is to write a report to display the managerid, firstname and lastname of that manager and the number of unique different departments they supervise. and only show the manager that supervises the most departments.
I have to ensure that all employees are currently employed (ie enddate does not contain a date).
The code above is working in showing number the managerID and number of department he runs but whenever I try to put in the first name and last name I have to put them also in the 'group by' clause and that way it makes the whole report going crazy. Please Help.
Database Here
From your schema, seems that the managerID column in Employee is populated with the ID of the manager for that employee. That would explain why when adding firstName and lastName the report goes crazy, because you'd be grouping by the employee's name, not the manager's.
Without seeing the tables content it's hard to tell, but you may have that managers can be recognised by not having managerID populated.
If this is the case, you can write your query like this
select e.EmployeeID, c.firstName, e.lastName, count(distinct edh.DepartmentID)
from Employee e
join Contact c
on e.ContactID = c.ContactID
join Employee e2
on e1.EmployeeID = e2.ManagerID
join EmployeeDepartmentHistory edh
on e2.EmployeeID = edh.EmployeeID
where e.ManagerID is null and edh.EndDate is null
group by e.EmployeeID, c.firstName, e.lastName
The first instance of Employee table is the managers (because you set where e.ManagerID is null), the join with Contact gets you the managers' names, the second instance of Employee gets you all the people managed by each manager, and the join with EmployeeDepartmentHistory gets you their department (which you count on) and their EndDate, that has to be null to ensure you that they're currenty employed.
Edit
Please note the way I wrote the joins; writing them as comma separated tables names in your from clause with the join condition in your where is a bad habit that should be kicked, because it makes reading, maintaining and changing them to outer joins much harder. That's why join was introduced in SQL language back in 1992.
In MSSQL:
SELECT e.ManagerID, e.FirstName, e.LastName, COUNT(*) AS NumberOfDepartments FROM HumanResources.Employee e
INNER JOIN Person.Contact c ON e.ContactID=c.ContactID
GROUP BY e.ManagerID, e.FirstName, e.LastName
If you need it in MySql, change ON to WHERE pattern and INNER JOIN to JOIN

Two table joins for one query -- confusing result

I'm trying to execute two separate inner table joins in my query to return values from two tables.
SELECT pname, avg(salary)
FROM project p INNER JOIN department d on p.dnum = d.dnumber
INNER JOIN employee e ON e.dno = d.dnumber;
I'm getting one row in the result set... pname = null, avg(salary) = null.
Result set should contain 11 rows because there are 11 projects in the schema.
Can someone point me in the right direction?
Thank you
You are missing the group by:
SELECT pname, avg(salary)
FROM project p INNER JOIN
department d
on p.dnum = d.dnumber INNER JOIN
employee e
ON e.dno = d.dnumber
GROUP BY pname;
In most databases, your version would fail with an obvious syntax error. MySQL only enforces the ANSI standard if you use the ONLY_FULL_GROUP_BY mode (see here).
Use left outer join instead of inner join
Or can you show me your data tables
Do you need the department table in your query?
Does the following query return all the data you need to summarize?
SELECT pname, salary
FROM ( SELECT salary, dno AS dnum FROM employee ) e
NATURAL JOIN project;
If it does, then this might be the summarization you require:
SELECT pname, AVG( salary ) AS average_salary
FROM ( SELECT salary, dno AS dnum FROM employee ) e
NATURAL JOIN project
GROUP
BY pname;

SQL Count(Select) Query

I am working on a sql query to do the following:
For each project, retrieve the project number, the project name, the number of employees who work on that project.
Here is what I have so far:
select pno, pname,
count(select fname from
employee inner join works_on
on employee.ssn=works_on.essn
inner join project
on works_on.pno=project.pno)
as num_emp from project
Which gives me this error:
1064 - 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 'select fname from employee inner join works_on on employee.ssn=works_on.essn inn' at line 1
Which I am assuming means I can't put a select statement inside a count function, but I do not see how else to do this
Attached is my schema
I'd do it like this, grouping on the project details for which you want the employee count:
SELECT
pno,
pname,
COUNT(employee.snn) AS num_emp
FROM
project
INNER JOIN
works_on
ON works_on.pno = project.pno
INNER JOIN
employee
ON employee.ssn = works_on.essn
GROUP BY
pno,
pname;
EDIT:
Actually, if you want to list projects with no employees assigned then you could make your original query more correct by doing something like:
SELECT
pno,
pname,
(
SELECT
COUNT(fname)
FROM
employee
INNER JOIN
works_on
ON employee.ssn = works_on.essn
WHERE
works_on.pno = project.pno
) AS num_emp
FROM
project;
You don't need the sub query
Count(employee ID) and group by the other fields
You can do it like this:
SELECT pno, pname,count(fname) AS Cnt
FROM employee, works_on, project
WHERE employee.ssn=works_on.essn
AND works_on.pno=project.pno
GROUP BY pno,pname
Use a COUNT(*) with an ordinary join and GROUP BY.
select p.pno, p.pname, COUNT(*) AS num_emp
FROM project AS p
JOIN works_on AS w ON w.pno = p.pno
GROUP BY p.pno
You don't need to join the employee table because you're not using any information from that table -- the information is all in works_on.
If you need to include projects with no employees working on them, you need to use a left join.
select p.pno, p.pname, COUNT(w.pno) AS num_emp
FROM project AS p
LEFT JOIN works_on AS w ON w.pno = p.pno
GROUP BY p.pno
In the unlikely case that there can be multiple entries in works_on for the same project+employee, use COUNT(DISTINCT w.essn) in the queries so they're not counted multiple times.