How to do Basic Oracle Sql Commands task Using HR Data Model - sql

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)

Related

Writing sql without aggregation function

I want to write a SQL query for the problem as defined below, I am not sure about the answer, can anyone help me? Is the answer correct, or if not, how can I improve it?
I have used aggregation function, how can I improve it, to write an sql without aggregation function?
Let us consider the following relational schema about physicians and departments:
PHYSICIAN (PhysicianId, Name, Surname, Specialization, Gender, BirthDate, Department);
Let every physician be univocally identified by a code and characterized by a name, a surname, a specialization (we assume to record exactly one specialization for each physician), a gender, a birth date, and the relative department (each physician is assigned to one and only one department).
DEPARTMENT (Name, Building, Floor, Chief)
Let every department be univocally identified by a name and characterized by its location (building and floor) and a chief.
Let us assume that a physician can be the chief of at most one department (the department he/she belongs to). We do not exclude the possibility for two distinct departments to be located at the same floor of the same building.
I want to formulate an SQL query to compute the following data (exploiting aggregate functions only if they are strictly necessary):
the departments which have no male physicians and with at least two physicians whose home city is Venice.
My answer is as below:
select d.name
from department d
where d.name in (select p.department from physician p where p.gender =! 'Male')
and d.name in (select p.department from physician p
where HomeCity = 'Venice'
group by p.PhysicianId
having count > 2)
Or:
select d.*
from department d
inner join physician p on d.name=p.department and
p.gender=!"Male"
left join physician o where d.name=o.department and o.birthdate='venice'
groupby birthdate
having sum(o. physicianID) >2
Assuming that both the conditions should apply at the same time, I'd say your answer is correct. I would modify it just a bit:
select d.name
from department d
where not exists (select 1 from physician p where p.gender = 'Male' and p.Department = d.name)
and d.name in (select p.department from physician p
where HomeCity = 'Venice'
group by p.department, p.HomeCity
having count >= 2)
If you go with the inner join solution, you will have to apply a distinct to your select, which will make your query slower.
The not exists clause I propose, will create an execution plan, similar to inner join. That is you will not need to compare each name with all the names of the departments without Male employees.
You can't avoid the aggregate function if you want to count something. There is a workaround with Over in TSQL but I would not recommend it.
Despite conditions are different the main idea is the same as in improving the written sql query.
You need to use the same approach but substitute your own join and where conditions.
Update
Here is a version for your case:
select distinct d.*
from departments d
left join physicians m on d.name = m.department and m.gender = 'Male'
inner join physicians v1 on d.name = v1.department and v1.homecity = 'Venice'
inner join physicians v2 on d.name = v2.department and v2.homecity = 'Venice' and v2.id <> v1.id
where m.id is null

Finding the number of job switches

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.

How can we rewrite a query with a subquery in SELECT clause?

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

Count the number of employees for every country

I have this task:
Count the number of employees for every country. Show only those countries, when works more than 20 employees
employee_id is dedicated for Employees table
country belongs to different table - Countries table and we need country_name from this table
I have no idea how to solve this task. Below what I was able to create. I think we should use Inner Join.
SELECT a.employee_id
, b.country_name
, COUNT(a.employee_id) AS count
FROM employees a
INNER JOIN countries b ON a.employee_id = b.country_name
GROUP BY b.country_name
WHERE employee_id >20;
I think I need help from the beginning.
Thanks
Your join doesn't seem correct but as I don't know the table structure, I can't say what the right column is (I'm going to assume that it should be country_name. Even so, try this:
SELECT b.country_name
, COUNT(a.employee_id) AS count
FROM employees a
INNER JOIN countries b ON a.country_name = b.country_name
GROUP BY b.country_name
HAVING COUNT(employee_id) >20;
When grouping you need to use the HAVING statement to filter.

sql query null data was not retrieved

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.