Multiple Count with different value each record - sql

I have a problem with my query, please help me to solve this problem.
My Query :
SELECT D.DEPTNO,
(SELECT COUNT(DISTINCT P.PROJNO) FROM SCHEMA.PROJECT P, SCHEMA.DEPARTMENT D WHERE P.DEPTNO = D.DEPTNO) AS PROJECT,
(SELECT COUNT(DISTINCT E.EMPNO) FROM SCHEMA.EMPLOYEE E, SCHEMA.DEPARTMENT D WHERE E.WORKDEPT = D.DEPTNO) AS EMPLOYEE
FROM SCHEMA.DEPARTMENT D, SCHEMA.PROJECT P, SCEHMA.EMPLOYEE E GROUP BY D.DEPTNO#
AND HERE THE RESULT :
enter image description here
but it should each row has a different result.
I must show total of project and employee each department, so i group that by deptno, but the result shown all total project and employee
Please help me guys :)

I think you are trying to write this query:
SELECT D.DEPTNO,
(SELECT COUNT(DISTINCT P.PROJNO)
FROM SCHEMA.PROJECT P
WHERE P.DEPTNO = D.DEPTNO
) AS PROJECT,
(SELECT COUNT(DISTINCT E.EMPNO)
FROM SCHEMA.EMPLOYEE E
WHERE E.WORKDEPT = D.DEPTNO
) AS EMPLOYEE
FROM SCHEMA.DEPARTMENT D;
Notes:
You don't need JOINs in the subquery. The correlation clause is sufficient.
You don't need a GROUP BY in the outer query.
You probably don't need the COUNT(DISTINCT), but I'm not sure so I'm leaving it. in.
Never use commas in the FROM clause. Always use explicit JOIN syntax.

When you join tables - do not forget the join predicates!
If I got your requirement right - this might be a possible solution:
select deptno
,count_projects
,count_employees
from (
select deptno
,count(projno) as count_projects
from project
group by deptno ) as p
inner join
(select workdept
,count(*) as Count_employees
from employee
group by workdept) as e
on p.deptno = e.workdept
The table DEPARTMENT is not necessary unless you have to retrieve specific data from this table as well.

Related

ORA-00933 when trying to compare counts

This is the question
List the name of employee who work on more projects than employee 'Grace'', Show three columns in result: name of employee, project count of employee, grace's project count.
This is my code
SELECT employee."NAME", T1."# OF PROJECTS",
(SELECT COUNT(pid) FROM workon WHERE empid = 30) AS "Grace's Project"
FROM employee,
(SELECT empid, COUNT(pid) AS "# OF PROJECTS"
FROM workon
GROUP BY empid
ORDER BY empid)AS T1
WHERE T1."# OF PROJECTS" > (SELECT COUNT(pid) FROM workon WHERE empid = 30)
AND t1.empid = employee.EMPID
I keep getting ORA-00933: SQL command not properly ended. what am I missing?
The only error in your query is that Oracle does not accept AS for table aliases. Remove it and your query runs just fine.
There are two things I'd like to mention, though:
You are using an ancient join syntax you shouldn't use anymore. Comma-separated joins were made redundant by the introduction of explicit joins (e.g. INNER JOIN ... ON) in 1992.
Your query is a little over-complicated. Most of all, because you are counting projects thrice, once for all employees, twice for Grace. You can avoid this by using WITH clauses.
Here is the query built-up step by step with WITH clauses:
WITH emp AS
(
SELECT empid, e.name, COUNT(*) AS projects
FROM workon w
JOIN employee e USING(empid)
GROUP BY empid, e.name
ORDER BY empid
)
, grace AS
(
SELECT * FROM emp WHERE name = 'Grace'
)
SELECT
emp.name,
emp.projects as "# OF PROJECTS",
grace.projects as "Grace's Projects"
FROM emp
CROSS JOIN grace
WHERE emp.projects > grace.projects
ORDER BY emp.projects DESC, emp.name;
Demo: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=f40e2c33541c76f0af112be967370784
I would use window functions:
select ew.name, ew.num_projects
from (select e.empid, e."NAME", count(*) as num_projects,
max(case when ew."NAME" = 'Grace' then count(*) else 0 end) over () as grace_num_projects
from employee e join
workon w
on w.empid = e.empid
group by e.empid, e."NAME"
) ew
where num_projects > grace_num_projects;
SELECT DISTINCT
e.empid,
,e."NAME"
FROM employee e
INNER JOIN (SELECT
empid
,count(*) as num_projects
FROM workon
GROUP BY empid) w ON w.empid = e.empid
LEFT JOIN (SELECT
1 AS ID
COUNT(*) as grace_projects
FROM workon
GROUP BY empid
WHERE empid = 30) g ON g.ID = 1
WHERE w.num_projects > g.grace_projects;
So here's what I am doing.
I am counting the projects before they are joined, that should decrease the overhead for the query, as the return on the join is shrank considerably prior to joining.
An index on that WORKON table by EmpID would speed up the query considerably.
Then, I query Graces figures, because I want them to return a value against any person, it should only return one result for a count, and then just join that by an arbitrary value so it returns against all rows
Again, it should utilise the same index.
Because it is calculating Graces figures first, it should only need to do this once, whereas a subquery would need calculate graces figures for each employee which is an unnecessary overhead.
This is then filtered in the where clause.

