Fill in sequence numbers in sql oracle - sql

I have a table that includes two columns, these columns have ranges i.e
Batch from _serial_no to_serial_no
a 1 5
b 2 7
I want to create another column to fill in the gaps for a abd b separately
Something like this
Batch from _serial_no to_serial_no seq_number
a 1 5 1
a 1 5 2
a 1 5 3
a 1 5 4
a 1 5 5
b 2 7 2
b 2 7 3
b 2 7 4
b 2 7 5
b 2 7 6
b 2 7 7
Is there an sql I could use?
I tried something like this but it didn't work
select *
from (
select a.*,rownum n
from my_table a connect by level <= TO_SERIAL_NO
)
where n >= FROM_SERIAL_NO;

SQL> with
2 data as (select 'a' batch, 1 from_serial_number, 5 to_serial_number from dual
3 union all
4 select 'b' batch, 2 from_serial_number, 7 to_serial_number from dual),
5 seq as (select rownum n# from dual connect by level <= (select max(to_serial_number) from data))
6 select
7 data.*,
8 seq.n#
9 from
10 data,
11 seq
12 where
13 seq.n# between data.from_serial_number and data.to_serial_number
14 order by
15 1, 2, 4;
BATCH FROM_SERIAL_NUMBER TO_SERIAL_NUMBER N#
----- ------------------ ---------------- ----------
a 1 5 1
a 1 5 2
a 1 5 3
a 1 5 4
a 1 5 5
b 2 7 2
b 2 7 3
b 2 7 4
b 2 7 5
b 2 7 6
b 2 7 7
11 rows selected

Yet another option:
SQL> with test (batch, from_serial_no, to_serial_no) as
2 (select 'a', 1, 5 from dual union
3 select 'b', 2, 7 from dual
4 )
5 select
6 batch,
7 from_serial_no,
8 to_serial_no,
9 froM_serial_no + column_value - 1 seq_number
10 from test,
11 table(cast(multiset(select level from dual
12 connect by level <= to_serial_no - from_serial_no + 1
13 ) as sys.odcinumberlist))
14 order by batch, seq_number;
B FROM_SERIAL_NO TO_SERIAL_NO SEQ_NUMBER
- -------------- ------------ ----------
a 1 5 1
a 1 5 2
a 1 5 3
a 1 5 4
a 1 5 5
b 2 7 2
b 2 7 3
b 2 7 4
b 2 7 5
b 2 7 6
b 2 7 7
11 rows selected.
SQL>

Using a join
select d.*, t.seq_number
from data d
join
(
SELECT from_serial_no + level - 1 seq_number
FROM (select min(from_serial_no) from_serial_no,
max(to_serial_no) to_serial_no
from data) t
CONNECT BY from_serial_no + level - 1 <= to_serial_no
) t on d.from_serial_no <= t.seq_number and
d.to_serial_no >= t.seq_number
order by d.batch, t.seq_number;
dbfiddle demo

Related

In DolphinDB, how to group a table by value of a certain column to achieve the same operation as qcut in pandas?

testData = table(1 2 3 4 5 6 7 8 9 10 as id,1 2 3 4 5 6 7 8 9 10 as val)
Divide val column into 2 groups: val<=5, 5<val<=10, and then obtain the sum of each group.
Here you are:
testData = table(1 2 3 4 5 6 7 8 9 10 as id,1 2 3 4 5 6 7 8 9 10 as val)
select sum(val) from testData group by val <= 5

Oracle generate Data

I tried to generate data using the "by level" expression.
I want to generate GROUPNR, starting with 1,
meanwhile each group contains 4 items with nr 1 to 4
This is, how the result should look like
groupnr itemnr
1 1
1 2
1 3
1 4
2 1
2 2
2 3
2 4
3 1
I used this statement, but I have no idea how to handle the nvl2 or other functions to get the right values
select level, floor(level+3/4) GROUPNR, nvl2(0, mod(level,4),4) ITEMNR
from dual
connect by level <= 25;
The result of the select is:
groupnr itemnr
1 1
1 2
1 3
1 0
2 1
2 2
2 3
2 0
4 1
Please give me a hint how to modify the level to get the right values.
Bye Jochen
This could be a way:
select floor((level -1) / 4) +1 as groupNR,
row_number() over (partition by floor((level -1) / 4) +1 order by level) as itemNR
from dual
connect by level <= 25;
or even, without analytic functions:
level - 4*floor((level -1) / 4) as itemNR
TEST:
SQL> select floor((level -1) / 4) +1 as groupNR,
2 row_number() over (partition by floor((level -1) / 4) +1 order by level) as itemNR,
3 level - 4*floor((level -1) / 4) as itemNR_2
4 from dual
5 connect by level <= 25
6 order by level;
GROUPNR ITEMNR ITEMNR_2
---------- ---------- ----------
1 1 1
1 2 2
1 3 3
1 4 4
2 1 1
2 2 2
2 3 3
2 4 4
3 1 1
3 2 2
3 3 3
3 4 4
4 1 1
4 2 2
4 3 3
4 4 4
5 1 1
5 2 2
5 3 3
5 4 4
6 1 1
6 2 2
6 3 3
6 4 4
7 1 1
25 rows selected.
as varaition to Aleksejs solution (Thanks):
select floor((level +3) / 4) as groupNR,
row_number() over (partition by floor((level +3) / 4) +1 order by 1) as itemNR
from dual
connect by level <= 25;
Your query was very close. I suggest you change it to:
select level,
TRUNC((LEVEL-1) / 4) + 1 AS GROUPNR,
mod(LEVEL-1, 4) + 1 AS ITEMNR
from dual
connect by level <= 25
This produces:
LEVEL GROUPNR ITEMNR
1 1 1
2 1 2
3 1 3
4 1 4
5 2 1
6 2 2
7 2 3
8 2 4
9 3 1
10 3 2
11 3 3
12 3 4
13 4 1
14 4 2
15 4 3
16 4 4
17 5 1
18 5 2
19 5 3
20 5 4
21 6 1
22 6 2
23 6 3
24 6 4
25 7 1
LEVEL won't be NULL so there's no need to fiddle around with NVL or anything like that.
Best of luck.

Repeat values with in the GROUP in SQL

I am trying to repeat a row value in the subsequent rows with in GROUP. A Group can have one or more TAG. The requirement is to populate NEW_TAG in the row where the TAG is populated and in the subsequent rows until another TAG populated with in the same group or we reach end of that GROUP.
Current Table Required Table
GROUPID SEQ TAG GROUPID SEQ TAG NEW_TAG
------- --- ---- ------- --- --- --------
1 1 1 1
1 2 1 2
1 3 1 3
1 4 4 1 4 4 4
1 5 1 5 4
1 6 1 6 4
1 7 1 7 4
1 8 1 8 4
2 1 2 1
2 2 2 2
2 3 2 3
2 4 2 4
2 5 5 2 5 5 5
2 6 2 6 5
2 7 2 7 5
2 8 2 8 5
2 9 9 2 9 9 9
2 10 2 10 9
2 11 2 11 9
select
groupid,
seq,
tag,
last_value(tag) ignore nulls over (
partition by groupid
order by seq
) as new_tag
from t
order by groupid, seq;
GRO SEQ TAG NEW_TAG
1 1 - -
1 2 - -
1 3 - -
1 4 4 4
1 5 - 4
1 6 - 4
1 7 - 4
1 8 - 4
2 1 - -
2 2 - -
2 3 - -
2 4 - -
2 5 5 5
2 6 - 5
2 7 - 5
2 8 - 5
2 9 9 9
2 10 - 9
2 11 - 9
19 rows selected.

summarising a 3 months sales report across 2 branches into top 3 product for each month

I have the following REPORT table
m = month,
pid = product_id,
bid = branch_id,
s = sales
m pid bid s
--------------------------
1 1 1 20
1 3 1 11
1 2 1 14
1 4 1 16
1 5 1 31
1 1 2 30
1 3 2 10
1 2 2 24
1 4 2 17
1 5 2 41
2 3 1 43
2 5 1 21
2 4 1 10
2 1 1 5
2 2 1 12
2 3 2 22
2 5 2 10
2 4 2 5
2 1 2 4
2 2 2 10
3 3 1 21
3 5 1 10
3 4 1 44
3 1 1 4
3 2 1 14
3 3 2 10
3 5 2 5
3 4 2 6
3 1 2 7
3 2 2 10
I'd like to have a summary of this sales table
by showing the top 3 sales among the products across all branches.
something like this:
m pid total
---------------------
1 5 72
1 1 50
1 4 33
2 3 65
2 5 31
2 2 22
3 4 50
3 3 31
3 2 24
so on month 1, product #5 has the highest total sales with 72, followed by product #1 is 50.. and so on. if i could separate them into different table for each month would be better
so far what i can do is make a summary for 1 month and shows the entire thing and not top 3.
select pid, sum(s)
from report
where m = 1
group by pid
order by sum(s);
thanks a lot!
Most databases support the ANSI standard window functions. You can do what you want with row_number():
select m, pid, s
from (select r.m, r.pid, sum(s) as s,
row_number() over (partition by m order by sum(s) desc) as seqnum
from report r
group by r.m, r.pid
) r
where seqnum <= 3
order by m, s desc;

SQL or PL/SQL queries to Print sequence of given N numbers

I need help in building the query as the below structured.i have tried using for loop but its just printing as 1 2 3 4 5 6 7 8 9
For example if the N Value is 9 means the output should be like this.
EXAMPLE Output :
0
0 1
0 1 2
0 1 2 3
0 1 2 3 4
0 1 2 3 4 5
0 1 2 3 4 5 6
0 1 2 3 4 5 6 7
0 1 2 3 4 5 6 7 8
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8
0 1 2 3 4 5 6 7
0 1 2 3 4 5 6
0 1 2 3 4 5
0 1 2 3 4
0 1 2 3
0 1 2
0 1
0
To do this just in SQL - albeit with a bind variable if you want to be able to specify n - you need to combine start with the connect by and build from there. This is one way, though I'm pretty sure it can be done without the union:
with t as (
select level as rn, level - 1 as val
from dual
connect by level <= :n + 1
)
select t1.rn as rn,
listagg(t2.val, ' ') within group (order by t2.val) as answer
from t t1
join t t2 on t2.val <= t1.val
group by t1.rn, t1.val
union all
select (2 * (:n + 1)) - t1.rn,
listagg(t2.val, ' ') within group (order by t2.val) as answer
from t t1
join t t2 on t2.val <= t1.val
where t1.rn <= :n
group by t1.rn, t1.val
order by rn;
The CTE generates the numbers 0 to n. The two halves of the union create the mirror halves of the output; the second has the rn <= :n filter to prevent the 'middle' line being duplicated.
With:
var n number;
exec :n := 9;
This gives:
RN ANSWER
------ ----------------------------------------
1 0
2 0 1
3 0 1 2
4 0 1 2 3
5 0 1 2 3 4
6 0 1 2 3 4 5
7 0 1 2 3 4 5 6
8 0 1 2 3 4 5 6 7
9 0 1 2 3 4 5 6 7 8
10 0 1 2 3 4 5 6 7 8 9
11 0 1 2 3 4 5 6 7 8
12 0 1 2 3 4 5 6 7
13 0 1 2 3 4 5 6
14 0 1 2 3 4 5
15 0 1 2 3 4
16 0 1 2 3
17 0 1 2
18 0 1
19 0
Or for 6:
exec :n := 6;
RN ANSWER
------ ----------------------------------------
1 0
2 0 1
3 0 1 2
4 0 1 2 3
5 0 1 2 3 4
6 0 1 2 3 4 5
7 0 1 2 3 4 5 6
8 0 1 2 3 4 5
9 0 1 2 3 4
10 0 1 2 3
11 0 1 2
12 0 1
13 0
You don't really want to see the rn but you can remove that by putting this in a subquery.
This select query gives exact result:
WITH CTE1 AS
(SELECT 9 AS COL FROM DUAL)
,CTE2
AS (
SELECT LEVEL - 1 AS A,
SYS_CONNECT_BY_PATH(LEVEL - 1, ' ') AS B,
COL
FROM DUAL, CTE1
CONNECT BY LEVEL <= COL + 1)
SELECT B
FROM (SELECT A, B FROM CTE2
UNION ALL
SELECT 2 * COL - A, B
FROM CTE2
WHERE A != COL)
ORDER BY A;
OUTPUT:
0
0 1
0 1 2
0 1 2 3
0 1 2 3 4
0 1 2 3 4 5
0 1 2 3 4 5 6
0 1 2 3 4 5 6 7
0 1 2 3 4 5 6 7 8
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8
0 1 2 3 4 5 6 7
0 1 2 3 4 5 6
0 1 2 3 4 5
0 1 2 3 4
0 1 2 3
0 1 2
0 1
0
Using a while loop:
DECLARE
my_limit SIMPLE_INTEGER := 9;
my_step SIMPLE_INTEGER := +1;
i SIMPLE_INTEGER := 0;
s VARCHAR2(32000);
BEGIN
WHILE (i > -1) LOOP
s := '';
FOR j IN 0 .. i LOOP
IF j>0 THEN s := s || ' '; END IF;
s := s || to_char(j);
END LOOP;
dbms_output.put_line(s);
IF (i >= my_limit) THEN my_step := -1; END IF;
i := i + my_step;
END LOOP;
END;
/
I think, that sys_connect_by_path is what you are looking for.
Oracle Doc
SYS_CONNECT_BY_PATH is valid only in hierarchical queries. It returns the path of a column value from root to node, with column values separated by char for each row returned by CONNECT BY condition.
with desired_number as (select 5 as nm from dual)
,tree
as ( select level - 1 as a, sys_connect_by_path(level - 1, ' ') as b, nm
from dual, desired_number
connect by level <= nm + 1)
select *
from (select a, b from tree
union all
select 2 * nm - a, b
from tree
where a != nm)
order by a
Btw, how do you format it like sql?
You may find connect by to be a useful basis for working out the answer.
select level n
from dual
connect by level <= 10
Using three nested for loops:
DECLARE
my_limit SIMPLE_INTEGER := 9;
PROCEDURE one_line(n simple_integer) IS
s VARCHAR2(4000);
BEGIN
FOR j IN 0 .. n LOOP
IF j>0 THEN s := s || ' '; END IF;
s := s || to_char(j);
END LOOP;
dbms_output.put_line(s);
END one_line;
BEGIN
FOR i IN 0 .. my_limit LOOP
one_line(i);
END LOOP;
FOR i IN REVERSE 0 .. my_limit-1 LOOP
one_line(i);
END LOOP;
END;
/
Got it at last:
WITH CTE AS
(
SELECT LEVEL-1 COL
FROM DUAL
CONNECT BY LEVEL <= 10
)
SELECT SYS_CONNECT_BY_PATH(COL, ' ') COL
FROM ( SELECT COL,
ROW_NUMBER() OVER (ORDER BY COL) RN
FROM CTE)
START WITH RN = 1
CONNECT BY PRIOR RN = RN - 1
UNION ALL
SELECT COL FROM(
SELECT RPAD(SYS_CONNECT_BY_PATH(COL, ' ') , '20' )COL
FROM ( SELECT COL,
ROW_NUMBER() OVER (ORDER BY COL) RN
FROM CTE)
START WITH RN = 1
CONNECT BY PRIOR RN = (RN - 1 )
ORDER BY COL DESC
);
Produces This output:
0
0 1
0 1 2
0 1 2 3
0 1 2 3 4
0 1 2 3 4 5
0 1 2 3 4 5 6
0 1 2 3 4 5 6 7
0 1 2 3 4 5 6 7 8
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8
0 1 2 3 4 5 6 7
0 1 2 3 4 5 6
0 1 2 3 4 5
0 1 2 3 4
0 1 2 3
0 1 2
0 1
0