how can list row with max value of one column - oracle

I have some database like example below.
WITH TB AS(
SELECT 'A' C1, 'B' AS C2, 1 AS N1, 1 AS N2 FROM DUAL UNION ALL
SELECT 'A' C1, 'B' AS C2, 2 AS N1, 2 AS N2 FROM DUAL UNION ALL
SELECT 'A1' C1, 'B1' AS C2, 1 AS N1, 3 AS N2 FROM DUAL UNION ALL
SELECT 'A1' C1, 'B1' AS C2, 2 AS N1, 4 AS N2 FROM DUAL UNION ALL
SELECT 'A1' C1, 'B1' AS C2, 3 AS N1, 1 AS N2 FROM DUAL UNION ALL
SELECT 'A2' C1, 'B2' AS C2, 1 AS N1, 6 AS N2 FROM DUAL
)
SELECT * FROM TB
How can I list all row with Max(N1), and group by C1, C2 like Image below?

The description of the requirements in the question is unclear, I am guesing that max(n1) should be calculated for each group of C1+c2.
If this is a case, then you can use MAX() OVER () analytic function in this way:
SELECT tb.*,
max( n1 ) over (partition by c1, c2 ) xxxx
FROM TB;
| C1 | C2 | N1 | N2 | XXXX |
|----|----|----|----|------|
| A | B | 1 | 1 | 2 |
| A | B | 2 | 2 | 2 |
| A1 | B1 | 1 | 3 | 3 |
| A1 | B1 | 2 | 4 | 3 |
| A1 | B1 | 3 | 1 | 3 |
| A2 | B2 | 1 | 6 | 1 |
and then wrap the above query as a subquery, and filter out unwanted rows:
SELECT c1,c2,n1,n2 FROM (
SELECT tb.*,
max( n1 ) over (partition by c1, c2 ) xxxx
FROM TB
)
WHERE n1 = xxxx
| C1 | C2 | N1 | N2 |
|----|----|----|----|
| A | B | 2 | 2 |
| A1 | B1 | 3 | 1 |
| A2 | B2 | 1 | 6 |
Demo: http://sqlfiddle.com/#!4/d2fb9/4

One way is using KEEP .. DENSE_RANK
SQL Fiddle
Query 1:
WITH TB AS(
SELECT 'A' C1, 'B' AS C2, 1 AS N1, 1 AS N2 FROM DUAL UNION ALL
SELECT 'A' C1, 'B' AS C2, 2 AS N1, 2 AS N2 FROM DUAL UNION ALL
SELECT 'A1' C1, 'B1' AS C2, 1 AS N1, 3 AS N2 FROM DUAL UNION ALL
SELECT 'A1' C1, 'B1' AS C2, 2 AS N1, 4 AS N2 FROM DUAL UNION ALL
SELECT 'A1' C1, 'B1' AS C2, 3 AS N1, 1 AS N2 FROM DUAL UNION ALL
SELECT 'A2' C1, 'B2' AS C2, 1 AS N1, 6 AS N2 FROM DUAL
)
SELECT C1
,C2
,MAX(N1) AS N1
,MAX(N2) KEEP (
DENSE_RANK FIRST ORDER BY N1 DESC
) AS N2
FROM TB
GROUP BY C1
,C2
Results:
| C1 | C2 | N1 | N2 |
|----|----|----|----|
| A | B | 2 | 2 |
| A1 | B1 | 3 | 1 |
| A2 | B2 | 1 | 6 |

Related

NULLIF of FINAL SUM Doubled when using MATCH_RECOGNIZE

