Is Inner Join needed when operating on one table? - mysql

There is a table Employee as below:
I need finds out employees who earn more than their managers and I figure out two methods:
SELECT a.Name AS Employee FROM Employee a, Employee b WHERE a.Salary > b.Salary AND a.ManagerId = b.Id;
SELECT a.Name AS Employee FROM Employee a INNER JOIN Employee b ON a.Salary > b.Salary AND a.ManagerId = b.Id;
Both of them work well and have close speed.
What's the difference between them? Thanks.

Those queries are equivalent. But you should use the join syntax instead of commas in the from clause. INNER JOIN ON vs WHERE clause
Here's an alternative option which might have a better performance using exists:
select e.Name AS Employee
from employee e
where exists (
select 1
from employee e2
where e.managerid = e2.id and e.salary > e2.salary
)

Related

sql query company database

database:
I have a query that needs a name of employee(employee table) whose sum of total_sale(works_with table)>34000.
This means firstly group emp_id by sum(total_sale) and then sum(total_sale)>34000 and then return their names from employee table.
You can use the group by and having as follows:
select e.emp_id, e.first_name, e.last_name
from employee e
join works_with w on w.emp_id = e.emp_id
group by e.emp_id, e.first_name, e.last_name
having sum(w.total_sale)>34000
You can do this with a correlated subquery:
select e.*
from employee e
where (select sum(ww.total_sale)
from works_with ww
where ww.emp_id = e.emp_id
) > 34000;
Because this avoids the outer aggregation, this can often be faster than a method that uses group by explicitly. In particular, this can take advantage of an index on works_with(emp_id, total_sale).

Sql numbers of employees by department

