Oracle error: ORA-00979: not a GROUP BY expression - sql

Following is the result of my first query (in Oracle). Totals is the total number of employees in each department (so Employee 1-5 are on department#1, employees 6-8 on department#2 etc.)
select a.name, (select count(*) from employee b where a.dname = b.dname group by b.dname) as totals
from employee a;
NAME TOTALS
------------------------------ ----------------
Employee 1 5
Employee 2 5
Employee 3 5
Employee 4 5
Employee 5 5
Employee 6 3
Employee 7 3
Employee 8 3
Employee 9 4
Employee 10 4
Employee 11 4
Employee 12 4
And here is my second query on which employer has less taskhours than 5 (note "taskhours" is a different field than "totals" in the database)
select a.name
from job o, employee a, works w , task t
where w.name=a.name and w.menuid=o.menuid and w.worktype='office' and t.fname = o.fname
group by a.name
having sum(t.taskhours) <5
;
NAME
------------------------------
Employee 3
I want now combine the above, i.e query for the employees who have fewer task-hours than the total employees in the department. I am trying this query
select a.name
from job o, employee a, works w , task t
where w.name=a.name and w.menuid=o.menuid and w.worktype='office' and t.fname = o.fname
group by a.name
having sum(t.taskhours) <(select count(*) from employee b where a.sname = b.sname group by b.sname)
but I get the error:
ERROR at line 5:
ORA-00979: not a GROUP BY expression
Can someone help me find the right query?
Sample data for 1st query:
NAME DNAME
------------------------------ ------------------------------
Employee 1 Dep1
Employee 2 Dep1
Employee 3 Dep1
Employee 4 Dep1
Employee 5 Dep1
Employee 6 Dep2
Employee 7 Dep2
Employee 8 Dep2
Employee 9 Dep3
Employee 10 Dep3
Employee 11 Dep3
Employee 12 Dep3

We can phrase this by putting each of your two queries into subqueries, and then joining them together:
SELECT
e.name,
e.dname,
t1.emp_cnt,
t2.hours_cnt
FROM employee e
LEFT JOIN
(
SELECT dname, COUNT(*) AS emp_cnt
FROM employee
GROUP BY dname
) t1
ON e.dname = t1.dname
LEFT JOIN
(
SELECT a.name, SUM(t.taskhours) AS hours_cnt
FROM works w
INNER JOIN employee a
ON w.name = a.name
INNER JOIN job o
ON w.menuid = o.menuid
INNER JOIN task t
ON t.fname = o.fname
WHERE w.worktype = 'office'
GROUP BY a.name
) t2
ON e.name = t2.name
WHERE
t1.emp_cnt < t2.hours_cnt;

You can simplify the first query as follows
use that as a cte block
with tot_count
as (select dept_id,count(emp_id) as dept_cnt
from employee
group by dept_id)
,less_than_five
as (select a.name
,sum(t.taskhours) as sum_taskhours
,max(a.dept_id) as dept_id_of_emp
from job o,
employee a,
works w ,
task t
where w.name=a.name
and w.menuid=o.menuid
and w.worktype='office'
and t.fname = o.fname
group by a.name
)
select *
from less_than_five a
join tot_count b
on a.dept_id_of_emp=b.dept_id
where less_than_five.sum_taskhours<=b.dept_cnt

Related

How to Query Hierarchical Data in SQL and determine height of each user

I have an Azure SQL table containing all the employees in a company. I use the below query to determine the height of each employee:
WITH emp AS (
SELECT *, 0 AS d
FROM tableA
WHERE Email = '<email>'
UNION ALL
SELECT e.*, emp.d + 1
FROM tableA e INNER JOIN emp
ON e.ManagerId = emp.Id
)
SELECT max(d)
FROM emp e
Is there a more efficient way to determine the height all the employees in the company without recursively running the above query for each employee?
UPDATE:
Here is my table structure:
Id ManagerId Email
1 -1 CEO
2 1 ABC
3 1 DEF
4 2 PQR
5 2 STU
6 3 EPS
7 3 DMN
Is there a more efficient way to determine the height all the employees in the company without recursively running the above query for each employee?
You can do it all with a single recursive query if you start with the top-level employees and recurse down their reporting hierarcies, something like:
WITH emp AS (
SELECT *, 0 AS d
FROM tableA
WHERE ManagerId is null
UNION ALL
SELECT e.*, emp.d + 1
FROM tableA e INNER JOIN emp
ON e.ManagerId = emp.Id
)
SELECT e.Id, e.ManagerId, max(e.d) over () - e.d depth
FROM emp e

How to write insert function or query

I have three tables which are very extensive, but I will give only a small version:
employee_position :
ID
position
1
Programmer
2
Administrator
3
Analyst
employee:
ID
name
1
Adam Smith
2
Sam Jones
3
Barbra Streisand
4
Dorothy Brown
employee_functions:
ID
employee_id
employee_position
1
1
3
2
3
1
Assuming what I need: if there is no employee in the employee_functions table [from the employee table] with an assigned position, then all positions in the employee_positions table should be assigned to him in this table.
I admit that I am not very fluent in SQL, so I would ask for help, can it be done with one query or can I do some function?
The final result for the given data (employee_functions):
ID
employee_id
employee_position
1
1
3
2
3
1
3
2
1
4
2
2
5
2
3
6
4
1
7
4
2
8
4
3
here is one way :
select employee_id ,employee_position from employee_functions f
union all
select e.id, p.id from employees e
cross join employee_position p
where e.id not in (select employee_id from employee_functions)
You can use insert . . . select, filtering out the employees who already have functions:
insert into employee_functions (employee_id, position_id)
select e.id, p.id
from employee e cross join
employee_positions ep
where not exists (select 1
from employee_functions ef
where ef.employee_id = e.id
);
insert into employee_functions (employee_id, position_id)
select e.id, p.id
from employee e cross join
employee_positions ep
where not exists (select 1
from employee_functions ef
where ef.employee_id = e.id
);

SQL: Retrieve all records where has all joined

Sorry, couldn't think of a better title.
I have 3 tables in Oracle XE. An EMPLOYEE table a PROJECT table and a WORK_ON table. An EMPLOYEE can WORK_ON many PROJECTs. I am trying to get the employee name who is working on all the projects.
EMPLOYEE Table
Emp_ID EMP_Name
1 Esther
2 Peter
3 Joan
4 Roger
5 Liam
PROJECT Table
Project_ID
1
2
3
WROKS_ON Table
Emp_ID Project_ID
1 3
2 1
2 2
2 3
3 1
3 2
4 1
4 2
4 3
Given the fields my result should be Peter and Roger.
Started with the following, but got stuck:
SELECT EMP_NameLOYEE.E_NAME
FROM EMP_NameLOYEE INNER JOIN
(PROJECT INNER JOIN WROKS_ON ON PROJECT.Project_ID = WROKS_ON.Project_ID) ON
EMP_NameLOYEE.Emp_ID = WROKS_ON.Emp_ID
WHERE WROKS_ON.Project_ID In (SELECT DISTINCT Project_ID FROM PROJECT);
Obviously this retrieves all the names of the employees that are working on each project duplicated, but not exactly what I want.
You can leave the project table out of it.
SELECT e.emp_id, COUNT(project_id)
FROM employee e
INNER JOIN works_on wo ON wo.emp_id = e.emp_id
GROUP BY e.emp_id
HAVING COUNT( project_id ) = (SELECT COUNT(*) FROM project);
SQL Fiddle
You need to generate all combinations of employees and projects with a cross join and left join the works table and check for row counts for each e_name.
SELECT e.E_NAME
FROM EMPLOYEE e
CROSS JOIN PROJECT p
LEFT JOIN WORKS_ON w ON p.Project_ID = w.Project_ID and e.emp_id=w.emp_id
GROUP by e.E_NAME
HAVING COUNT(*)=COUNT(w.project_id)

Get the output based on column in the same table

I have a table which looks like this:
Employee table:
Employee_id E_Name Manager_Id
-------------------------------
1 p -
2 q 1
3 r 1
4 s 2
Here column Manager_Id denotes the manager's employee Id. Meaning for p there is no manager. for q and r employee with id 1 is manager (p). and for s employee with emp id 2 is manager.
I want to build a query which will return below result:
Employee_id E_Name Manager_Name
-------------------------------
1 p -
2 q p
3 r p
4 s q
table here has manager name instead of id.
How to achieve above? Thanks in advance
It can easily be done with a LEFT OUTER JOIN:
Try this:
SELECT a.EMPLOYEE_ID, a.E_NAME, b.E_NAME
FROM EMPLOYEE a left outer join EMPLOYEE b
on A.MANAGER_ID = B.EMPLOYEE_ID
you did not mentioned the database provider, but for MySQL, it is:
SELECT e.employee_id, e.e_name,COALESCE(m.e_name,'-') AS Manager_Name
FROM employee e
LEFT JOIN employee m ON m.employee_id = e.manager_id
Please try:
select
Employee_id,
E_Name,
(select E_Name from YourTable b where b.Employee_id=a.Manager_Id) as Manager_Name
from YourTable a

List the name of division that has the most employees working on projects

So there are three tables that would be applicable in this statement. The division table, which houses the division name and division id, the workon table which houses the projects and employee ids that correlate to the project, and the employee table that houses the employee id, department id, and name. I'm trying to find the department that has the most employees who work on projects.
This is my code:
select distinct
(dname) as "Division Name"
from
employee e, division d
where
d.did = e.did and
d.did in (
select did from employee where empid in (
select empid from workon having count(pid) >= all(pid)
)
)
I'm supposed to get the answer "human resources" but I cannot seem to get that answer no matter what code I use.
Workon table
PID EMPID HOURS
3 1 30
2 3 40
5 4 30
6 6 60
4 3 70
2 4 45
5 3 90
3 3 100
6 8 30
4 4 30
5 8 30
6 7 30
6 9 40
5 9 50
4 6 45
2 7 30
2 8 30
2 9 30
1 9 30
1 8 30
1 7 30
1 5 30
1 6 30
2 6 30
Employee Table
EMPID NAME SALARY DID
1 kevin 32000 2
2 joan 42000 1
3 brian 37000 3
4 larry 82000 5
5 harry 92000 4
6 peter 45000 2
7 peter 68000 3
8 smith 39000 4
9 chen 71000 1
10 kim 46000 5
11 smith 46000 1
Division
DID DNAME MANAGERID
1 engineering 2
2 marketing 1
3 human resource 3
4 Research and development 5
5 accounting 4
Check this reference out please.
SQLFIDDLE
select d.id, d.name, p.maxcounts
from dept d,
(select we.dep, max(we.counts) as maxcounts
from (select w.eid, count(w.pid) as counts,
e.dep as dep from employee e, workon w
where e.id = w.eid
group by e.dep) as we) as p
where d.id = p.dep
;
RESULTS:
ID NAME MAXCOUNTS
111 human resoruces 5
FOLLOWING is the edit based on your own data:
Reference : SQLFIDDLE_Based_ON_OP_Data
There are three ways you can achieve this. Either use the nested selects, save Max(count) into a variable or order data by desc and limit it to 1.
Method 1:
-- using nested select
--sub query 1 explaining to OP how final answer is derived
select e.dep, count(w.eid) as num_emp
from employee e, workon w
where e.id = w.eid
group by e.dep
order by e.dep
;
-- **results of sub query 1:**
DEP NUM_EMP
1 4
2 5
3 7
4 5
5 3
-- Final nested select query
select ee.dep, dd.name, count(ww.eid)
from employee ee, dept dd, workon ww
where ee.id = ww.eid
and ee.dep = dd.id
group by ee.dep, dd.name
having count(ww.eid) =
(select distinct max(t.num_emp)
from (select e.dep, count(w.eid) as num_emp
from employee e, workon w
where e.id = w.eid
group by e.dep
order by e.dep)as t)
;
-- results using nested selects
DEP NAME COUNT(WW.EID)
3 human resource 7
-- query using a variable
select max(x.num_emp) into #myvar from
(select e.dep, count(w.eid) as num_emp
from employee e, workon w
where e.id = w.eid
group by e.dep) as x
;
select x.dep, x.name, x.num_emp as num_emp from
(select e.dep, d.name, count(w.pid) as num_emp
from employee e, workon w, dept d
where e.id = w.eid
and e.dep = d.id
group by e.dep) as x
where x.num_emp = #myvar
;
-- results using variable
DEP NAME NUM_EMP
3 human resource 7
-- query using limit 1 with ordered desc table
select e.dep, d.name, count(w.eid) as num_emp
from employee e, workon w, dept d
where e.id = w.eid
and e.dep = d.id
group by e.dep
order by num_emp desc
limit 1
-- results using order by desc and limit 1:
DEP NAME NUM_EMP
3 human resource 7
Now when using Method 3, it may or may not matter to you that sometimes there will be two departments with same highest number of employees working in projects. So in that case you may use either nested or variable methods.
*PS I do not have the privilledge to be full time on StackOverFlow, hence sorry for getting back to you late :) *