I'm using the HR table schema where i have an exercise stating:
The job_history can contain more than one entries for an employee who was hired more than once. Create a query to retrieve a list of employees that were hired more than once. Include the columns EMPLOYEE_ID, LAST_NAME, FIRST_NAME and the aggregate "Times Hired".
What have I done so far is:
select e.employee_id, e.last_name, e.first_name,
count (start_date) as Times_Hired
from job_history jH, employees e
WHERE e.employee_id=jH.employee_id
group by e.employee_id, e.last_name, e.first_name;
Now, my questions are:
should the whole thing be a subquery?
which columns connect the tables job_history and employees?
Because when I run this it displays a few employees only.
You can (and should) rewrite the query with explicit ANSI JOIN syntax to make it clear:
SELECT employee_id, e.last_name, e.first_name,
count (*) AS Times_Hired
FROM employees e
JOIN job_history j USING (employee_id)
GROUP BY employee_id, e.last_name, e.first_name
HAVING count (*) > 1;
I also use a LEFT [OUTER] JOIN to include employees in the result that do not have any rows on job_history (yet).
This is not relevant, since you are only interested in employees ..
than were hired more than once
I implemented this condition with HAVING count (*) > 1.
As to your 2nd question: obviously, employee_id is the column that ..
connects the tables job_history and employees?
Since the column name we join on (employee_id) is identical in both tables I simplified to an equi-join with USING.
And I use count(*) instead of count (start_date), since it has not been declared whether start_date can be NULL, in which case it wouldn't add to the count.
As to your first question: no, you don't need a subquery here.
Alternative JOIN syntax with ON
SELECT e.employee_id, e.last_name, e.first_name,
count (*) AS Times_Hired
FROM employees e
JOIN job_history j ON j.employee_id = e.employee_id
GROUP BY e.employee_id, e.last_name, e.first_name
HAVING count (*) > 1;
Tested both in SQLfiddle.
Related
select J.job_title,
D.department_name,
E.first_name || ' ' || E.last_name as "Employee Name",
H.start_date
from job_history H
join jobs J
on H.job_id = J.job_id
join departments D
on H.department_id = D.department_id
join employees E
on H.department_id = E.department_id
where H.start_date between '01-Jan-93' and '03-Aug-97';
2nd query
SELECT job_title,
department_name,
first_name || ' ' || last_name AS Employee_name,
start_date
FROM job_history
JOIN jobs USING (job_id)
JOIN departments USING (department_id)
JOIN employees USING (employee_id)
WHERE start_date between '01-jan-93' AND '08-aug-97';
You get different answers (more rows from the first query, which is very likely the wrong query) because in the first query you join the last table, employees, to the first table, job_history, on the department_id column. (In the second query you join by employee_id, which is probably the correct way.)
This way, for every row in job_history that matches and employee in the employees table on the department_id column you will get a row in the join output - even if the employee doesn't match that job_history row by employee id. All employees in the Executive department will appear in the output.
Note that your where clause is not the same (one period ends 3 August, the other 8 August), but that is not the reason for the different output. (It might be on different data though.)
Also, they should teach you not to use strings in date comparisons; but this is unrelated to your question.
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;
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 );
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.
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;`