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

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

Related

practice sql explanation

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.

EXISTS command vs IN command

For reference, I am trying to answer a SQL question at: https://www.w3resource.com/sql-exercises/sql-subqueries-exercise-21.php
And the answer is:
SELECT first_name, last_name, department_id
FROM employees
WHERE EXISTS (SELECT *
FROM employees
WHERE salary > 3700);
But can someone please explain why the above does not return the same result as:
SELECT e1.first_name, e1.last_name, e1.department_id
FROM employees AS e1
WHERE e1.employee_id IN (SELECT e2.employee_id
FROM employees AS e2
WHERE e2.salary > 3700);
The EXISTS clause gives a value of TRUE or FALSE. The statement that you've written will give a value of TRUE if there is any row at all in employees with a salary of more than 3700. You did not include any condition that requires that row to match the id of the employee you're checking.
If you want to add such a condition, you'd write (assuming a unique employee id exists and is called employee_id):
SELECT first_name, last_name, department_id
FROM employees E1
WHERE EXISTS
(SELECT *
FROM employees
WHERE salary > 3700 AND employee_id = E1.employee_id )
The first has no correlation clause. Hence, it either returns all rows in employees (if any row matching the condition exists). Or it returns no rows in employees (if no such rows exist).
The second is comparing the rows in the subquery to the id in the outer query. The equivalent query using exists would be:
SELECT e1.first_name, e1.last_name, e1.department_id
FROM employees AS e1
WHERE EXISTS (SELECT e2.employee_id
FROM employees AS e2
WHERE e2.employee_id = e1.employee_id AND
e2.salary > 3700
);
Of course, one would expect the employee_id to be the primary key for a table called employees. So this would do the same thing:
SELECT e1.first_name, e1.last_name, e1.department_id
FROM employees AS e1
WHERE e1.salary > 3700

How do I use a value from the superquery inside a subquery?

I need to create a query that shows the last name of an employee, the employee id, the last name of the manager of that employee and the id of that manager.
The last name, id, and the manager id of that employee is easy to do because it is already in one row, which means that the following is sufficient:
SELECT last_name, employee_id, manager_id FROM employees WHERE manager_id IS NOT NULL;
But to get the last_name of the manager, you have to search the same table by the manager id you got from the employee. The solution I found is:
SELECT last_name,
employee_id,
(SELECT last_name FROM employees WHERE employee_id = manager_id),
manager_id
FROM employees
WHERE manager_id IS NOT NULL;
However, it seems that 'manager_id' doesn't work in the subquery (although I expected that) and the output is NULL (for the manager id, all the other columns do have values).
So my question is, how can I use the manager_id in the subquery?
Side note: The manager_id can be different for each employee, so using a constant value doesn't work.
What you need is a correlated subquery. I strongly, strongly recommend that you use table aliases and qualified column names in all your queries. However, these are particularly important with correlated subqueries.
You should write this query as:
SELECT e.last_name, e.employee_id,
(SELECT m.last_name
FROM employees m
WHERE m.employee_id = e.manager_id
),
e.manager_id
FROM employees e
WHERE e.manager_id IS NOT NULL;
The alias e is an abbreviation for the table reference to employees in the outer query. The alias m is an abbreviation for the table reference in the subquery.
Notice that all column references use the table alias. This makes the query unambiguous, can prevent unexpected errors, and makes the query much easier for you and others to understand.
You could use a self inner join ( a join with the same table)
SELECT
a.last_name
, a.employee_id
, b.last_name
, a.manager_id
FROM employees a
INNER JOIN employees b ON b.employee_id = a.manager_id;
The inner join work only if a.manager_id is not null so you can avoid this where condition
When you want to refer to a table in the outer query, you need to either use the full table name like table.field or, as in your case, if the outer query table is same as the subquery table, you need to assign an alias to the outer query table and use it in the subquery like this:
SELECT
last_name, employee_id,
(SELECT last_name FROM employees WHERE employee_id = emp_outer.manager_id),
manager_id
FROM employees emp_outer
WHERE manager_id IS NOT NULL;

Employees without subordinates

I'm trying to print all the employees that don't have subordinates.
I have been thinking about a tree data structure. Practically, most of the employees have subordonates (those are called managers). The only ones without subordonates are the leafs (they don't have any children).
However, I don't understand how can I select the leafs from this tree.
--following prints employees without manager.
SELECT e.employee_id, e.last_name, e.first_name
FROM employees e
WHERE e.employee_id = (SELECT employee_id FROM employees WHERE manager_id IS NULL AND employee_id = e.employee_id);
In short, you want to select all employees, who don't act as managers for other employees. That means, you want to select such employees, whose employee_id is not used as manager_id for any other employee.
Try this:
SELECT *
FROM employees e
WHERE NOT EXISTS (SELECT 1
FROM employees e2
WHERE e2.manager_id = e.employee_id)
You can do this via an outer join:
SELECT e.employee_id, e.last_name, e.first_name
FROM
employees e
LEFT JOIN employees sub
ON e.employee_id = sub.manager_id
WHERE sub.manager_id IS NULL
The filter condition selects only those rows of the left table that have no matching rows in the right table.
This is preferable to filtering via a correlated subquery, as the latter may require performing the subquery separately for every single employee row. (If the query planner avoids that, it will be by transforming it into an equivalent of the outer join.)
SELECT e.employee_ID, e.last_name, e.First_name, CONNECT_BY_ISLEAF "IsLeaf",
LEVEL, SYS_CONNECT_BY_PATH(e.employee_ID, '/') "Path"
FROM employees e
CONNECT BY PRIOR E.employeeID = E.Manager_ID;
where isLeaf =1
Basically stolen from help docs:
http://docs.oracle.com/cd/B12037_01/server.101/b10759/pseudocolumns001.htm#i1007332
or another stack question: get ALL last level children (leafs) from a node (hierarhical queries Oracle 11G)
SELECT *
FROM employees e
WHERE e.employee_id NOT IN ( SELECT nvl(manager_id, 0)
FROM employees );

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;`