When I run the following code, I would expect b1 and b2 to be equal, however, b2 is doubled. Am I doing something wrong? Is this a bug in the database? We're running Oracle 12c (12.2.0.1.0).
WITH TBL AS
(
SELECT 1 a, 1 b FROM DUAL UNION ALL
SELECT 1 a, 2 b FROM DUAL UNION ALL
SELECT 1 a, 3 b FROM DUAL UNION ALL
SELECT 1 a, 4 b FROM DUAL
)
SELECT
*
FROM
TBL
MATCH_RECOGNIZE
(
PARTITION BY
a
ORDER BY
b
MEASURES
FINAL SUM(b) b1,
NULLIF(FINAL SUM(b), 0) b2
ALL ROWS PER MATCH WITH UNMATCHED ROWS
AFTER MATCH SKIP PAST LAST ROW
PATTERN
(C*)
DEFINE
C AS B > 0
) mr
Result:
| A | B | B1 | B2 |
|---|---|----|----|
| 1 | 1 | 10 | 20 |
| 1 | 2 | 10 | 20 |
| 1 | 3 | 10 | 20 |
| 1 | 4 | 10 | 20 |
The problem seems to be with NULLIF when I converted the same into it's logical equivalent and it is working fine CASE WHEN expr1 = expr 2 THEN NULL ELSE expr1 END
WITH TBL AS
(
SELECT 1 a, 1 b FROM DUAL UNION ALL
SELECT 1 a, 2 b FROM DUAL UNION ALL
SELECT 1 a, 3 b FROM DUAL UNION ALL
SELECT 1 a, 4 b FROM DUAL
)
SELECT
*
FROM
TBL
MATCH_RECOGNIZE
(
PARTITION BY
a
ORDER BY
b
MEASURES
FINAL SUM(b) b1,
CASE WHEN FINAL SUM(b)=0 THEN NULL ELSE FINAL SUM(b) END b2
ALL ROWS PER MATCH WITH UNMATCHED ROWS
AFTER MATCH SKIP PAST LAST ROW
PATTERN
(C*)
DEFINE
C AS B > 0
) mr
Result
| A | B | B1 | B2 |
|---|---|----|----|
| 1 | 1 | 10 | 10 |
| 1 | 2 | 10 | 10 |
| 1 | 3 | 10 | 10 |
| 1 | 4 | 10 | 10 |

Oracle iterates thru values on columns (Oracle 11)

So I have some data like this
NO| ID | PID | COUNT
1 | 00033 | P4 | 1
2 | 00033 | P3 | 3
3 | 00033 | P2 | 2
i want to iterate the ID and PID based on count values, like this
NO| ID | PID
1 | 00033 | P4
2 | 00033 | P3
3 | 00033 | P3
4 | 00033 | P3
5 | 00033 | P2
6 | 00033 | P2
actually its already solved using this query
SELECT row_number() OVER ( ORDER BY t."ID", t."PID" DESC ) as NO,
t."ID", t."PID"
FROM Table1 t
CROSS APPLY(
SELECT 1 FROM dual
CONNECT BY level <= t."COUNT"
)
ORDER BY t."ID", t."PID" DESC
as per this link
Oracle iterates thru values on columns
but since our DB using oracle 11. the code doesnt working anymore.
i'd like to know the same approach for oracle 11.
Regards, Rian
Cast this hierarchical query as table of numbers and then join:
with
t("NO", "ID", "PID", "COUNT") as (
select 1, '00033', 'P4', 1 from dual union all
select 2, '00033', 'P3', 3 from dual union all
select 3, '00033', 'P2', 2 from dual ),
a as (select t.*,
cast(multiset(select level lvl
from dual
connect by level <= "COUNT")
as sys.odcinumberlist) nums
from t)
select "NO", "ID", "PID" from a cross join table(nums)
Result:
NO ID PID
---------- ----- ---
1 00033 P4
2 00033 P3
2 00033 P3
2 00033 P3
3 00033 P2
3 00033 P2
6 rows selected

Use count(decode...) across multiple tables

