repeat rows in a given sequence format - oracle

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

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

A scenario for combining multiple rows in oracle

I have a table with vaues like
MY_ID(NUMBER) COL_1(NUMBER) COL_2(NUMBER) COL_3(VARCHAR2)
11 1001 NULL GT
11 NULL 1002 TG
11 NULL 1003 TG2
12 1004 NULL GT
12 NULL 1006 TG
12 NULL 1005 TG2
My expected result is
MY_ID(NUMBER) COL_1(NUMBER) COL_2(NUMBER) COL_3(VARCHAR2)
11 1001 1003 TG2
12 1004 1006 TG
I can use MAX for numbers, but how about the Varchar2?
How can I combine multiple rows like this?
MAX aggregate functions can be used for varchar too, see this example:
SELECT my_id, max( col_1 ), max( col_2 ), max( col_3 )
FROM Table1
GROUP BY my_id;
Demo: http://sqlfiddle.com/#!4/406e8d7/6
| MY_ID | MAX(COL_1) | MAX(COL_2) | MAX(COL_3) |
|-------|------------|------------|------------|
| 11 | 1001 | 1003 | TG2 |
| 12 | 1004 | 1006 | TG2 |
However, in your question is stated that for the record 12 must be returned value TG instead TG2. I'm guessing that you want to return not the maximum column value of COL_3, but the value from the record for which the maximum value from column COL2 exists. In such a case you can use a query like this:
SELECT my_id, max( col_1 ), max( col_2 ),
max( col_3 ) KEEP (DENSE_RANK LAST ORDER BY col_2 NULLS FIRST)
FROM Table1
GROUP BY my_id;
Demo: http://sqlfiddle.com/#!4/406e8d7/6
| MY_ID | MAX(COL_1) | MAX(COL_2) | MAX(COL_3)KEEP(DENSE_RANKLASTORDERBYCOL_2NULLSFIRST) |
|-------|------------|------------|------------------------------------------------------|
| 11 | 1001 | 1003 | TG2 |
| 12 | 1004 | 1006 | TG |

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.

SQL bring back highest sum of rows

I'm looking to calculate the highest basket in my set of data but I can't get my head around how I should do it.
I have data like:
OrderID | CustomerID | BasketID | ProductID | Price
1 | 1 | 1 | 221 | 10
2 | 1 | 1 | 431 | 123
3 | 1 | 2 | 761 | 44
4 | 2 | 3 | 12 | 54
5 | 2 | 3 | 102 | 78
6 | 3 | 4 | 111 | 98
7 | 3 | 4 | 41 | 45
8 | 3 | 5 | 65 | 66
9 | 4 | 6 | 32 | 47
10 | 4 | 6 | 118 | 544
Sorry if it seems quite messy.
But I can easily get the SUM with an obvious
SELECT SUM([Price]), BasketID, CustomerID FROM table
GROUP BY BasketID, CustomerID
But how can I filter the list for only the highest priced Basket ID for that CustomerID
Thanks
You can use a CTE (Common Table Expression) with the ROW_NUMBER function:
;WITH HighestPricePerCustomerAndBasket AS
(
SELECT
ID, UserID, ClassID, SchoolID, Created,
ROW_NUMBER() OVER(PARTITION BY BasketID,CustomerID ORDER BY Price DESC) AS 'RowNum'
FROM dbo.YourTable
)
SELECT
[Price], BasketID, CustomerID
FROM HighestPricePerCustomerAndBasket
WHERE RowNum = 1
This CTE "partitions" your data by BasketID,CustomerID, and for each partition, the ROW_NUMBER function hands out sequential numbers, starting at 1 and ordered by Price DESC - so the first row (highest price) gets RowNum = 1 (for each BasketID,CustomerID "partition") which is what I select from the CTE in the SELECT statement after it.
SELECT *
FROM (SELECT *,
DENSE_RANK() OVER (PARTITION BY CustomerID ORDER BY BasketTotal DESC) AS RNK
FROM (SELECT Sum(Price) AS BasketTotal,
BasketID,
CustomerID
FROM Order a
GROUP BY BasketID,
CustomerID
) a
) b
WHERE RNK = 1
I managed to conjure something up that worked.

Resources