practice sql explanation - sql

http://studybyyourself.com/seminar/sql/exercises/8-3/?lang=en
Please provide data about all employees whose salary is higher or equal to the average salary of employees working in the same department (regardless if employees have left the company or not). Required attributes are last name, first name, salary and department name.
SELECT emp.Last_name, emp.First_name, emp.Salary, D.Name
FROM Employee AS emp
INNER JOIN Department AS D ON emp.Department_id = D.ID
WHERE emp.Salary >=
(
SELECT AVG(e.Salary)
FROM Employee AS e
GROUP BY e.Department_id
HAVING e.Department_id = emp.Department_id
)
Can anyone please help explain the solution? Specifically, what does the 'having' clause do in this case that allows the sub-query to work? I get stuck up until that point without the having clause and I expectedly get the 'subquery returns more than 1 row' error but I am not sure how the having clause is fixing the problem.

Related

How to find a destinct values in a table that are larger than all values that reference it?

I need to find the names of bosses who were hired after all the hired date of all their subordinates.
Here is what I have got so far:
SELECT DISTINCT
TRIM(boss.first_name || ' ' || boss.last_name) AS NAME,
boss.phone_number AS phone
FROM
HR.employees e
INNER JOIN HR.employees boss ON (boss.employee_id = e.manager_id)
WHERE
boss.hire_date > MIN(e.hire_date)
AND boss.hire_date > MAX(e.hire_date);
The idea was to check that the boss.hire_date is larger than the earliest and latest hire_date of his subordinates.
However I get following error message:
ORA-00934: group function is not allowed here
Any idea how I have to restructure the query?
What about using a subselect to get the maximum hire date of all employees for a given boss and check that in the WHEREclause?
You may need to filter this list for employees, that are not a boss.
Like:
SELECT boss.*
FROM hr.employees boss
WHERE boss.hire_date > (SELECT MAX(e.hire_date)
FROM hr.employees e
WHERE boss.employee_id = e.manager_id);
This should work better
SELECT TRIM(boss.first_name || ' ' || boss.last_name) AS NAME,
boss.phone_number AS phone
FROM HR.employees boss
INNER JOIN
(
SELECT e.manager_id, MAX(e.hire_date) AS Max_hire_date
FROM HR.employees e
GROUP BY e.manager_id
) m ON m.manager_id=boss.employee_id
WHERE boss.hire_date > m.Max_hire_date
Note that I have supposed that a boss can't manage itself. In an usual hierarchical database, the top manager has its manager_id field = NULL. If in your data a boss can have its manager_id =i ts own employee_id, it wont work.

subquery and join not giving the same result

1
select *
from employees
where salary > (select max(salary) from employees where department_id=50)
2
select *
from employees e left join
employees d
on e.DEPARTMENT_ID =d.DEPARTMENT_ID
where d.salary > (select max(salary) from employees where department_id=50)
why the second query is giving multiple record
i want achieve the same result as of 1st query using join.....
Thanks in Advance......
Rocky, the first select is correct. Why do you want to do any join? Without further information the objective of the second select is not clear (nonsense).
I can't see the point about joining against the same table by DEPARTMENT_ID. Anyway, the problem about duplicates is because you are joining the same two tables by a key is not pk, basically you are multiplyng each employee for all the employees of the same department. This version eliminate duplicates but still has no improvement from the first one.
select *
from employees e left join
employees d
on e.employee_ID = d.employee_ID
where d.salary > (select max(salary) from employees where department_id=50)
You are probably looking for an anti join. This is a pattern mainly used in a young DBMS where IN and EXISTS clauses are slow compared to joins, because the developers focused on joins only.
You are looking for all employees whose salaries are greater than all salaries in department 50. With other words: WHERE NOT EXISTS a salary greater or equal in department 50.
Your query can hence be written as:
select *
from employees e
where not exists
(
select null
from employees e50
where e50.department_id = 50
and e50.salary >= e.salary
);
As an anti join (an outer join where you dismiss all matches):
select *
from employees e
left join employees e50 on e50.department_id = 50 and e50.salary >= e.salary
where e50.salary is null;

