I am attempting to write a query that returns the the number of employees, the average salary, and the number of employees paid below the average.
The query I have so far is:
select trunc(avg(salary)) "Average Pay",
count(salary) "Total Employees",
(
select count(salary)
from employees
where salary < (select avg(salary) from employees)
) UnderPaid
from employees;
But when I run this I get the ora-00937 error in the subquery.
I had thought that maybe the "count" function is what is causing the issue, but even running a simpler sub query such as:
select trunc(avg(salary)) "Average Pay",
count(salary) "Total Employees",
(
select avg(salary) from employees
) UnderPaid
from employees;
still returns the same error. As both AVG and COUNT seem to be aggregate functions, I'm not sure why I'm getting the error?
Thanks
When you use scala subquery, which is a subquery in the select list, it should return only one row.
In general, subquery can return multiple rows. So when you use it in the select list with aggregation function, you should wrap it with aggregation function that has no side effect.
select count(*), (select count(*) from emp) from emp
-- ERROR. Oracle doesn't know that the subquery returns only 1 row.
select count(*), max((select count(*) from emp)) from emp
-- You know that the subquery returns 1 row, applying max() results the same.
Or you can rewrite the query like this:
select avg(salary), count(*), count(case when salary < sal_avg then 1 end)
from (select salary, avg(salary) over () sal_avg from emp);
ntalbs' answer works (thanks, ntalbs!), but see question "ORA-00937: Not a single-group group function - Query error" for a more complete explanation if you want one.
Related
When I try it without displaying department_id it works fine as :
SQL> SELECT MAX(AVG(SALARY)) FROM EMPLOYEE GROUP BY DEPARTMENT_ID;
MAX(AVG(SALARY))
----------------
800000
But when I want to display the department_id's too, it gives me error as follows:
SQL> SELECT DEPARTMENT_ID, MAX(AVG(SALARY)) FROM EMPLOYEE GROUP BY DEPARTMENT_ID;
SELECT DEPARTMENT_ID, MAX(AVG(SALARY)) FROM EMPLOYEE GROUP BY DEPARTMENT_ID
*
ERROR at line 1:
ORA-00937: not a single-group group function
Is there any explanation for this? What am I doing wrong? I went through answers of previous questions like this and tried their solutions but got same or some other error. Any help would be appreciated.
I suggest you use
SELECT department_id, avg_salary
FROM ( SELECT DEPARTMENT_ID,
AVG (SALARY) avg_salary,
RANK () OVER (ORDER BY AVG (salary) DESC) rnk
FROM EMPLOYEE
GROUP BY DEPARTMENT_ID)
WHERE rnk = 1;
i.e.
use your first query as a "source"
additionally, rank average salaries in descending order (using the rank analytic function)
select row which ranks as highest
This is a SQL query to find the Nth highest salary of employees:
SELECT *
FROM emp t
WHERE 1 = (SELECT COUNT(DISTINCT sal)
FROM emp t2
WHERE t2.sal > t.sal)
I don't know how it returns the result. If you put 1 in the WHERE clause, it will return the second highest, and for 2 the 3rd highest salary, and so on.
Please explain the query as I am unsure.
Let me start by saying a better way to write the query is:
select e.*
from (select e.*, dense_rank() over (order by sal desc) as seqnum
from emp e
) e
where seqnum = 2;
What is your query doing? Go step-by-step:
The outer query is doing a comparison for every row in emp.
The comparison counts the number of distinct salaries that are larger than the salary in the row.
The row is kept if there is exactly 1 salary that is larger.
In other words, this is keeping all rows that have the second largest salary. dense_rank() is a much saner way to write the query (and it has better performance too).
I'm trying to adapt a query that works in MSSQL to Oracle, the query is much bigger (this part is just a field from a much bigger query) but I managed to reduce it so it looks simpler.
SELECT CASE WHEN COUNT(*) > 0 THEN COUNT(*)
ELSE (SELECT COUNT(*) FROM table2)
END
FROM table1
The error I'm getting is:
ora-00937 not a single-group group function
Can someone tell me where's the problem or how can I redefine it?
You can try with this query:
SELECT CASE WHEN (SELECT COUNT(*) FROM table1) > 0 then (SELECT COUNT(*) FROM table1)
ELSE (SELECT COUNT(*) FROM table2)
END
FROM dual;
It is still ugly but it works :)
Update:
To explain how it's working:
We have 2 cases:
If there are records in the table1 then show me how many records
there are
If the table1 is empty, then give me the number of records from the
table2
Dual is the dummy table.
I think that NikNik answer is cleaner but another solution would be:
SELECT *
FROM (SELECT CASE
WHEN Count(*) > 0 THEN Count(*)
ELSE (SELECT Count(*)
FROM table2)
END
FROM table1
GROUP BY table1.primarykey1,
table1.primarykey2)
WHERE ROWNUM = 1
I do understand the meaning of this statement but I don't understand why do we need this?
This is equivalent to
select first_Name, last_name from employees
I can see this type of statements in many examples. Can you please explain when we need this? In practical do we use this type of statements?
Can you please explain when we need this?
These are called Derived Tables.
A "derived table" is essentially a statement-local temporary table
created by means of a subquery in the FROM clause of a SQL SELECT
statement. It exists only in memory and behaves like a standard view
or table.
In SQL, subqueries can only see values from parent queries one level deep.
In practical do we use this type of statements?
The most common use of it is the classic row-limiting query using ROWNUM.
Row-Limiting query:
SELECT *
FROM (SELECT *
FROM emp
ORDER BY sal DESC)
WHERE ROWNUM <= 5;
Pagination query:
SELECT eno
FROM (SELECT e.empno eno,
e.ROWNUM rn
FROM (SELECT empno
FROM emp
ORDER BY sal DESC) e)
WHERE rn <= 5;
This kind of statement is useless, you're right, but there are many occasions when you need a subselect because you can't do everything in one statement. Of the top of my head I'd be thinking about for instance, combining aggregate functions, get the min, max and avg of a sum
select min(t.summed), max(t.summed), avg(t.summed)
from (select type, sum(value) as summed from table1 group by type) t
this is just from the top of my head, but I did encounter many occasions where subselects in the from clause were necessary. Once the statements are complex enough you'll see it.
I have a complex query with group by and order by clause and I need a sorted row number (1...2...(n-1)...n) returned with every row. Using a ROWNUM (value is assigned to a row after it passes the predicate phase of the query but before the query does any sorting or aggregation) gives me a non-sorted list (4...567...123...45...). I cannot use application for counting and assigning numbers to each row.
Is there a reason that you can't just do
SELECT rownum, a.*
FROM (<<your complex query including GROUP BY and ORDER BY>>) a
You could do it as a subquery, so have:
select q.*, rownum from (select... group by etc..) q
That would probably work... don't know if there is anything better than that.
Can you use an in-line query? ie
SELECT cols, ROWNUM
FROM (your query)
Assuming that you're query is already ordered in the manner you desire and you just want a number to indicate what row in the order it is:
SELECT ROWNUM AS RowOrderNumber, Col1, Col2,Col3...
FROM (
[Your Original Query Here]
)
and replace "Colx" with the names of the columns in your query.
I also sometimes do something like:
SELECT * FROM
(SELECT X,Y FROM MY_TABLE WHERE Z=16 ORDER BY MY_DATE DESC)
WHERE ROWNUM=1
If you want to use ROWNUM to do anything more than limit the total number of rows returned in a query (e.g. AND ROWNUM < 10) you'll need to alias ROWNUM:
select *
(select rownum rn, a.* from
(<sorted query>) a))
where rn between 500 and 1000