I have three 3 tables (having the same key) in the following structure:
Input Table t1:
file_in| f_in_state|
--------------------
F01 | 1 |
F02 | 2 |
F21 | 1 |
F41 | 2 |
Input Table t2:
line_in| file_in| l_in_state |
-----------------------------
L001 | F01 | 1 |
L002 | F01 | 2 |
L003 | F01 | 2 |
L004 | F01 | 2 |
L005 | F21 | 1 |
L006 | F21 | 1 |
L007 | F21 | 1 |
L008 | F21 | 1 |
Input Table t3:
line_out|line_in| file_in| l_out_state|
---------------------------------------
D001 |L001 | F01 | 1 |
D002 |L002 | F01 | 1 |
D003 |L003 | F01 | 1 |
and I need to count the number of occurrences of the columns refering to the different states across my three tables for each "file in id" and then combine them to get an output like this:
file_in_id|file_in_state| A | B | C | D | E |
---------------------------------------------
F01 | 1 | 1 | 3 | 0 | 0 | 3 |
F02 | 2 | 2 | 0 | 0 | 0 | 0 |
F21 | 1 | 1 | 4 | 0 | 0 | 0 |
F41 | 2 | 2 | 0 | 0 | 0 | 0 |
with:
A refers to the number of input lines ("line_in") having the state = '1'
B refers to the number of input lines ("line_in") having the state = '2'
C refers to the number of input lines ("line_in") having the state = '3' (in my case, there is no line with this state, but it is possible to happen)
D refers to the number of output lines ("line_out") having the state = '1'
E refers to the number of output lines ("line_out") having the state = '2'
So, I tried to use the decode function in my query but I didn't get the wished result.
SELECT
t1.file_in AS file_in_id,
t1.f_in_state AS file_in_state,
COUNT(DECODE(t2.f_in_state, '1', 1, null)) AS A,
COUNT(DECODE(t2.f_in_state, '2', 1, null)) AS B,
COUNT(DECODE(t2.f_in_state, '3', 1, null)) AS C,
COUNT(DECODE(t3.f_out_state, '1', 1, null)) AS D,
COUNT(DECODE(t3.f_out_state, '2', 1, null)) AS E
FROM table1 t1,
table2 t2,
table3 t3
WHERE t1.file_in = t2.file_in (+)
AND t2.file_in = t3.file_in (+)
GROUP BY t1.file_in, t1.f_in_state
ORDER BY t1.file_in
But, this is what I get :
file_in_id|file_in_state|A |B |C |D |E |
----------------------------------------
F01 |1 |1 |3 |9 |0 |12|
F02 |2 |2 |0 |0 |0 |0 |
F21 |1 |1 |4 |0 |0 |0 |
F41 |2 |2 |0 |0 |0 |0 |
Could somebody tells me what is wrong with this query and how can I fix it to get what I would like to have as a result.
It's very important, this is how the input table 3 should be :
Input Table t3:
line_out|*file_out*| file_in| l_out_state|
---------------------------------------
D001 |W01 | F01 | 1 |
D002 |W01 | F01 | 1 |
D003 |W01 | F01 | 1 |
This query gives desired result:
select file_in, f_in_state,
count(case l_in_state when '1' then 1 end) a,
count(case l_in_state when '2' then 1 end) b,
count(case l_in_state when '3' then 1 end) c,
count(case l_out_state when '1' then 1 end) d,
count(case l_out_state when '2' then 1 end) e
from t1
left join t2 using (file_in)
left join t3 using (file_in, line_in)
group by file_in, f_in_state
order by file_in
You could also use pivot if you have Oracle 11g or above.
Test:
with t1(file_in, f_in_state) as (
select 'F01', '1' from dual union all
select 'F02', '2' from dual union all
select 'F21', '1' from dual union all
select 'F41', '2' from dual ),
t2(line_in, file_in, l_in_state) as (
select 'L001', 'F01', '1' from dual union all
select 'L002', 'F01', '2' from dual union all
select 'L003', 'F01', '2' from dual union all
select 'L004', 'F01', '2' from dual union all
select 'L005', 'F21', '1' from dual union all
select 'L006', 'F21', '1' from dual union all
select 'L007', 'F21', '1' from dual union all
select 'L008', 'F21', '1' from dual ),
t3(line_out, line_in, file_in, l_out_state) as (
select 'D001', 'L001', 'F01', '1' from dual union all
select 'D002', 'L002', 'F01', '1' from dual union all
select 'D003', 'L003', 'F01', '1' from dual )
select file_in, f_in_state,
count(case l_in_state when '1' then 1 end) a,
count(case l_in_state when '2' then 1 end) b,
count(case l_in_state when '3' then 1 end) c,
count(case l_out_state when '1' then 1 end) d,
count(case l_out_state when '2' then 1 end) e
from t1
left join t2 using (file_in)
left join t3 using (file_in, line_in)
group by file_in, f_in_state
order by file_in
Output:
FILE_IN F_IN_STATE A B C D E
------- ---------- ---------- ---------- ---------- ---------- ----------
F01 1 1 3 0 3 0
F02 2 0 0 0 0 0
F21 1 4 0 0 0 0
F41 2 0 0 0 0 0
Variant with SUM
Select a.file_in, a.f_in_state,
Sum(Case When b.l_in_state=1 Then 1 Else 0 End) A,
Sum(Case When b.l_in_state=2 Then 1 Else 0 End) B,
Sum(Case When b.l_in_state=3 Then 1 Else 0 End) C,
Sum(Case When c.l_out_state=1 Then 1 Else 0 End) D,
Sum(Case When c.l_out_state=2 Then 1 Else 0 End) E
From T1 a
Left join T2 b on a.file_in=b.file_in
Left join T3 c on a.file_in=c.file_in and b.line_in=c.line_in
GROUP BY a.file_in, a.f_in_state
ORDER BY a.file_in

Oracle count sub types group by sub type along with main type

My Table Structure
Id| Sub_Type| Main_Type
---|------- | -------
1 | high | c1
2 | low | c1
3 | high | c1
4 | low | c1
5 | high | c2
6 | low | c2
Expected result
count|Sub_Type|Main_Type
-----|------- |---------
2 | high | c1
2 | low | c1
1 | high | c2
1 | low | c2
I tried with below group by clause but was returning the count of main types
Here is my query
SELECT COUNT(*) AS Count, Sub_Type, Main_Type FROM CHANGE GROUP BY Sub_Type, Main_Type
You never showed us your query, but I am guessing that you grouped by the wrong column(s). Just do a GROUP BY on the sub and main type columns, and select the count.
SELECT
COUNT(*),
Sub_Type,
Main_Type
FROM yourTable
GROUP BY
Sub_Type,
Main_Type
ORDER BY
Main_Type,
Sub_Type