somebody explain to me how this query works step by step?

I don't yet understand this SQL statement:
select FIRST_NAME
from EMPLOYEES e
where DEP_ID != (select DEP_ID
from EMPLOYEES
where e.MANAGER_ID = EMPLOYEE_ID);
I would write your query as:
select e.FIRST_NAME
from EMPLOYEES e
where e.DEP_ID <> (select e2.DEP_ID
from EMPLOYEES e2
where e.MANAGER_ID = e2.EMPLOYEE_ID
);
This does not functionally change the query but it qualifies all column references and uses <> which is the traditional SQL operator for not equals.
What this query is doing is returning all employees whose department is not the same as their managers department.
How does it do this? The subquery is a correlated subquery. For each row in employees the subquery returns the department id of the manager.
The where clause then checks whether or not it matches the employee's manager.
This subquery will get the Manager's departments.
select DEP_ID from EMPLOYEES where e.MANAGER_ID = EMPLOYEE_ID
So the main query will just get the employees that not managers.
select FIRST_NAME from EMPLOYEES e where DEP_ID != (Managers dept_ID)
It's finding the employees who are not under a particular manager.
Let's see the inner part first:
select DEP_ID from EMPLOYEES where e.MANAGER_ID = EMPLOYEE_ID
This will fetch the department under particular manager
Now the outer part:
select FIRST_NAME from EMPLOYEES e where DEP_ID != <Departments under particular manager>
Now the result will the list of employees's first name not under that manager

SQL select with multiple different conditions

I am still learning SQL and can't find a proper way to find the following information:
I have created a table "employees" with the following columns:
'department', 'age', 'salary', 'bonus';
I am trying to design a query that will give me all employees that have someone the same age as them in another department and with a bonus superior to their salary.
(to be more precise, if someone in department 'SALES' has the same age as someone in department 'RESEARCH' and have a bonus that is superior to that guy in research's salary, then I would like to display both of them)
Is this possible to do in sql?
Thank you for your time,
-Tom
You can do this using exists. Because you care about the relationship in both direction, this is as simple as looking for people with the same age in the two departments but who do not have the same bonus:
select e.*
from employees e
where exists (select 1
from employees e2
where e2.department <> e.department and e2.age = e.age and
e2.bonus <> e.bonus
);
To get the pairs on the same row, use a self-join:
select e1.*, e2.*
from employees e1 join
employees e2
on e1.age = e2.age and e1.department <> e2.department and
e1.bonus > e2.bonus;

find the link in tables sql exercise

so I'm stuck on this question where it says:
write a query to retrieve a list of ALL departments in alphabetical order containing the columns DEPARTMENT_ID , DEPARTMENT_NAME , LAST_NAME , FIRST_NAME where last_name and first_name are the name of the Manager of the Department wherever there is one.
I'm have the HR database using the departments, employees table what i have written so far is:
select department_id, department_name, e.last_name, e.first_name
from departments d, employees e
where e.department_id=d.department_id
and d.department_id=e.department_id
and d.manager_id=e.manager_id
having department_name = '%Manager%';
yet i can't figure it out, any tips will help thanks!
A having clause relates to a group by. Where you have no group by, there is no meaning for a having clause.
You just need your join to get the record in the employees table for the manager.
select department_id, department_name, e.last_name, e.first_name
from departments d, employees e
where d.manager_id=e.id
Better is to use the newer join syntax,
select dept.department_id, dept.department_name, emp.last_name, emp.first_name
from departments dept
inner join employees emp on dept.manager_id = emp.id
You don't need conditions for e.department_id=d.department_id and d.department_id=e.department_id, (for one thing these are redundant, so you would only need one if they were needed) because the employees.department_id field refers to the department the employee is in, and what you want is the manager employee record for the department. The manager employee record for the department is represented as the foreign key to the primary key of the employee table.
this might help you...
`select e.first_name,e.last_name,d.department_id ,d.department_name from employees e,departmets d where d.manager_id=e.employee_id order by d.department_name;`