Group by with join condition - sql

I've been asked to create a view with full outer join of two tabe departments and employees as the following question:
Create a new view name as fullview. Get the following column: MIN(LASTNAME) name as name, distinct (manager_id) rename as “Available Manager_id”, count (manager_id) rename as “No. of Manager_id”, and the sum(salary) per job_id. Apply Full Outer Join
Since it's been asked to sum(salary) per job_id ,I think need to be used group by but I donot know how.I would very appreciate that somebody can help me and fix my following statement regarding the above question:
CREATE VIEW FULLVIEW
AS (
SELECT MIN(LASTNAME) AS NAME,
DISTINCT(E.MANAGER_ID) AS "Available Manager_id",
COUNT(E.MANAGER_ID) AS "No.of Manager_id",
JOB_ID,
sum(salary)
FROM EMPLOYEES E
FULL OUTER JOIN DEPARTMENTS D
ON E.DEPARTMENT_ID = D.DEPARTMENT_ID
GROUP BY JOB_ID);

One possible and correct answer that I found for the question is as following answer:
CREATE VIEW fullview
AS
( SELECT DISTINCT (d.manager_id) AS "Available Manager_id",
MIN (lastname) AS name,
COUNT (d.manager_id) AS "No.of Manager_id",
job_id,
SUM (salary) AS salary
FROM employees e
FULL OUTER JOIN departments d ON e.manager_id = d.manager_id
GROUP BY e.job_id, d.manager_id);

Related

SQL in Oracle HR Schema

