Using Aggregate function in select statement - oracle

Is there a way we can use an aggregate function inside a CASE statement as part of the same select ?
When i run the query below i get the following error ORA-00937: not a single-group group function
00937. 00000 - "not a single-group group function".
SELECT
A.id id,
SUM(B.quantity) quantity ,
MAX(CASE WHEN nvl(A.threshold,0) > SUM(B.quantity) THEN 'T' ELSE 'F' END ) late
FROM
TableB B ,
TableA A
WHERE B.Type in ('ATYPE,'BTYPE') AND A.nKey = B.id
GROUP BY
A.id
Table B
id quantity Type
9 10 Type A
9 11 Type B
10 5 Type A
Table A
id nkey threshold
1 9 15
2 10 10
Result
id quantity late
1 21 F
2 5 T

You just need to use the aggregate function for threshold or add threshold in the group by list:
either replace nvl(A.threshold,0) with min(nvl(A.threshold,0))
or
use the following GROUP BY:
group by a.id, a.threshold

Related

Subquery as CASE WHEN condition

Below query, syntax error happens on AS PQ_COUNT
SELECT CASE WHEN
RESULTS LIKE '%PQ - Duplicate%' AND
(SELECT COUNT(*) FROM MY_TABLE WHERE ID = '998877'AND FINAL_RESULTS='FL_57') AS PQ_COUNT >= 1
THEN 'PQ count = '|| PQ_COUNT
ELSE RESULTS END AS RESULTS
If I moved AS PQ_COUNT inside select query,
(SELECT COUNT(*) AS PQ_COUNT FROM MY_TABLE WHERE ID = '998877'AND FINAL_RESULTS='FL_57') >= 1
the reference of PQ_COUNT in THEN block become invalid identifier (ORA-00904)
What might go wrong here when addressing subquery as CASE WHEN condition?
One option is to use a subquery (or a CTE, as in my example) to calculate number of rows that satisfy condition, and then - as it contains only one row - cross join it to my_table. Something like this:
SQL> WITH
2 my_table (id, final_results, results) AS
3 -- sample data
4 (SELECT '998877', 'FL_57', 'PQ - Duplicate' FROM DUAL),
5 cnt AS
6 -- calculate COUNT first ...
7 (SELECT COUNT (*) pq_count --> pq_count
8 FROM MY_TABLE
9 WHERE ID = '998877'
10 AND FINAL_RESULTS = 'FL_57')
11 -- ... then re-use it in "main" query
12 SELECT CASE
13 WHEN a.results LIKE '%PQ - Duplicate%'
14 AND b.pq_count >= 1 --> reused here
15 THEN
16 'PQ count = ' || b.PQ_COUNT --> and here
17 ELSE
18 a.results
19 END AS results
20 FROM my_table a CROSS JOIN cnt b;
RESULTS
---------------------------------------------------
PQ count = 1
SQL>
You cannot refer to an alias in the same sub-query where you create it; you need to nest sub-queries (or use a sub-query factoring clause; also called a CTE or WITH clause) and refer to it in the outer one:
SELECT CASE
WHEN results LIKE '%PQ - Duplicate%'
AND pq_count >= 1
THEN 'PQ count = '|| pq_count
ELSE results
END AS RESULTS
FROM (
SELECT results,
( SELECT COUNT(*)
FROM MY_TABLE
WHERE ID = '998877'
AND FINAL_RESULTS='FL_57'
) AS pq_count
FROM your_table
);

How to use select statement insert first row of dummy dash value with 2 table combination

table_A
no desciption
1 Apple
2 orange
3 banana
4 kiwi
5 papaya
table_B
no price mydate
1 10.00 20210801
2 8.00 20210802
3 5.00 20210803
4 12.00 20210804
5 4.00 20210805
Hi, I try to use this SQL and union all but show an error.
select '-' a.description, '-' b.price from dual union all select a.description,sum(b.price) from table_A a, table_B b where a.no=b.no and b.mydate='20210801' group by a.description;
ORA-00923 : FROM keyword not found where expected
I need result
a.description sum(b.price)
- - <-----dummy dash always on first row
Apple 10.00
Anyone help is much appreciated.
There is no aliases a and b in your first part of union all, so just remove a. and b.
Don't use implicit conversion from char literals to date, use literal dates instead:
select '-' as description, '-' as price from dual
union all
select a.description,to_char(sum(b.price))
from
table_A a
join table_B b
on a.no=b.no
where b.mydate=date'2021-08-01'
group by a.description;
One option is to
use a subquery
it contains additional rn column (where 1 means that those values will represent "row number 1" in the result)
apply to_char function to sum(b.price) because - with union - queries have to match in number of columns and their datatypes
So:
SELECT description, price
FROM (SELECT 1 rn, '-' description, '-' price FROM DUAL
UNION ALL
SELECT 2 rn, a.description, TO_CHAR (SUM (b.price))
FROM table_A a, table_B b
WHERE a.no = b.no
AND b.mydate = '20210801'
GROUP BY a.description)
ORDER BY rn
which - applied to your sample data - returns
DESCRIPTION PRICE
--------------- ----------------------------------------
- -
apple 10
Note that:
mydate should be a true date datatype column; don't use strings because nothing prevents you from entering e.g. 2f$8-23 into a varchar2 column, and that certainly isn't a valid date value
try to switch to joins; leave where clause for conditions data should meet in order to be returned
Something like this:
from table_a a join table_b b on a.no = b.no --> JOIN
where b.mydate = date '2021-08-01' --> MYDATE being DATE datatype

Count(*) in Group By - Returns 1, instead of '0'

I am using the below oracle query to get the count of rows.
SELECT T.ID,T.NAME,COUNT(*) AS NO_OF_STUDENTS FROM STUDENT S RIGHT JOIN
TEACHER T ON S.TEACHER_ID = T.ID
GROUP BY T.ID,T.NAME ORDER BY T.ID
Actual Result Should be:
TEACHER 1 - 10 STUDENTS
TEACHER 2 - 5 STUDENTS
TEACHER 3 - 0 STUDENT
The Result what i am getting is:
TEACHER 1 - 10 STUDENTS
TEACHER 2 - 5 STUDENTS
TEACHER 3 - 1 STUDENT
Since TEACHER 3 is not having any student, the result should be 0 Student. But i am getting the result as 1 Student.
You need to count a specific column (not use *, which includes nulls) in the table being outer joined to - since that is the one that might not have matching data. So:
SELECT T.ID, T.NAME, COUNT(S.ID) AS NO_OF_STUDENTS
FROM STUDENT S
RIGHT JOIN TEACHER T ON S.TEACHER_ID = T.ID
GROUP BY T.ID, T.NAME
ORDER BY T.ID
The only difference is COUNT(S.ID) instead of COUNT(*).
Simple demo with made-up data provided via CTEs:
with teacher (id, name) as (
select 1, 'Teacher 1' from dual
union all select 2, 'Teacher 2' from dual
union all select 3, 'Teacher 3' from dual
),
student (id, teacher_id) as (
select level, 1 from dual connect by level <= 10
union all
select level + 10, 2 from dual connect by level <= 5
)
SELECT T.ID, T.NAME, COUNT(S.ID) AS NO_OF_STUDENTS
FROM STUDENT S
RIGHT JOIN TEACHER T ON S.TEACHER_ID = T.ID
GROUP BY T.ID, T.NAME
ORDER BY T.ID;
ID NAME NO_OF_STUDENTS
---------- --------- --------------
1 Teacher 1 10
2 Teacher 2 5
3 Teacher 3 0
You could also do this as a left join, which I find more intuitive:
SELECT T.ID, T.NAME, COUNT(S.ID) AS NO_OF_STUDENTS
FROM TEACHER T
LEFT JOIN STUDENT S ON S.TEACHER_ID = T.ID
GROUP BY T.ID, T.NAME
ORDER BY T.ID
which gets the same result.

Fetching Lastrow from the table in toad

I have to fetch the first and last row of the table in Toad.
I have used the following query
select * from grade_master where rownum=(select max(rownum) from grade_master)
select * from grade_master where rownum=1
The second query works to fetch the first row. but the first not working. Anyone please help me.
Thanks in advance
Such request makes sense if you specify sort order of the results - there are no such things in database as "first" and "last" rows if sort order is not specified.
SQL> with t as (
2 select 'X' a, 1 b from dual union all
3 select 'C' , 2 from dual union all
4 select 'A' a, 3 b from dual
5 )
6 select a, b, decode(rn, 1, 'First','Last')
7 from (
8 select a, b, row_number() over(order by a) rn,
9 count(*) over() cn
10 from t
11 )
12 where rn in (1, cn)
13 order by rn
14 /
A B DECOD
- ---------- -----
A 3 First
X 1 Last
In oracle the data is not ordered until you specify the order in you sql statement.
So when you do:
select * from grade_master
oracle will give the rows in anyway it want wants.
OTOH if you do
select * from grade_master order by id desc
Then oracle will give the rows back ordered by id descending.
So to get the last row you could do this:
select *
from (select * from grade_master order by id desc)
where rownum = 1
The rownum is determined BEFORE the "order by" clause is assessed, so what this query is doing is ordering the rows descending (the inside query) and then giving this ordered set to the outer query. The outer gets the first row of the set then returns it.

Query to exclude row based on another row's filter

I'm using Oracle 10g.
Question: How can I write query to return just ID only if ALL the codes for that ID end in 6? I don't want ID=1 because not all its codes end in 6.
TABLE_A
ID Code
===============
1 100
1 106
2 206
3 316
3 326
4 444
Desired Result:
ID
==
2
3
You simply want each ID where the count of rows for that id is the same as the count of rows where the third digit is six.
SELECT ID
FROM TABLE_A
GROUP BY ID
HAVING COUNT(*) = COUNT(CASE WHEN SUBSTR(code,3,1) = '6' THEN 1 END)
Try this:
SELECT DISTINCT b.id
FROM (
SELECT id,
COUNT(1) cnt
FROM table_a
GROUP BY id
) a,
(
SELECT id,
COUNT(1) cnt
FROM table_a
WHERE CODE LIKE '%6'
GROUP BY id
)b
WHERE a.id = b.id
AND a.cnt = b.cnt
Alternative using ANALYTIC functions:
SELECT DISTINCT id
FROM
(
SELECT id,
COUNT(1) OVER(PARTITION BY id) cnt,
SUM(CASE WHEN code LIKE '%6' THEN 1 ELSE 0 END) OVER(PARTITION BY id) sm
FROM table_a
)
WHERE cnt = sm

Resources