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

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.

Related

Column value based on previous column value

I had a result set on Oracle like this table:
Is there a way to add a new column with the values based on the previous TEMREGIONAL column to be like that:
311,1,1,0
430,2,0,1
329,3,0,1
What I want is based on the TEMREGIONAL value, if it is 1, so all rows after that will be 1 to.
So if I have something like that:
311,1,0
430,2,0
329,3,1
334,4,0
323,5,0
324,6,0
326,7,0
The result should be:
311,1,0,0
430,2,0,0
329,3,1,0
334,4,0,1
323,5,0,1
324,6,0,1
326,7,0,1
What I want is to add a new column and after the row with the value 1 at the third column, all rows should have the value 1 in this new column.
Anybody can help me?
You may use ignore nulls addition of lag to find previous 1, turning zeroes to null. This can be done in one pass.
with a(
ID_ORGAO_INTELIGENCIA
, ORD
, TEMREGIONAL
) as (
select 311,1,0 from dual union all
select 430,2,0 from dual union all
select 329,3,1 from dual union all
select 334,4,0 from dual union all
select 323,5,0 from dual union all
select 324,6,0 from dual union all
select 326,7,0 from dual
)
select
a.*
, coalesce(
lag(nullif(TEMREGIONAL, 0))
ignore nulls
over(order by ord asc)
, 0) as prev
from a
ID_ORGAO_INTELIGENCIA | ORD | TEMREGIONAL | PREV
--------------------: | --: | ----------: | ---:
311 | 1 | 0 | 0
430 | 2 | 0 | 0
329 | 3 | 1 | 0
334 | 4 | 0 | 1
323 | 5 | 0 | 1
324 | 6 | 0 | 1
326 | 7 | 0 | 1
db<>fiddle here
For sample data
SQL> select * from test order by ord;
ID_ORGAO_INTELIGENCIA ORD TERMREGIONAL
--------------------- ---------- ------------
311 1 0
430 2 0
329 3 1
334 4 0
323 5 0
324 6 0
326 7 0
7 rows selected.
this might be one option:
SQL> with
2 temp as
3 -- find minimal ORD for which TERMREGIONAL = 1
4 (select min(a.ord) min_ord
5 from test a
6 where a.termregional = 1
7 )
8 select t.id_orgao_inteligencia,
9 t.ord,
10 t.termregional,
11 case when t.ord > m.min_ord then 1 else 0 end new_column
12 from temp m cross join test t
13 order by t.ord;
ID_ORGAO_INTELIGENCIA ORD TERMREGIONAL NEW_COLUMN
--------------------- ---------- ------------ ----------
311 1 0 0
430 2 0 0
329 3 1 0
334 4 0 1
323 5 0 1
324 6 0 1
326 7 0 1
7 rows selected.
SQL>

how to check length of "concatenate" string

Has anybody an idea how to write a query in Oracle to get length of characters:
| user | action |
-------------------------
| mary | aaa | # 3 characters from action
| mary | bbbbb | # 5 characters from action
| mary | c | # 1 character from action
| adam | xx | # 2 characters from action
| adam | yyyy | # 4 characters from action
| adam | zzzzzzz | # 7 characters from action
So in result should be sum of characters for each:
| mary | 9 |
| adam | 13 |
Thanks.
SUM + LENGTH along with GROUP BY user. Sample data in lines #1 - 14; query you need begins at line #15.
SQL> WITH
2 test (cuser, action)
3 AS
4 (SELECT 'mary', 'aaa' FROM DUAL
5 UNION ALL
6 SELECT 'mary', 'bbbbb' FROM DUAL
7 UNION ALL
8 SELECT 'mary', 'c' FROM DUAL
9 UNION ALL
10 SELECT 'adam', 'xx' FROM DUAL
11 UNION ALL
12 SELECT 'adam', 'yyyy' FROM DUAL
13 UNION ALL
14 SELECT 'adam', 'zzzzzzz' FROM DUAL)
15 SELECT cuser, SUM (LENGTH (action))
16 FROM test
17 GROUP BY cuser;
CUSE SUM(LENGTH(ACTION))
---- -------------------
mary 9
adam 13
SQL>
Use the LENGTH function in your aggregation:
SELECT "USER",
SUM( LENGTH( action ) ) AS total_length
FROM table_name
GROUP BY "USER"
Which, for your sample data:
CREATE TABLE table_name( "USER", action ) AS
SELECT 'mary', 'aaa' FROM DUAL UNION ALL
SELECT 'mary', 'bbbbb' FROM DUAL UNION ALL
SELECT 'mary', 'c' FROM DUAL UNION ALL
SELECT 'adam', 'xx' FROM DUAL UNION ALL
SELECT 'adam', 'yyyy' FROM DUAL UNION ALL
SELECT 'adam', 'zzzzzzz' FROM DUAL;
Outputs:
USER | TOTAL_LENGTH
:--- | -----------:
mary | 9
adam | 13
db<>fiddle here

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

repeat rows in a given sequence format

I have a table with following data
Order_no | Part_No | R_from | R_to
1001 | 1010037-00L| 1 | 5
1001 | 1010025-00L| 6 | 12
I need to get the above data to a report in below manner.
R_NO | PART_NO
------------------
1 | 1010037-00L
2 | 1010037-00L
3 | 1010037-00L
4 | 1010037-00L
5 | 1010037-00L
6 | 1010025-00L
7 | 1010025-00L
8 | 1010025-00L
9 | 1010025-00L
10 | 1010025-00L
11 | 1010025-00L
12 | 1010025-00L
Something like:
WITH r_nos ( r_no ) AS (
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= ( SELECT MAX( R_to ) FROM your_table )
)
SELECT r_no,
part_no
FROM r_nos r
INNER JOIN
your_table y
ON ( r.r_no BETWEEN y.r_from AND y.r_to )
Here's an alternative which doesn't require a separate join. You should test both solutions to see which is more performant for your data etc, though.
WITH your_table AS (SELECT 1001 order_no, '1010037-00L' part_no, 1 r_from, 5 r_to FROM dual UNION ALL
SELECT 1001 order_no, '1010025-00L' part_no, 6 r_from, 12 r_to FROM dual)
SELECT r_from + LEVEL -1 r_no,
order_no,
part_no
FROM your_table
CONNECT BY PRIOR order_no = order_no
AND PRIOR part_no = part_no
AND PRIOR sys_guid() IS NOT NULL
AND LEVEL <= r_to - r_from + 1
ORDER BY r_no;
R_NO ORDER_NO PART_NO
---------- ---------- -----------
1 1001 1010037-00L
2 1001 1010037-00L
3 1001 1010037-00L
4 1001 1010037-00L
5 1001 1010037-00L
6 1001 1010025-00L
7 1001 1010025-00L
8 1001 1010025-00L
9 1001 1010025-00L
10 1001 1010025-00L
11 1001 1010025-00L
12 1001 1010025-00L

Resources