not a single-group group function when using max - oracle

I am trying to get out the max avrg using query below but I am getting wrror saying
ORA-00937: not a single-group group function
00937. 00000 - "not a single-group group function"
*Cause:
*Action: Error at Line: 1 Column:
SELECT B.STUDENT_ID,
A.FRIST_NAME,
A.FATHER_NAME,
A.LAST_NAME,
SUM (B.GRADE) AS SUM_GRADE,
COUNT(B.COURSE_ID) AS COURSE_COUNT,
max(SUM(B.GRADE) / COUNT(B.COURSE_ID)) AS AVRG
FROM STUDENT A,
STUDENT_COURSE B
WHERE A.STUDENT_ID = B.STUDENT_ID
GROUP BY A.FRIST_NAME, A.FATHER_NAME, A.LAST_NAME, B.STUDENT_ID;
this error gone when I remove the max function any one can help me why ?
I tried to use having maxbut I am getting error that says invalid renational
any way to use having with this query ?

One option is to use your current query (without MAX) as an inline view, and apply the MAX function to "sum/count":
SELECT student_id,
first_name,
father_name,
last_name,
sum_grade,
course_count,
-- this:
MAX(avrg) max_avrt
FROM (-- your current query
SELECT b.student_id,
a.frist_name,
a.father_name,
a.last_name,
SUM(b.grade) AS sum_grade,
COUNT(b.course_id) AS course_count,
SUM(b.grade) / COUNT(b.course_id) AS avrg
FROM student a,
student_course b
WHERE a.student_id = b.student_id
GROUP BY a.frist_name,
a.father_name,
a.last_name,
b.student_id
)
GROUP BY student_id, first_name, father_name, last_name, sum_grade, course_count;
However, you won't achieve anything good, as you'd still get the same record set due to outer GROUP BY clause. Consider using SUM in its analytic form.
Here's a simple example which shows what I mean, based on Scott's schema.
This is what you have now:
SQL> select deptno, sum(sal) / count(*) ssc
2 from emp
3 group by deptno
4 order by deptno;
DEPTNO SSC
---------- ----------
10 2916,66667
20 2258,33333
30 1566,66667
Apparently, you'd like to select the first SSC value (2916). If you apply what I wrote earlier (i.e. use that query as an inline view), you'd get this:
SQL> select deptno, max(ssc) max_ssc
2 from (select deptno, sum(sal) / count(*) ssc
3 from emp
4 group by deptno
5 )
6 group by deptno
7 order by deptno;
DEPTNO MAX_SSC
---------- ----------
10 2916,66667
20 2258,33333
30 1566,66667
SQL>
No improvement, eh? So, analytical function might be what you need:
SQL> select deptno,
2 max(sum(sal) / count(*)) over (order by deptno) max_ssc
3 from emp
4 group by deptno
5 order by deptno;
DEPTNO MAX_SSC
---------- ----------
10 2916,66667
20 2916,66667
30 2916,66667
This does return desired MAX value (if that's what you're looking for. If not, explain what you'd want to get as a result).

Related

How to use a rowcount in select statement to modify the query to fetch data for 10 days , if rowcount is 0 for 5 days?

I need to modify my script using rowcount to check if the data in table or not?. Here, i write the query to select a data for last 5 days from current system date. But sometimes there is no data in table for 5 days. So i need to fetch for 10 day or more.
Query:
Select ep.ENTERPRISE_NAME||'|'||s.id||'|'||s.SUBMISSION_DATE||'|'||E.VALUE
from JOB_SUMMARY_EXT e, ob_summary s, enterprise ep
where e.id = s.id and e.name_res_key = 'Model'
and s.job_id in (select id from job_summary where
trunc(start_date) > trunc(sysdate) -10 and service_name ='Model2' )
I don't know how to modify my Query using rowcount. If rowcount is 0 then i want select data for 10 days.Otherwise it should to fetch for 5 days automatically. I want this to be done as single query.
It looks that you want to select the last 5 "days" from that table. So, why would you anchor to SYSDATE if there aren't rows for each of those days? I'd suggest another approach: literally, select last 5 days. Here's how.
As I don't have your tables, I'm using Scott's EMP table which contains information about employees. It is an ancient one so HIREDATE column is set to 1980s, but never mind that. Sorting employees by HIREDATE in descending order shows:
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> select ename, hiredate from emp order by hiredate desc;
ENAME HIREDATE
---------- ----------
ADAMS 12.01.1983 1.
SCOTT 09.12.1982 2.
MILLER 23.01.1982 3.
FORD 03.12.1981 4.
JAMES 03.12.1981 4.
KING 17.11.1981 5. --> I want to fetch rows up to KING
MARTIN 28.09.1981
TURNER 08.09.1981
CLARK 09.06.1981
BLAKE 01.05.1981
JONES 02.04.1981
WARD 22.02.1981
ALLEN 20.02.1981
SMITH 17.12.1980
14 rows selected.
SQL>
As you can see, the 4th date is shared by two employees so I want to include them both. DENSE_RANK analytic function helps:
SQL> with last5 as
2 (select ename,
3 job,
4 sal,
5 hiredate,
6 dense_rank() over (order by hiredate desc) rnk
7 from emp
8 )
9 select ename, job, sal, hiredate
10 from last5
11 where rnk <= 5;
ENAME JOB SAL HIREDATE
---------- --------- ---------- ----------
ADAMS CLERK 1100 12.01.1983
SCOTT ANALYST 3000 09.12.1982
MILLER CLERK 1300 23.01.1982
JAMES CLERK 950 03.12.1981
FORD ANALYST 3000 03.12.1981
KING PRESIDENT 5000 17.11.1981
6 rows selected.
SQL>
What does it do? The LAST5 CTE sorts employees (as above), DENSE_RANK ranks them; finally, the last SELECT (which begins at line #9) fetches desired rows.
In your case, that might look like this:
with last5 as
(select id,
dense_rank() over (order by start_date desc) rnk
from job_summary
where service_name = 'Model2'
)
select ep.enterprise_name,
s.id,
s.submission_date,
e.value
from job_summary_ext e
join ob_summary s on e.id = s.id
join last5 t on t.id = s.id
join enterprise ep on <you're missing join condition for this table>
where e.name_res_key = 'Model';
Note that you're missing join condition for the ENTERPRISE table; if that's really so, no problem - you'd use cross join for that table, but I somehow doubt that you want that.
Finally, as you use SQL*Plus, perhaps you don't need to concatenate all columns and separate them by the pipe | sign - set it as a column separator, e.g.
SQL> set colsep '|'
SQL>
SQL> select deptno, dname, loc from dept;
DEPTNO|DNAME |LOC
----------|--------------|-------------
10|ACCOUNTING |NEW YORK
20|RESEARCH |DALLAS
30|SALES |CHICAGO
40|OPERATIONS |BOSTON
SQL>
If you want to
return 10 last days if select count(*) returns 0, or
return 5 last days if select count(*) returns a positive number
then something like this might help (again based on Scott's EMP table):
with
tcnt as
-- count number of rows; use your own requirement, I'm checking
-- whether someone got hired today. In Scott's EMP table, nobody was
-- so CNT = 0
(select count(*) cnt
from emp
where hiredate >= trunc(sysdate)
)
select e.ename, e.job, e.sal, e.hiredate
from emp e cross join tcnt c
where e.hiredate >= case when c.cnt = 0 then trunc(sysdate) - 10
else trunc(sysdate) - 5
end;
Apply it to your tables; I don't know which of those 3 tables' count you want to check.
Tried to add in comments but it was too long for comments and Not clear on count based on but here is case in where clause substitute your count statement with nvl function
SELECT ep.ENTERPRISE_NAME||'|'||s.id||'|'||s.SUBMISSION_DATE||'|'||E.VALUE
FROM JOB_SUMMARY_EXT e,
ob_summary s,
enterprise ep
WHERE e.id = s.id
AND e.name_res_key = 'Model'
AND s.job_id IN
(SELECT id
FROM job_summary
WHERE service='Model'
AND trunc(start_date) >
CASE WHEN
(WRITE your SELECT COUNT criteria WITH NVL FUNCTION)<=0 THEN
trunc(sysdate) -10
ELSE trunc(sysdate)-5
END )

SELECT DISTINCT on one column and return multiple other columns in Oracle/TOAD

My goal is to get a distinct for Clm_Pd_Amt column only and return all other columns:
SELECT CLM_AMT, PAID_DATE, MBR, DISTINCT CLM_PD_AMT
FROM MY_CLAIMS
WHERE DATE >= '20200101
AND STATUS = 'CURRENT'
A GROUP BY is equivalent to a distinct, for example, the distinct list of departments in the EMP table can be found with (say)
SQL> select deptno, min(sal)
2 from emp
3 group by deptno
4 /
DEPTNO MIN(SAL)
---------- ----------
30 951
10 1300
20 800
But what if I want to know which employee had that minimum salary. Then you can use the KEEP clause to gather that as well, eg
SQL> select deptno, min(sal), min(empno) KEEP ( dense_rank FIRST order by sal) empno
2 from emp
3 group by deptno
4 /
DEPTNO MIN(SAL) EMPNO
---------- ---------- ----------
10 1300 7934
20 800 7369
30 951 7900
So using that approach, you should be able to adapt your query to get the distinct CLM_PD_AMT and then pick up the other columns with KEEP. This only works if you have a definition for which distinct CLM_PD_AMT means, ie, the smallest? the largest? etc

Row num is not working for duplicate columns while pagination

Code is written below, multiple nested query:
select *
from
(select employee_id,employee_id
from employees) a
where rownum <= 5
)
where rnum >= 10
If i give duplicate columns in the select its giving "column ambiguously defined" error.
employee_id,employee_id, rownum rnum
Firstly, your query is incorrect since you have ambiguously defined the columns. It will throw ORA-00918: column ambiguously defined.
You must use proper ALIAS to avoid the error. For example,
SELECT departments.department_id AS "dept_id",
employees.department_id AS "emp_Dept_id"
FROM...
Secondly, it is not at all a pagination query. Since you are alwyas going to pick random rows as there is no ORDER BY clause. You are not ordering the rows.
where rownum <= 5
where rnum >= 10
At last, how on earth could you try to fetch the rows beyond 10 when you have fetched ONLY 5 rows in the inner query? It will ALWAYS return zero rows.
The correct way of paging through data is:
SQL> SELECT empno
2 FROM (SELECT empno, rownum AS rnum
3 FROM (SELECT empno
4 FROM emp
5 ORDER BY sal)
6 WHERE rownum <= 8)
7 WHERE rnum >= 5;
EMPNO
----------
7654
7934
7844
7499
SQL>
when you give a.* it means you are trying to refer two columns with same name in a table which is not permitted.The column names in a table are unique
So select employee_id,employee_id from employees is not a problem
but
select a.* from (select employee_id,employee_id from employees)a is a problem
the sqlfiddle here
Also if you want records from 10 to 15 in your query then use like the below
select * from
(select a.*,Rownum rnum from
(select employee_id as emp_id1,employee_id as emp_id2
from employees order by 1)
a where rownum <= 15 ) where rnum >= 10
EDIT1:- If duplicate column is required use like the below
with emp1 as (select employee_id from employees),
emp2 as (select * from
(select a.*,rownum rnum from emp1 a order by 1)
where rownum <=15)
select b.*,c.*
from emp1 b,emp2 c
where b.employee_id=c.employee_id
and c.rnum >=10

How can i select just one row in Oracle by row id?

I have used mysql database in my application, but I want to migrate to Oracle.
The problem is in that query:
select * from users limit ?,1;"
That query returns every row one by one depending on ?.
How can i do that in oracle?
On Oracle 12c, you could use the row limiting feature using FETCH FIRST clause.
SQL> SELECT empno, sal, deptno FROM emp ORDER BY empno DESC
2 FETCH FIRST 1 ROWS ONLY;
EMPNO SAL DEPTNO
---------- ---------- ----------
7934 1300 10
SQL>
Prior 12c solution is ROWNUM, however, if you want the row to be first sorted, then you need to do it in a sub-query -
SQL> SELECT empno, sal, deptno FROM
2 ( SELECT * FROM emp ORDER BY empno DESC
3 ) WHERE ROWNUM = 1;
EMPNO SAL DEPTNO
---------- ---------- ----------
7934 1300 10
SQL>
If the order doesn't matter to you, if you just want any random row, simply use ROWNUM.
Depending on your requirement, you could also use ANALYTIC functions such as ROW_NUMBER, RANK, DENSE_RANK.
select * from (select rownum r, u.* from users u ) where r=1;
or if you want it sorted (replace x by columnnumber or columnname):
select * from (select rownum r, u.* from users u order by x) where r=1;

Update Statement with two tables -Oracle

I have two tables and I need to Update the second table with a value from first table based on a common column.
I used the below statement
UPDATE emp
2 SET ename = ( SELECT dname
3 FROM dept
4 WHERE emp.deptno = dept.deptno)
5 WHERE EXISTS
6 ( SELECT dname
7 FROM dept
8 WHERE emp.deptno = dept.deptno);
But am getting the error
ORA-01427 - Single row subquery returns more than one row.
Can you plese help me out?
SELECT dname
FROM dept
WHERE emp.deptno = dept.deptno
query must return single record.
check with
SELECT count(*), dname
FROM dept
group by dname
having count(*) > 1
order by 1 desc
or use
SELECT dname
FROM dept
WHERE emp.deptno = dept.deptno
and rownum = 1
You need to check if 1st subquery returns only 1 value bcz if this subquery returns more then 1 row you want to update 1 field ename with 2 values from row and that's why i guess you have this error
Your subquery to dept table is probably not returning one row. Add min or max operation to get one row from dept for each row from emp. Each setting value must be nonambigous.
UPDATE emp
SET ename = ( SELECT min dname
FROM dept
WHERE emp.deptno = dept.deptno)
WHERE EXISTS
( SELECT dname
FROM dept
WHERE emp.deptno = dept.deptno);
Remark1: in default SCOTT schema deptno is primary key, so no error would be encountered.
Remark2: shouldn't your question to be placed in serverfault.com?

Resources