How to use count and order by with inner join in mysql?

employee table
department table
How to display department name and number of employees working for that department.
My SQL code:
SELECT department.dname , employee.count(*)
FROM employee
INNER JOIN department
ON dno=dnumber
ORDER BY department.dname;
The correct syntax is:
select d.dname, count(*)
from employee e inner join
department d
on d.dno = e.dnumber
group by d.dname
order by d.dname;
Notes:
You are describing an aggregation query with one row per department. That suggests GROUP BY.
Table aliases make the query easier to write and to read.
Qualify all column names. I have to guess what table dno comes from. There should be no reason to guess.

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

Oracle: single-row subquery returns more than one row

SELECT e.name
from emp e, departm d, salery s
WHERE e.dep=d.depid
AND s.emp= e.empid
AND s.sal > (SELECT round(avg(s.sal)) as AVGSAL
from emp e, departm d, salery s
WHERE e.dep=d.depid AND s.emp= e.empid
GROUP BY d.depid
);
MY Tables are:
emp (empid, name, dep)
departm (depid, name, headdep)
salery (emp, cost, sal, fdate)
I have some foreign keys:
departm: FOREIGN KEY (headdep) REFERENCES departm(depid)
emp: FOREIGN KEY(dep) REFERENCES departm(depid)
salery: FOREIGN KEY(emp) REFERENCES emp(empid)
I want to print a list of all employees which earn more money than the average of theyr department but when i run this query i have an error: single-row subquery returns more than one row
Can anyone help me? What's the problem with my query?
Finally I want to create a procedure, but first of all i have to write the query.
Thank you guys...
Analytic functions are the way to go on this query, but your version has several problems. You would see these problems much more readily if you used proper join syntax. You seem to be learning SQL, so just remember one simple rule: NEVER use commas in from clauses.
Apart from the syntax problem, you have a problem with your correlated subquery. Here is a version that should work:
SELECT e.vollername
from emp e join
salery s
on s.emp= e.empid
WHERE s.sal > (select round(avg(s2.sal)) as AVGSAL
from emp e2 join
salery s2
on s2.emp= e2.empid
where e2.dep = e.depid
);
Note the removal of the departm table from both the inner and the outer queries. In the outer one, it was merely superfluous. In the inner one, it prevented the query from producing the correct results because it removed the correlation.
This is somewhat easier with analytic functions (I also removed the join with departm, since it is not needed):
SELECT e.vollername
FROM (
SELECT
e.vollername,
s.sal,
round(avg(s.sal) over (partition by e.dep)) as avg_dep_sal
FROM
emp e
JOIN salery s ON e.empid = s.emp
)
where sal > avg_dep_sal

How to find average without using group by statement in sql

I want to know if its possible to get the functionality of group by with using it.
I need to find average without using group by.
basically I am looking for an alternative for the below simple query without using group by.
SELECT
AVG(salary)
, g.emp_id
FROM #emp e ,#salary d
WHERE e.emp_id=d.emp_id
GROUP BY e.emp_id
One option
SELECT e.emp_id ,
( SELECT AVG(salary)
FROM #salary d
WHERE d.emp_id = e.emp_id )
FROM #emp e
If your DB supports partition you can do:
SELECT e.emp_id
, AVG(s.salary) OVER(PARTITION BY s.emp_id) AS average_salary
FROM #emp e
INNER JOIN #salary s ON (e.emp_id = s.emp_id)
I fail to see the purpose of this exercise however.
It just makes your query harder to read, harder to debug and replaces commonly used syntax with obscure code.
Group by rocks
Did you know that group by even as a 'grant total' function build in
SELECT
AVG(d.salary)
, e.emp_id
FROM #emp e
INNER JOIN #salary s ON (e.emp_id = s.emp_id)
GROUP BY e.emp_id WITH ROLLUP
See: SQL-server: http://msdn.microsoft.com/en-us/library/bb522495.aspx
MySQL: http://dev.mysql.com/doc/refman/5.0/en/group-by-modifiers.html
Remark
Do not use implicit SQL '89 syntax, as much as I love Bill Joel's we didn't start the fire it is time to move on to the much better SQL '92's Explicit join syntax.
As for songs, 1989 really has a better year:
If you really wanted to do this, you could SELECT ... ORDER BY employee and then write a cursor to calculate the averages piecemeal. I don't see any good reason to do this, though.
Are you looking for something like this?
SELECT AVG(d.salary) over (partition by null),
e.emp_id
FROM #emp e
JOIN #salary d ON e.emp_id = d.emp_id;
It will return all employee ids and the average salary calculated for all employees (so it will contain the same information for all rows).
Try This:
select * from (SELECT
AVG(salary)
, g.emp_id
FROM #emp e ,#salary d
WHERE e.emp_id=d.emp_id
GROUP BY e.emp_id) a`