From the HR Schema, how can I get the number of job switches of each employee?
My try:
select e.first_name, count(*)-1 switches
from hr.job_history j
right outer join hr.employees e on (j.employee_id=e.employee_id)
group by e.first_name, j.employee_id;
It does give an answer but I'm not quite sure if it's right. Is the code written correctly?
You get incorrect results because you are grouping on e.first_name, j.employee_id. j.employee_id will be null for every employee that doesn't have a job history, and then your count will reflect the number of employees with that first name. (It doesn't show in your results, but in my Oracle HR sample schema I get 2 rows each for 'David', 'John' and 'Peter', who have no job history.)
Below I have changed it to use e.employee_id instead of j.employee_id in the group by clause:
select e.employee_id, e.first_name
, count(*) -1 switches
from hr.employees e
left join hr.job_history j
on j.employee_id = e.employee_id
group by e.first_name, e.employee_id
order by switches desc, e.first_name;
I have also removed some redundant brackets (there are no brackets in join syntax) and the outer keyword which is valid but optional (I have a thing about removing useless clutter). A right outer join is just a left outer join written backwards to confuse everyone, so I have reversed it for human readability.
Related
I try to display the number of reviews which an employee provided and the number of reviews which he received. I try to do something like this, but in my output table (3) I receive incorrect data. I believe that this happens due to the wrong reference:
pr.reviewer_id = e.employee_id
however, if I reference it like this:
pr.employee_id = e.employee_id
then nr_of_reviews_receieved is correct and nr_of_reviews_posted is not, and if I change it back it's vice versa. So, I need in one case to use one reference and in another a different one, but all in one query.
SELECT
e.employee_id,
CONCAT_WS(' ',employee_first_name, employee_last_name) AS full_name,
COUNT(pr.reviewer_id) AS nr_of_reviews_posted,
COUNT(pr.employee_id) AS nr_of_reviews_received
FROM
employee AS e
LEFT JOIN
performance_review AS pr ON pr.reviewer_id = e.employee_id
GROUP BY
employee_first_name, employee_last_name, e.employee_id
You have to sample the tables twice and join to the table each time
for example:
SELECT
e.employee_id,
CONCAT_WS(' ',employee_first_name, employee_last_name) AS full_name,
COUNT(pr.reviewer_id) AS nr_of_reviews_posted,
COUNT(pr2.employee_id) AS nr_of_reviews_received
FROM
employee AS e
LEFT JOIN
performance_review AS pr ON pr.reviewer_id = e.employee_id
LEFT JOIN
performance_review AS pr2 ON pr2.employee_id = e.employee_id
GROUP BY
Necessary columns ...
How would you rewrite the following query into one without subquery as much as possible?
Select dept name,
(Select Count(*)
From instructor
Where department.dept name = instructor.dept name
) As num_instructors
From department;
I came up with the following. Is it a good equivalence to the above?
Select dept name, count(*)
From department, instructor
Where department.dept name = instructor.dept name
Group By department.dept_name;
Thanks.
The proper way to write the query uses explicit JOIN syntax:
select d.dept_name, count(i.dept_name)
from department d left join
instructor i
on d.dept_name = i.dept_name
group by d.dept_name;
If you only care about departments that have at least one instructor, then no join is necessary at all:
select i.dept_name, count(*)
from instructor i
group by i.dept_name;
Your attempt is really close, just a couple things..
You should use explicit joins (ie. JOIN, LEFT JOIN etc.) instead of implicit joins (commas in the FROM clause). Implicit joins are 25+ years depreciated.
Also, in this case you will want a LEFT JOIN or no departments will be displayed that don't have instructors. LEFT JOIN will retain departments without instructors and give you a 0 count (like the first query), where a JOIN would not display those at all.
SELECT d.dept_name, COUNT(i.dept_name) as num_instructors
FROM department d
LEFT JOIN instructors i on d.dept_name = i.dept_name
GROUP BY d.dept_name
Link To Data Model That I need to use for this question. Check my sql code.
(source: databasejournal.com)
My Task for my self is to find The total number of employees of each job type in a particular location (i.e. the location ID or city should be input by the user).
The job title, city location and country name should also be included.
My Code That I am trying to work on. It's not working though I try many things but showing different errors such as Column ambiguously defined, no group by allowed here, not a valid identifier. I am using oracle apex to do everything.
SELECT job_title, city,country_name,location_id, count(j.job_id) "number of employees"
FROM hr.employees, hr.jobs, hr.locations, hr.countries, hr.departments
WHERE employees.job_id = jobs.job_id
AND Countries.country_id = Locations.country_id
AND Locations.location_id = departments.location_id
GROUP BY job_title, city, country_name, location_id;
You should consider using the 21st-century version
of the JOIN command, because it's far
easier to use and troubleshoot.
You should also consider qualifying all your
column names by table to disambiguate them.
Try this:
SELECT COUNT(DISTINCT e.employee_id) "number of employees",
j.job_title,
l.city,
c.country_name,
l.location_id
FROM hr.employees e
JOIN hr.departments d ON e.department_id = d.department_id
JOIN hr.locations l ON d.location_id = l.location_id
JOIN hr.countries c ON l.country_id = c.country_id
JOIN hr.jobs j ON e.job_id = j.job_id
GROUP BY j.job_title, l.city, c.country_name, l.location_id
Notice the COUNT(DISTINCT e.employee_id) item in the SELECT clause. This makes it
clear to another programmer that you're counting employees in your aggregate query. You
can replace that with COUNT(*) and get the same exact results.
If you wish to report on a subset of your employees, you can insert a WHERE clause immediately before the GROUP BY clause. For example, you could do this:
WHERE l.city = 'New York' AND c.country_name = 'US'
But of course you need to know the city names and country names actually appearing in your data to get this right.
You could also append WITH ROLLUP to the end of your GROUP BY clause. If you do that, change the order of your fields, using this version of GROUP BY.
GROUP BY ROLLUP(c.country_name, l.city, l.location_id, j.job_title)
I'm trying to write a query that will give me all the info from my departments table and join with employees table to get the names of the managers of all departments. I can get them except for one department with no manager, and for that I need to print out "no manager". I have tried using nvl and to_char in a WHERE clause but I don't think I am writing it correctly.
Here is the code I have written:
SELECT d.department_id,d.DEPARTMENT_NAME,d.LOCATION_ID,d.MANAGER_ID,
e.first_name||' '||e.last_name AS Manager
FROM departments d
JOIN employees e ON d.MANAGER_ID = e.employee_ID
WHERE NVL(TO_CHAR(d.MANAGER_ID),'No Manager');
When i run it without the WHERE clause, I get the correct output except for that one missing department.
What you need is a OUTER JOIN.
select d.department_id,
d.department_name,
d.location_id,
coalesce(d.manager_id, 'No Manager'),
coalesce(e.first_name||' '||e.last_name, 'No Manager') as manager
from departments d
left outer join
employees e
on d.manager_id = e.employee_id;
With a inner join, you will get only those departments which have a manager.
LEFT OUTER JOIN ensures the query returns all records from the table on the left, i.e departments.
COALESCE is similar to NVL and is standard across different DBMS, while NVL is Oracle specific.
Table DEPARTMEnT
TABLE EMPLOYEE
There is the Operations Department which has not any employee. So, i believed that the query would retrieved also the row(image 1):
Department_ID=10 , Department_Name =Operations, Employee=0
Why doesnt happen???
SELECT EMPLOYEE.Department_ID, DEPARTMENT.Department_Name, Count(*) AS Employees
FROM EMPLOYEE right JOIN DEPARTMENT ON DEPARTMENT.Department_ID = EMPLOYEE.Department_ID
GROUP BY DEPARTMENT.Department_Name,.EMPLOYEE.Department_ID
Since the principal data you care about for this query is coming from the DEPARTMENT table, you may want to consider rewriting your query to be:
SELECT DEPARTMENT.Department_ID, DEPARTMENT.Department_Name, Count(EMPLOYEE.Employee_ID) As Employees
FROM DEPARTMENT
LEFT JOIN EMPLOYEE ON EMPLOYEE.Department_ID = DEPARTMENT.Department_ID
GROUP BY DEPARTMENT.Department_ID, DEPARTMENT.Department_Name
The default join is an inner join, which only returns rows for which at least one row is found on both sides. Replace join with left join to retrieve departments without employees.
Example code:
SELECT e.Department_ID
, d.Department_Name
, count(e.Employee_ID) AS Employees
FROM Department d
LEFT JOIN
Employee e
ON d.Department_ID = e.Department_ID
GROUP BY
d.Department_ID
, d.Department_Name
This should do the trick. You could put in a RIGHT JOIN if you have the EMPLOYEE table first, but the reason this is not good is because soon your queries will start being a mix of LEFT and RIGHT joins, which becomes very hard to read, even for seasoned SQL professionals. By sticking with LEFT JOIN you keep the query maintainable and understandable. (In very rare circumstances RIGHT JOIN may simplify a query that has a complex order of precedence but I have only done it something like twice to avoid having to add parentheses around groups of joins).
SELECT
D.Department_ID,
D.Department_Name,
Employees = Count(*)
FROM
dbo.DEPARTMENT D
LEFT JOIN dbo.EMPLOYEE E
ON D.Department_ID = E.Department_ID
GROUP BY
D.Department_ID,
D.Department_Name
Also, I recommend that you use aliases for your tables instead of full table names. The query becomes much easier to scan and understand when there is consistent use of aliases. Spelling out the entire table name all too often obscures other parts of the query.