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

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;

Related

How to get result in following scenario

I have table
EMP(id int primary key, name varchar2(15), mgrID int).
Now this table contain all employees(including worker and manager) in company. mgrID column contain id of employee to whom they are reporting.
I want to list the name of worker who is not manager along with their name of manager.
What to do for such query.
I tried nested select query as follows:
select name, (select name from EMP where mgerID is NULL)
as Manager from EMP;
Will this query give proper result?
You could use a self-join:
SELECT e.name AS name, m.name AS manager_name
FROM emp e
JOIN emp m ON e.mgrid = m.id
Your query should fail because you sub-query is uncorrelated and will return multiple results if you have multiple top-level managers.
select name
, (select name from EMP b where b.ID = a.mgerID ) as Manager
from EMP a;
I think the self-join is the more canonical solution, but you should understand the correlated subquery as well as it has many application.

Create table error oracle

I am getting an error: ORA-01789: query block has incorrect number of result columns
when trying to create a table from data in 2 other tables. Please help, is this just a syntax error or am I combining the tables in the wrong way?
CREATE TABLE EMPDATA(ID, NAME, SALARY, DEPTNAME)
AS
SELECT e.employee_id, (e.first_name || e.last_name), e.salary
FROM employees e
UNION
SELECT d.department_name
FROM departments d;
I think you want JOIN instead of UNION:
CREATE TABLE EMPDATA(ID, NAME, SALARY, DEPTNAME)
AS
SELECT e.employee_id, (e.first_name || e.last_name), e.salary, d.department_name
FROM employees e
JOIN departments d on(d.department_id = e.department_id);
The number of coumns while using UNION should be same in the SELECT statement
You need to join to set the department and not union
CREATE TABLE EMPDATA(ID, NAME, SALARY, DEPTNAME)
AS
SELECT e.employee_id, (e.first_name || e.last_name), e.salary, d.department_name
FROM employees e
JOIN departments d on d.id = e.department_id
You might need to adjust the column names of the join condition since you did not mention the relation.
Your select query returns three columns, for the table you want four. union does union the results of queries, but they have to have same number and types of columns. Your second query has one column, first - three.
When you create a table from a subquery, the resulting columns must be of the same type and number of columns as the subquery.
It is not worth having an EMPLOYEE_ID column of type NUMBER in the DDL and having the column EMPLOYEE_ID of type DATE for example.
Maybe the column exists in the DDL and the subquery DOES NOT EXIST.
in your example you try to have the column DEPTNAME in the DDL, but in the subquery that column does not exist in the table EMPLOYEES,
If you want to have the column DEPTNAME, you have to do a JOIN with the table DEPARTMENTS.
Remaining as follows:
CREATE TABLE EMPDATA (ID, NAME, SALARY, DEPTNAME) AS
   SELECT e.employee_id, (e.first_name || e.last_name), e.salary, d.department_name
   FROM employees e,
        departments d
WHERE 1 = 1
AND d.department_id = e.department_id;
In my BLOG I have an article that talks about the DDL CREATE TABLE statement in ORACLE SQL, I'll share it for you.
There I talk about a little more things, like creating primary, foreign, insert, etc. klaves.

group by expression not working properly?