I have made a query in Oracle HR schema to see the following information:
The city where the department is located
The total number of employees in the department
However, the query cannot be executed correctly and said this is "not a GROUP BY expression".
Does anyone knows what's the problem is? Thanks in advance.
SELECT department_name, city, COUNT(employees.department_id)
FROM departments
JOIN employees on (departments.department_id=employees.department_id)
JOIN locations USING (location_id)
GROUP BY department_name;
You are grouping by department and want to show the department's city. You expect this to work, because each department is in exactly one city. (SQL people call this functional dependency.)
For this to work, ...
there would have to be a unique contraint on the department name or you'd have to group by department_id instead
the DBMS must detect and support functional dependency in aggregation queries
Unfortunately, Oracle doesn't support functional dependency in aggregation queries. It forces us to put every such column in the GROUP BY clause or into an aggregation function.
So either extend the GROUP BY clause:
SELECT d.department_name, l.city, COUNT(e.department_id)
FROM departments d
JOIN employees e ON e.department_id = d.department_id
JOIN locations l USING (location_id)
GROUP BY d.department_name, l.city
ORDER BY d.department_name;
or use some aggregation function as MIN or MAX on that single value.
SELECT d.department_name, MAX(l.city) AS city, COUNT(e.department_id)
FROM departments d
JOIN employees e ON e.department_id = d.department_id
JOIN locations l USING (location_id)
GROUP BY d.department_name
ORDER BY d.department_name;
What I prefer though, is to aggregate first and only then join. You want to join the departments with their employee count, so do just that:
SELECT d.department_name, l.city, COALESCE(e.cnt, 0) AS employee_count
FROM departments d
JOIN locations l USING (location_id)
LEFT JOIN
(
SELECT department_id, COUNT(*) as cnt
FROM employees
GROUP BY department_id
) e ON e.department_id = d.department_id
ORDER BY d.department_name;
The problem is you have both aggregated and non-aggregated column (in your case city in the select list.
As I don't know the structure of location table and considering a department have only one location defined you can use max(city),
SELECT department_name, max(city) city, COUNT(employees.department_id) no_of_employees
FROM departments
JOIN employees on (departments.department_id=employees.department_id)
JOIN locations USING (location_id)
GROUP BY department_name;
As excellently explained by Thorsten, you could also group the data using OVER and PARTITION BY function which would eliminate the use of GROUP BY function.
SELECT d.department_name, l.city, COUNT(e.department_id) OVER (PARTITION BY e.department_id) as emp_count
FROM departments d
JOIN employees e ON e.department_id = d.department_id
JOIN locations l USING (location_id)
ORDER BY d.department_name;

Sql subquery with a group by and a join on two tables

So i have two tables
EMPLOYEE- Contains columns including EMPLOYEE_NAME, DEPARTMENT_ID and SALARY
DEPARTMENTS - Contains columns including DEPARTMENT_NAME, and DEPARTMENT_ID
I need to display the department name and the average slary for each department and order it by the average salaries.
I am new to DBs and am having trouble.
I try to do a subquery in the from field ( this subquery returns exactly what i need minus the department name which requires me to then join the departments table to the results) all the data in the subquery is in one table- employees. while department name is in the departments table.
here is what i tried.
SELECT D.DEPARTMENT_NAME, T.PERDEPT
FROM
(
SELECT DEPARTMENT_ID, AVG(SALARY) AS PERDEPT
FROM EMPLOYEE
GROUP BY DEPARTMENT_ID
ORDER BY PERDEPT
) AS TEST T
JOIN DEPARTMENTS
ON D.DEPARTMENT_ID=T.DEPARTMENT_ID;
This returns a
SQL command not properly terminated
on the line with the AS TEST T
any and all help is greatly appreciated
many thanks
This query should do what you ask:
select d.department_name, avg(e.salary) as avg_salary
from salary_department d
left join employee e on e.department_id = d.department_id
group by d.department_name
order by avg(e.salary)
Simply correct your table aliases as you seem to have two aliases for subquery (TEST and T) and no assignment for D. Adjust SQL with one alias for each table/query reference:
...
(
SELECT ...
) AS T
JOIN DEPARTMENTS D
With that said, you do not even need the subquery as aggregate query with JOIN should suffice, assuming DEPARTMENT_ID is unique in DEPARTMENTS table to not double count the aggregate.
SELECT D.DEPARTMENT_NAME,
AVG(E.SALARY) AS PERDEPT
FROM EMPLOYEE E
JOIN DEPARTMENTS D
ON E.DEPARTMENT_ID = D.DEPARTMENT_ID
GROUP BY E.DEPARTMENT_ID,
D.DEPARTMENT_NAME
ORDER BY AVG(SALARY)

Subquery using 3 Tables SQL

I'm trying to display the last name of the lowest paid employees from each city. The city column falls under a table titled LOCATIONS while employee information(salary, last name) falls under EMPLOYEES. Both of these tables are related share no common table, so I have to rely on a third table, DEPARTMENTS to connect the two as DEPARTMENTS contains a department_id that it shares with EMPLOYEES as well as a LOCATION_ID that it shares with LOCATIONS. This is what I have so far, but I'm having trouble with this as I've mostly worked with only two tables in the past.
SELECT LAST_NAME
FROM EMPLOYEES
WHERE (DEPARTMENT_ID) IN
(SELECT DEPARTMENT_ID
FROM DEPARTMENTS
WHERE LOCATION_ID IN
(SELECT LOCATION_ID
FROM LOCATIONS
GROUP BY CITY
HAVING MIN(SALARY)));
This seems to be an assignment in an intro course in SQL. So let's assume you can't use analytic functions, match_recognize clause, etc. Just joins and aggregates.
In the subquery in the WHERE clause below, we compute the min salary for each city. We need to join all three tables for this. Then in the overall query we join the three tables again, and we use the subquery for an IN condition (a semi-join). The overall query looks like this:
select e.last_name
from employees e join departments d
on e.department_id = d.department_id
join locations l
on d.location_id = l.location_id
where ( e.salary, l.city ) in
(
select min(salary), city
from employees e join departments d
on e.department_id = d.department_id
join locations l
on d.location_id = l.location_id
group by city
)
;
You should separate out the concept of table joins from WHERE clauses.
Use WHERE for filtering data, use JOIN for connecting data together.
I think this is what you are wanting. By the way, lose the ALL CAPS if you can.
SELECT
LAST_NAME
FROM
EMPLOYEES
INNER JOIN (
SELECT
DEPARTMENTS.DEPARTMENT_ID,
CITY,
MIN(SALARY) AS LOWEST_SALARY
FROM
EMPLOYEES
INNER JOIN DEPARTMENTS ON EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
INNER JOIN LOCATIONS ON DEPARTMENTS.LOCATION_ID = LOCATIONS.LOCATION_ID
GROUP BY
DEPARTMENTS.DEPARTMENT_ID,
LOCATIONS.CITY
) AS MINIMUM_SALARIES
ON EMPLOYEES.DEPARTMENT_ID = MINIMUM_SALARIES.DEPARTMENT_ID
AND EMPLOYEES.SALARY = MINIMUM_SALARIES.LOWEST_SALARY
First of all join the tables, so you see city and employee in one row. If we group by city we get the minimum salary per city.
with city_employees as
(
select l.city, e.*
from locations l
join departments d using (location_id)
join employees e using (department_id)
)
select last_name
from city_employees
where (city, salary) in
(
select city, min(salary)
from city_employees
group by l.city
);
It is easier to achieve the same, however, with window functions (min over or rank over here).
select last_name
from
(
select
e.last_name,
e.salary,
min(e.salary) over (partition by l.city) as min_salary
from locations l
join departments d using (location_id)
join employees e using (department_id)
)
where salary = min_salary;

Include rows that contain null values in a particular column with inner join

The goal here is to return the ID, Name, and Manager Name and ID for each Employee. The table does not contain a manager name, only a manager ID for a given employee. The current query works great using an inner join, except that one employee does not have a manager ID (he's the boss), and so none of his information appears. The current query looks like this:
SELECT DISTINCT e.employee_id AS EMPLOYEE_ID,
e.FULL_NAME AS EMPLOYEE_NAME,
m.manager_ID AS REPORTS_TO,
m.FULL_NAME AS MANAGER_NAME
FROM EMPS e
INNER JOIN EMPS m ON e.manager_id = m.employee_id;
How can I include the name and information for this employee despite his lack of a seemingly necessary field? Thanks
If you want to include the employee name when there is no manager do a left join:
SELECT DISTINCT e.employee_id AS EMPLOYEE_ID,
e.FULL_NAME AS EMPLOYEE_NAME,
m.manager_ID AS REPORTS_TO,
m.FULL_NAME AS MANAGER_NAME
FROM EMPS e
LEFT JOIN EMPS m ON e.manager_id = m.employee_id;

How to add subquery as a column in SQL

How do I add a subquery as a column in my SQL script?
e.g.
Select emp_no, name,gender ,
(select department_name from departments where employees.emp_no = departments.emp_no)
from employees
PS: I'm using oracle 8
Going by the semantics, what I understand is that you want an employee's department name to be shown alongside his/her other information. I would suggest you do a join instead:
Select emp_no, name, gender, department_name
from employees emp, departments dept
where emp.emp_no = dept.emp_no;
That looks reasonably sound, I would suggest some (possible typos) cleaning up: add a comma after "gender" and declare the table names, also set the subquery alias
Select employees.emp_no, employees.name, employees.gender,
(select departments.department_name from departments where employees.emp_no = departments.emp_no) as dept_name
from employees
Alternatively, a nice join would would work too, if the other data is feasible:
Select employees.emp_no, employees.name, employees.gender, departments.department_name
from employees
inner join departments on employees.emp_no = departments.emp_no
left join is the best-practice, and should be faster in performance:
Select e.emp_no, e.name, e.gender , d.department_name
from employees e left join departments d on e.emp_no = d.emp_no;
You seem to be missing comma after gender.
Select emp_no, name,gender ,
(select department_name from departments where employees.emp_no = departments.emp_no) as dept_name from employees
The below is what you need. Just added a comma after gender. This subquery would need to return only one row for each result as well or else an error will be seen.
Select emp_no, name,gender,
(select department_name from departments where employees.emp_no = departments.emp_no)
from employees
This query is your answer but it will work only if there is one column mentioned in that if we use more than one column than it will retrun an error .
"Select employee_id,first_name,
(select department_name,manager_id from departments where employees.department_id = departments.department_id) as new_column
from employees;"
can you try this:
SELECT em.emp_no, em."name",em.gender ,
(SELECTdistinctdp.department_name
FROM departments dp
WHERE em.emp_no = dp.emp_no) my_sub
FROM employees em