I have 2 tables employees(id, first_name, last_name, salary, department_id_ and department(id, name) and I want to show number of employees in each department.
I have this question here:
SELECT department.name, COUNT(*) AS 'employees_number'
FROM department
LEFT JOIN employees
ON employees.department_id = department.id
GROUP BY department.id, department.name;
But for some reason, in departments where I have no people, it shows a number of employees as 1. Any idea why this is happening?
With an outer join you still get a result row when no match in the outer table is found. Only all employee column values are null then.
So rather than count the records, you want to count matched records, i.e. where an employee was found and its data is not null. So Count a column in the employee table (nulls are not counted, when counting a column or expression). E.g. use COUNT(e.department_id) or COUNT(e.id):
SELECT d.name, COUNT(e.id) AS employees_number
FROM department d
LEFT JOIN employees e ON e.department_id = d.id
GROUP BY d.id, d.name;
What I prefer though, is to aggregate/count before joining. The query looks a bit more complicated, but is less prone to errors on future query changes:
SELECT d.name, COALESCE(e.how_many, 0) AS employees_number
FROM department d
LEFT JOIN
(
SELECT department_id, COUNT(*) AS how_many
FROM employees
GROUP BY department_id
) e ON e.department_id = d.id;
As it's one aggregated column only you want, you can move the subquery to your SELECT clause and get thus a simpler query:
SELECT
d.name,
(
SELECT COUNT(*)
FROM employees e
WHERE e.department_id = d.id
) AS employees_number
FROM department d;
Using SUM instead of COUNT also can give you what you want:
SELECT
department.name,
SUM(CASE WHEN employees.id IS NOT NULL THEN 1 ELSE 0 END) AS 'employees_number'
FROM department
LEFT JOIN employees
ON employees.department_id = department.id
GROUP BY department.id, department.name;
SQL Fiddle:
http://sqlfiddle.com/#!9/8b8976/1
select department.name, count(employee.id) as co from
department left join employee on
department.id = employee.dept_id group by department.name
order by co desc, department.name asc

MySQL query to find employees salary greater than any manager

So, I have this schema:
Emp(eid: integer,ename: string,age: integer,salary: real)
Works(eid:integer,did: integer,pct_time: integer)
Dept(did:integer,budget: real,managerid:integer)
I want to return the employees whose salary is greater that any manager (regardless of their department).
select E.ename, D.did, E.eid
from Emp E
join Works W ON E.eid = W.eid
join Dept D ON D.did = W.did
where E.salary > (select E2.salary from Emp E2, Dept D2
where E2.eid = D2.managerid)
But the sub query statement is returning more than one row. How can I compare multiple rows from 2 tables to get the correct result? Grouping should work, right?
Your requirement could be translated to finding out an employee who exist a manager that have salary lower than his salary.
You could try this.
SELECT e.*
FROM emp e
WHERE EXISTS (SELECT 1
                       FROM emp e2 INNER JOIN
                            dept d2
                           ON e2.eid = d2.managerid
                      WHERE  e2.salary < e.salary);
If you want to return the employees whose salary is greater that any manager, then why dont you just find out all the employees who have a salary greater than the minimun salary of all managers?
select E.ename, D.did, E.eid
from Emp E
join Works W ON E.eid = W.eid
join Dept D ON D.did = W.did
where E.salary > (select min(E2.salary)
from Emp E2, Dept D2
where E2.eid = D2.managerid)
I don't see why you need the works table. The question is about any manager. One way that you can write this is using > all:
select e.*
from emp e
where e.salary > all (select e2.salary
from emp e2 join
dept d2
on e2.eid = d2.managerid
);
You can run the subquery and see that it returns the salaries of all managers. You want employees whose salary is greater than those.
EDIT:
Does this version work?
select e.*
from emp e
where e.salary > (select MAX(e2.salary)
from emp e2 join
dept d2
on e2.eid = d2.managerid
);
The > all may not work as expected if any salaries are NULL.

Using <> in SQL

The question is to find the employee last names for employees who do not have a child of the same sex as themselves.
My code is :
SELECT E.Lname
FROM EMPLOYEE E, DEPENDENT D
WHERE E.Ssn = D.Essn
AND E.Sex <> D.Sex
But I've learned that it is a bad practice to use <>. When I used <> I get multiple names as output.
Smith
Smith
Wong
Wong
Wallace
Is there an alternate way of doing this using NOT IN clause?
Try this
SELECT E.Lname
FROM EMPLOYEE E, DEPENDENT D
WHERE E.Ssn = D.Essn
AND D.Relationship IN ("Son", "Daughter")
AND E.Sex = D.Sex
GROUP BY E.Ssn
Instead of using a Cartesian product from the query, join use left join to not repeat entries.
i.e.
SELECT E.Lname
FROM EMPLOYEE E LEFT JOIN DEPENDENT D
ON E.Ssn = D.Essn
WHERE E.Sex <> D.Sex
NOTE Query would give duplicate last name as long as multiple employee has same last name.if unique last name is required then try below query
SELECT DISTINCT(E.Lname)
FROM EMPLOYEE E LEFT JOIN DEPENDENT D
ON E.Ssn = D.Essn
WHERE E.Sex <> D.Sex
You can use NOT EXISTS:
SELECT E.Lname
FROM EMPLOYEE E
WHERE NOT EXISTS (SELECT 1
FROM DEPENDENT D
WHERE E.Ssn = D.Essn
AND E.Sex = D.Sex
AND D.Relationship IN ('Son','Daughter')
)
This will only return records from the EMPLOYEE table, so no multiple records returned.
Edit: Noticed the relationship options included 'spouse'.
Try this :
SELECT DISTINCT(EMPLOYEE.Lname) FROM DEPENDENT LEFT JOIN EMPLOYEE ON DEPENDENT.Essn=EMPLOYEE.Ssn WHERE (DEPENDENT.Sex <> EMPLOYEE.Sex) AND DEPENDENT.Relationship IN ('Daughter','Son');
Find the a list of employees who have a child with the same sex as them and then exclude them from the list of all those who have a child.
SELECT DISTINCT A.Lname
FROM Employee A
INNER JOIN Dependent B
on A.Ssn = B.essn
Where B.Relationship in ('Son', 'Daughter')
AND A.Ssn not in (
SELECT AA.ssn
FROM Employee AA
INNER JOIN Dependent BB
on AA.Ssn = BB.essn
Where BB.Relationship in ('Son', 'Daughter')
And AA.Sex = BB.Sex
)

MySQL Query - earn over the average salary

I am trying to get this query to work. Basically it is getting all people that earn't a degree at Oxford Brookes that now earn a salary over the average salary of the whole database.
I have searched for hours trying to find a solution. Please feel free to offer advice, so that I may learn from this and solve my issue. Thanks
SELECT a.personid,
a.firstname,
a.lastname,
b.placeofstudy,
AVG(c.salary)
FROM person a
INNER JOIN award d
ON a.personid = d.personid
INNER JOIN qualification b
ON d.qualid = b.qualid
INNER JOIN job c
ON a.personid = c.personid
WHERE placeofstudy = 'Oxford Brookes'
GROUP BY a.personid;
This currently produces no errors but does not return the average salary, just each persons current salary.Even when I add 'having c.salary > avg(c.salary)' it refuses to work.
select a.personid, a.firstname, a.lastname, b.placeofstudy, c.salary, (select avg(salary) from job) as AvgSalary
from person a
inner join award d on a.personid = d.personid
inner join qualification b on d.qualid = b.qualid
inner join job c on a.personid = c.personid
where placeofstudy = 'Oxford Brookes'
and c.salary > (select avg(salary) from job)
You need to use the keyword HAVING.
Try this:
select a.personid, a.firstname, a.lastname, b.placeofstudy, avg(c.salary) AS avgsalary
from person a
inner join award d
on a.personid=d.personid
inner join qualification b
on d.qualid=b.qualid
inner join job c
on a.personid=c.personid
where placeofstudy = 'Oxford Brookes'
group by a.personid, a.firstname, a.lastname, b.placeofstudy
having c.salary > avgsalary