I have an issue in executing the below database query.
I am using Oracle 11g Enterprise Edition
Query 1:
SELECT d.department_id, max(salary), min(salary), avg(salary), count(*) no_of_employees
FROM departments d, employees e
WHERE e.department_id = d.department_id
GROUP BY d.department_id
Result: successful output
Query 2:
SELECT d.department_id, d.department_name, max(salary), min(salary), avg(salary), count(*) no_of_employees
FROM departments d, employees e
WHERE e.department_id = d.department_id
GROUP BY d.department_id
Result:
ORA-00979: not a GROUP BY expression
Can anybody help me out with this issue?
Please let me know what is wrong with this expression.
you must also group by department_name
You need to GROUP BY all the columns that you don't have an aggregation function (MAX, COUNT, etc). Therefore:
select d.department_id
, d.department_name
, max(salary), min(salary), avg(salary) , count(*) no_of_employees
from departments d, employees e
where e.department_id = d.department_id
group by d.department_id, d.department_name;
But you should also consider doing an ANSI join instead:
select department_id
, department_name
, max(salary), min(salary), avg(salary) , count(*) no_of_employees
from departments
join employees USING (department_id)
group by department_id, department_name;
Related
I am using an Oracle Developer Database and I have the two tables, Employees and Departments :
I am supposed to find (using a correlated subquery) for each department all the employees, which have a salary within the range of the salary-standard deviation of every single department.
This is what I have tried so far:
SELECT d.department_name, e.employee_id, e.last_name, AVG(e.salary), e.salary
FROM hr.employees e
JOIN hr.departments d
on e.department_id = d.department_id
WHERE e.salary IN
(SELECT salary
FROM hr.employees
WHERE salary >
(SELECT ROUND(AVG(e.salary)-STDDEV(e.salary),2)
FROM hr.employees) AND salary < (SELECT ROUND(AVG(e.salary) + STDDEV(e.salary),2) FROM hr.employees)
GROUP BY d.department_name,
e.employee_id, e.last_name, e.salary;
Even though this query has the correct syntax, it does not show any result.
This other (partial) approach works but how can I query at the same time the employee table?
SELECT d.department_name, e.department_id, ROUND(AVG(e.salary) + STDDEV(e.salary), 2)
AS standard_deviation_max,
ROUND(AVG(e.salary) - STDDEV(e.salary), 2)
AS standard_deviation_min
FROM hr.employees e
JOIN hr.departments d
on e.department_id = d.department_id
GROUP BY department_name, e.department_id;
Since I am new to SQL, I would really appreciate any hint. Thank you in advance
You can use your query to find the employee details and then correlate the salary to the average ± the standard deviation:
SELECT d.department_name,
e.employee_id,
e.last_name,
e.salary
FROM hr.employees e
JOIN hr.departments d
on e.department_id = d.department_id
WHERE EXISTS (
SELECT 1
FROM hr.employees x
HAVING e.salary BETWEEN AVG(e.salary) + STDDEV(e.salary)
AND AVG(e.salary) + STDDEV(e.salary)
);
or, you can use analytic functions:
SELECT department_name,
employee_id,
last_name,
salary
FROM (
SELECT d.department_name,
e.employee_id,
e.last_name,
e.salary,
AVG(e.salary) OVER () AS avg_salary,
STDDEV(e.salary) OVER () AS stddev_salary
FROM hr.employees e
JOIN hr.departments d
on e.department_id = d.department_id
)
WHERE salary BETWEEN avg_salary - stddev_salary
AND avg_salary + stddev_salary;
select e.department_id,e.salary,d.department_name
from employees e
join departments d
on d.department_id=e.department_id
inner join
(
select department_id,max(salary) as max_sal from employees
group by department_id
) as t
on e.department_id=t.department_id
where e.salary =t.max_sal;
Oracle does not support as for table aliases, so you can try:
select e.department_id, e.salary, d.department_name
from employees e join
departments d
on d.department_id = e.department_id inner join
(select department_id, max(salary) as max_sal
from employees
group by department_id
) t
on e.department_id = t.department_id
where e.salary = t.max_sal;
Of course, this would be better written using window functions, but this answers the question that you asked.
I have 2 tables, Departments and Employees. I want to display the department_id, department_name, and the number of employees in any department that has fewer than 4 employees.
Here's the code that I'm using (I use SQL developer, btw):
select d.department_id, d.department_name, count(e.last_name)
from departments d, employees e
where e.last_name < 4
group by d.department_id, d.department_name;
However, I'm getting a invalid number error. What is the correct way to do this?
Something like this would make more sense:
SELECT d.department_id,
d.department_name,
COUNT(*) AS numEmployees
FROM departments d
INNER JOIN employees e
ON d.department_id = e.department_id
GROUP BY d.department_id,
d.department_name
HAVING COUNT(*) < 4
I have some problems trying to print department_id, department_name and the sum of salaries of each department and I can't figure out why. I get the error: 00979 - "not a GROUP BY expression"
SELECT d.department_id, d.department_name, SUM(e.salary)
FROM departments d, employees e
WHERE d.department_id = e.department_id
GROUP BY d.department_id;
Just add the name to the group by expression. Along the way, also fix the query to use explicit join syntax:
SELECT d.department_id, d.department_name, SUM(e.salary)
FROM departments d JOIN
employees e
ON d.department_id = e.department_id
GROUP BY d.department_id, d.department_name;
Although you didn't ask, I will point out that your version of the query does make sense and is ANSI-compliant (although most databases don't support this feature). You are aggregating by a primary key, so bringing in additional columns is allowed -- although Oracle does not support this feature.
SELECT d.department_id, d.department_name, SUM(e.salary)
FROM departments d, employees e
WHERE d.department_id = e.department_id
GROUP BY d.department_id, d.department_name;
Just needed to add the department_name column in your group by clause.
Alternately, you can aggregate the department name as well using MIN() or MAX():
SELECT d.department_id, MAX(d.department_name) AS department_name
, SUM(e.salary) AS department_salary
FROM departments d INNER JOIN employees e
ON d.department_id = e.department_id
GROUP BY d.department_id;
Note that I updated your syntax from the old ANSI standard to the newer one. If you prefer the older syntax (as I do), then just use this:
SELECT d.department_id, MAX(d.department_name) AS department_name
, SUM(e.salary) AS department_salary
FROM departments d, employees e
WHERE d.department_id = e.department_id
GROUP BY d.department_id;
I'm trying to print the department names that have the sum of all salaries bigger than the average sum on departments.
SELECT d.department_name, SUM(e.salary)
FROM departments d, employees e
WHERE d.department_id = e.department_id
GROUP BY d.department_name
HAVING SUM(e.salary) > (SELECT AVG(SUM(salary)) from employees);
In the second select, after what do I have to group by AVG(SUM(salary))?
You need to repeat the first query in the condition. This can be done with the WITH clause.
WITH dept_sums AS (SELECT d.department_name, SUM(e.salary) sum_salary
FROM departments d, employees e
WHERE d.department_id = e.department_id
GROUP BY d.department_name)
SELECT * FROM dept_sums d_s_1 WHERE d_s_1.sum_salary > (SELECT AVG(sum_salary) FROM dept_sums d_s_2);
This is where window (analytic) functions come in handy. Below I am using AVG() as an analytic function to calculate the average total salary across all departments.
SELECT department_name, dept_salary FROM (
SELECT d.department_name, SUM(e.salary) AS dept_salary
, AVG(SUM(e.salary)) OVER ( ) AS avg_dept_salary
FROM departments d INNER JOIN employees e
ON d.department_id = e.department_id
GROUP BY d.department_name
) WHERE dept_salary > avg_dept_salary;