My code works fine for the below query:
CREATE TABLE employee ( employee_id INTEGER, manager_id INTEGER, first_name VARCHAR2(10) NOT NULL, last_name VARCHAR2(10) NOT NULL, title VARCHAR2(20), salary NUMBER(6, 0) );
CREATE TABLE manager( manager_id INTEGER NOT NULL, manager_dept VARCHAR2(20) NOT NULL, first_name VARCHAR2(30) NOT NULL, last_name VARCHAR2(30) NOT NULL ) Here are my tables
SELECT
DISTINCT E.EMPLOYEE_ID, M.MANAGER_ID, E.FIRST_NAME,
E.LAST_NAME, CONNECT_BY_ISLEAF, CONNECT_BY_ISCYCLE, level
FROM EMPLOYEE E
LEFT OUTER JOIN MANAGER M
ON E.MANAGER_ID=M.MANAGER_ID
WHERE m.manager_id=10
start with e.employee_id >0
connect by NOCYCLE m.manager_id=prior e.employee_id
GROUP BY
E.EMPLOYEE_ID, M.MANAGER_ID, E.FIRST_NAME,
E.LAST_NAME, CONNECT_BY_ISLEAF, CONNECT_BY_ISCYCLE,LEVEL
but i want my result set to grouped by only on employee_id and not all fields
When i use this query below then an error generates "NOT A GROUP BY EXPRESSION"
SELECT
DISTINCT E.EMPLOYEE_ID, M.MANAGER_ID, E.FIRST_NAME,
E.LAST_NAME, CONNECT_BY_ISLEAF, CONNECT_BY_ISCYCLE, level
FROM EMPLOYEE E
LEFT OUTER JOIN MANAGER M
ON E.MANAGER_ID=M.MANAGER_ID
WHERE m.manager_id=10
start with e.employee_id >0
connect by NOCYCLE m.manager_id=prior e.employee_id
GROUP BY
E.EMPLOYEE_ID
Whats wrong with this query??
I assume your employee ID is unique, so by grouping by it, you're grouping by everything else in the query as well. But Oracle doesn't know that. If you were grouping by employee ID only, and you had two employees having the same id, one with last name "Smith" and one with last name "Miller", which one would you like to see? It's either Smith or Miller, as you have only one row of data, so the database doesn't know what to do.
That's why you always have to group by every column that's not aggregated (using SUM, MAX, MIN, or one of the other functions).
You could omit the columns from your result set and the group by, then put the whole query into a subquery, with the outer query selecting the remaining columns from a join of the subquery and the employee table. But trust me, you don't really want to do that, as that version would be much uglier to read and understand than your current query.

Specified Departments? - SQL

imagine I have two tables, the "departments" table and the "employee" table.
This employee table has a column for "category".
I'd like to make a query for selecting departments that only have a specified type of employees.
Thank you.
You will need to perform a join from your departments and employee table on whatever columns link these two tables together. In the where clause, you will specify what types of employees that you want.
This will return a row for each employee, which might not be what you want. You may use the distinct function on the important columns that you're looking for in the departments table to get the final answer.
select distinct dept_id
from employee
where category = 'cat1'
and dept_id not in (select distinct dept_id
from employee
where dept_id <> 'cat1');
SELECT dept_id
FROM departments
WHERE dept_id NOT IN
(SELECT DISTINCT dept_id
FROM employee
WHERE category_id != #specified_category)
This query assumes there are no departments with no employees, since it will also return those empty departments. If that's a problem, you can add:
AND dept_id IN (SELECT distinct dept_id FROM employee)
Select d.id_department from departments d where not exists
(Select e.id_employee from employees e where e.category!=your_category and e.id_department=d.id_department) you also need to verify that department has employees.

Reason for The multi-part identifier could not be bound

error - The multi-part identifier "Grant.EmpID" could not be bound.
query -
select emp.EmpID,
COUNT(*) as CountRecords,
COUNT(GrantName) AS CountValues
From Employee as emp full join [Grant] as gr
on emp.EmpID = [Grant].EmpID
-- This is the cause for the error ! Change it to gr.EmpID
group by emp.EmpID
Why does this error occur ? Can't I call a person by real name and also by nickname ?
You're aliasing [Grant]. In other words, you're stating that from here on out, [Grant] will be referred to as gr.
Use the ALIAS in the GROUP BY clause, not the tableName.
SELECT emp.EmpID,
COUNT(*) as CountRecords,
COUNT(GrantName) AS CountValues
FROM Employee as emp
FULL JOIN [Grant] as gr
on emp.EmpID = gr.EmpID -- use the alias.
GROUP BY gr.EmpID -- use the alias.
here's the SQL Order of Operation
FROM clause
WHERE clause
GROUP BY clause
HAVING clause
SELECT clause
ORDER BY clause
No you can't because sql server is not a human. imagine we have an Employee table which references itself
select *
from Employee Emp, Employee Mng
where Emp.ManagerID = Mng.EmployeeID
Mng and Emp are two instances of Employee
so if I select
select * from Employee e, Employee
it will return all employee TWO times, because I am telling give me employees once under the name Employee once under the name e (alias)