Oracle Group by salary range(0-999,1000-1999,2000-2999,...) [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
user_id | username | salary |
+---------+----------+------+
| 1 | John | 4000 |
| 2 | Paul | 0900 |
| 3 | Adam | 0589 |
| 4 | Ben | 2154 |
| 5 | Charles | 2489 |
| 6 | Dean | 2500 |
| 7 | Edward | 2900 |
| 8 | Fred | 2800 |
| 9 | George | 4100 |
| 10 | Hugo | 5200 |
I need output like this
range count
--------------------
0-999 2
1000-1999 0
2000-2999 5
3000-3999 0
4000-4999 2
5000-5999 1
Here is an attempt:
with w as
(
select 1000 * (level - 1) low, 1000 * level high from dual
connect by level <= 10
)
select w.low, w.high, sum(decode(t.user_id, null, 0, 1)) nb
from w, test_epn t
where w.low <= t.salary (+)
and w.high > t.salary (+)
group by w.low, w.high
order by w.low
;
This gives:
1 0 1000 2
2 1000 2000 0
3 2000 3000 5
4 3000 4000 0
5 4000 5000 2
6 5000 6000 1
7 6000 7000 0
8 7000 8000 0
9 8000 9000 0
10 9000 10000 0
SQL> col range format a30
SQL> with t as (
2 select 'John' name, 4000 sal from dual union all
3 select 'Paul' name, 900 from dual union all
4 select 'Adam' name, 589 from dual union all
5 select 'Ben' name, 2154 from dual union all
6 select 'Charles' name, 2489 from dual union all
7 select 'Dean' name, 2500 from dual union all
8 select 'Edward' name, 2900 from dual union all
9 select 'Fred' name, 2800 from dual union all
10 select 'George' name, 4100 from dual union all
11 select 'Hugo' name, 5200 from dual
12 )
13 select to_char(pvtid*1000)||'-'||to_char(pvtid*1000+999) range, count(t.sal)
14 from t
15 ,
16 (
17 select rownum-1 pvtid
18 from dual connect by level <= (select floor(max(sal)/1000) from t)+1
19 ) piv
20 where piv.pvtid = floor(t.sal(+)/1000)
21 group by piv.pvtid
22 order by 1
23 /
RANGE COUNT(T.SAL)
------------------------------ ------------
0-999 2
1000-1999 0
2000-2999 5
3000-3999 0
4000-4999 2
5000-5999 1
Oracle 11g R2 Schema Setup:
create table test_table as
select 1 user_id, 'John' username , 4000 salary from dual union all
select 2 , 'Paul' , 0900 from dual union all
select 3 , 'Adam' , 0589 from dual union all
select 4 , 'Ben' , 2154 from dual union all
select 5 , 'Charles' , 2489 from dual union all
select 6 , 'Dean' , 2500 from dual union all
select 7 , 'Edward' , 2900 from dual union all
select 8 , 'Fred' , 2800 from dual union all
select 9 , 'George' , 4100 from dual union all
select 10 , 'Hugo' , 5200 from dual
Query 1:
with range_tab(f,t) as (select (level - 1)*1000 , (level - 1)*1000 + 999
from dual
connect by (level - 1)*1000 <= (select max(salary) from test_table))
select f ||'-'|| t as range, count(user_id)
from test_table
right outer join range_tab on (salary between f and t)
group by f, t
order by 1
[Results][2]:
| RANGE | COUNT(USER_ID) |
|-----------|----------------|
| 0-999 | 2 |
| 1000-1999 | 0 |
| 2000-2999 | 5 |
| 3000-3999 | 0 |
| 4000-4999 | 2 |
| 5000-5999 | 1 |
In case of fixed interval you can also use Oracle WIDTH_BUCKET function.
select count(*),
(WIDTH_BUCKET(salary, 0, 10000,10)-1)*1000 ||'-'||to_char(WIDTH_BUCKET(salary, 0, 10000,10)*1000-1) as salary_range
from table1
group by WIDTH_BUCKET(salary, 0, 10000,10)
order by salary_range;
| COUNT(*) | SALARY_RANGE |
|----------|--------------|
| 2 | 0-999 |
| 5 | 2000-2999 |
| 2 | 4000-4999 |
| 1 | 5000-5999 |
Disadvantage is: It does not count empty buckets, but maybe this satisfy your needs anyway.

Resources