Simplest way to repeat every N rows in sql - sql

I know this is possible through some complex techniques, i want to know any simplest way to achieve this patter that every 10 rows should repeat .
for example
select a,b from tablename; (repeating 2 for example)
will give
a1,b1
a2,b2
a1,b1
a2.b2
a3,b3
a4,b4
a3,b3
a4,b4
where if it was 10 it will go like
a1,b1 to a10,b10 again a1,b1 to a10,b10
then
a11,b11 to a20,b20 again a11,b11 to a20,b20
and so on

You want blocks of ten rows repeated twice. So to get:
rows 1 to 10
rows 1 to 10
rows 11 to 20
rows 11 to 20
...
In order to get rows n-fold cross join with a table holding n records. (You get such for instance by querying a big enough table and stop at rowcount n.)
You also need the row number of your original records, so you can get block 1 first, then block 2 and so on. Use integer division to get from row numbers to blocks.
select t.a, t.b
from (select a, b, row_number() over (order by a, b) as rn from tablename) t
cross join (select rownum as repeatno from bigenoughtable where rownum <= 2) r
order by trunc((t.rn -1) / 10), r.repeatno, t.a, t.b;

Use a CTE and union all:
with rows as (
select a, b
from tablename
where rownum <= 2
)
select *
from rows
union all
select *
from rows;
Just some caveats to this. You should use an order by if you want particular rows from the table. This is important, because the same select can return different sets of rows. Actually, considering this, a better way is probably:
with rows as (
select a, b
from tablename
where rownum <= 2
)
select *
from rows cross join
(select 1 as n from dual union all select 2 from dual) n;

I would rather not use UNION so many times. My way would be CONNECT BY ROWNUM <=N. Actually a CARTESIAN JOIN. So, basically you need a ROW GENERATOR to cartesian join with it.,
Update
For example, this will repeat 10 rows 2 times -
SQL> WITH t AS
2 ( SELECT 'a1' A, 'b1' b FROM dual
3 UNION ALL
4 SELECT 'a2' a, 'b2' b FROM dual
5 UNION ALL
6 SELECT 'a3' a, 'b3' b FROM dual
7 UNION ALL
8 SELECT 'a4' A, 'b4' b FROM dual
9 UNION ALL
10 SELECT 'a5' A, 'b5' b FROM dual
11 UNION ALL
12 SELECT 'a6' a, 'b6' b FROM dual
13 UNION ALL
14 SELECT 'a7' A, 'b7' b FROM dual
15 UNION ALL
16 SELECT 'a8' a, 'b8' b FROM dual
17 UNION ALL
18 SELECT 'a9' a, 'b9' b FROM dual
19 UNION ALL
20 SELECT 'a10' a, 'b10' b FROM dual
21 )
22 SELECT A,B FROM t,
23 (SELECT 1 FROM DUAL CONNECT BY ROWNUM <=2
24 )
25 /
A B
--- ---
a1 b1
a2 b2
a3 b3
a4 b4
a5 b5
a6 b6
a7 b7
a8 b8
a9 b9
a10 b10
a1 b1
a2 b2
a3 b3
a4 b4
a5 b5
a6 b6
a7 b7
a8 b8
a9 b9
a10 b10
20 rows selected.
SQL>
So, above CONNECT BY ROWNUM <=10 means repeat the rows 10 times. If you want it to be repeated N times use CONNECT BY ROWNUM <=N.

Related

Variable ordering based on value Oracle SQL

I have this query that are a number of positions that goes from A1 to A100, then B1 to B100, and so on.
I want to order this query so that the positions appear alphabetically in ascending order, but when it reaches the next letter, it goes descending, and keep this until the end of the table.
Here is an example.
SELECT
POSITION
FROM POSITION_TABLE
-------------------
POSITION
--------
A1
A2
...
A99
A100
B100
B99
B98
...
B2
B1
C1
C2
..
How can I do this ordering inside a query?
I tried ordering by the position using CASE, but I wanted something more generic, in case I needed to increase in size.
You can use:
SELECT position
FROM (
SELECT position,
REGEXP_SUBSTR(position, '^\D+') AS leading_string,
CASE MOD(DENSE_RANK() OVER (ORDER BY REGEXP_SUBSTR(position, '^\D+')), 2)
WHEN 1
THEN ROW_NUMBER() OVER (
PARTITION BY REGEXP_SUBSTR(position, '^\D+')
ORDER BY TO_NUMBER(REGEXP_SUBSTR(position, '\d+$')) ASC
)
ELSE ROW_NUMBER() OVER (
PARTITION BY REGEXP_SUBSTR(position, '^\D+')
ORDER BY TO_NUMBER(REGEXP_SUBSTR(position, '\d+$')) DESC
)
END AS rn
FROM position_table
)
ORDER BY
leading_string,
rn;
Which, for the sample data:
CREATE TABLE position_table (POSITION) AS
SELECT 'A' || LEVEL FROM DUAL CONNECT BY LEVEL <= 10 UNION ALL
SELECT 'B' || LEVEL FROM DUAL CONNECT BY LEVEL <= 10 UNION ALL
SELECT 'C' || LEVEL FROM DUAL CONNECT BY LEVEL <= 10 UNION ALL
SELECT 'D' || LEVEL FROM DUAL CONNECT BY LEVEL <= 10;
Outputs:
POSITION
A1
A2
A3
...
A9
A10
B10
B9
...
B3
B2
B1
C1
C2
C3
...
C9
C10
D10
D9
...
D2
D1
fiddle

First negative number row in Oracle

How can I find the first row where the negative value starts in Oracle? Below is an example.
77
74
67
56
42
24
20
19
-17
-28
-31
-36
I would like to read the row -17 and do some operations on that row.
Any help is greatly appreciated. Thanks
If you want the complete row, you can use:
select t.*
from t
where n < 0
order by n asc
fetch first 1 row only;
If you have another column that specifies the ordering of the rows, then:
select t.*
from t
where n < 0
order by <ordering col> asc
fetch first 1 row only;
To select the maximum negative number you can do:
select max(n)
from t
where n < 0
Remember that in relational database tables, rows do not have inherent ordering. Therefore, in the absence of an ordering criteria, there's no such a thing as "first row where the negative value starts".
Assuming you have a column for sorting, which defines the order of the rows, it could look like this:
with t as (
select 77 a, 1 row_order from dual union all
select 74 a, 2 row_order from dual union all
select 67 a, 3 row_order from dual union all
select 56 a, 4 row_order from dual union all
select 42 a, 5 row_order from dual union all
select 24 a, 6 row_order from dual union all
select 20 a, 7 row_order from dual union all
select 19 a, 8 row_order from dual union all
select -17 a, 9 row_order from dual union all
select -28 a, 10 row_order from dual union all
select -31 a, 11 row_order from dual union all
select -36 a, 12 row_order from dual
), t1 as (
select a, row_number() over (partition by case when a < 0 then 0 else 1 end order by row_order) rn from t
)
select * from t1 where rn = 1 and a < 0;
It's using a window function in order to determine the first rows (here for positive a's and negative a's)
Then it selects the first row encountered that is negative.

Oracle: Analytical functions Sub totals after each change in value

I have the following data (order of records as in the example):
A B
1 10
1 20
1 30
1 40
2 50
2 65
2 75
1 89
1 100
from SQL:
with x as (
select A, B
from (
select 1 as A, 10 as B from dual
union all
select 1 as A, 20 as B from dual
union all
select 1 as A, 30 as B from dual
union all
select 1 as A, 40 as B from dual
union all
select 2 as A, 50 as B from dual
union all
select 2 as A, 65 as B from dual
union all
select 2 as A, 75 as B from dual
union all
select 1 as A, 89 as B from dual
union all
select 1 as A, 100 as B from dual
)
)
select A, B
from X
I want to group the data for each change of value in column A,
I want to get the following result:
A MIN(B) MAX(B)
1 10 40
2 50 75
1 89 100
How to get such a result in the ORACLE 11. I would expect a simple implementation...
This is a gaps and islands problem, solved using row_number analytic function
SELECT a,
MIN(b),
MAX(b)
FROM (
SELECT x.*,
ROW_NUMBER() OVER(
ORDER BY b
) - ROW_NUMBER() OVER(
PARTITION BY a
ORDER BY b
) AS seq
FROM x
)
GROUP BY a,
seq;
Demo

working with permutations of a column values

I am trying from the below table
key val
A 10
B 20
C 30
D 40
to achieve the following result. Column label displays all combinations of key in alphabetical order. Column total displays the addition of the vals for the combination of keys.
label total
A 10
AB 30
ABC 60
ABCD 100
AC 40
AD 50
B 20
BC 50
BCD 90
BD 60
C 30
CD 70
D 40
while managed to get a query going, still not so convinced with it. Looking for better ways to get the same result set. thanks in advance.
with f (rn, key, val) as
(
select rownum, a.* from
(
select 'A' key, 10 val from dual
union all select 'B', 20 from dual
union all select 'C', 30 from dual
union all select 'D', 40 from dual
-- union all select 'E', 50 from dual
-- union all select 'F', 60 from dual
order by 1
) a
)
,
-- irn, ikey, ival: anchor rownum, key and val to remember the starting row
-- rn, key, val: for the current row in the recursion
-- r1: current label in the recursion
-- r2: combination of anchor key and the current row key in the recursion
-- total: addition of all values for keys in r1
rs(irn, ikey, ival, rn, key, val, r1, r2, total) as
(
select rn, key, val, rn, key, val, key, null, val from f
union all
select rs.irn, rs.ikey, rs.ival, f.rn, f.key, f.val, rs.r1 || f.key, rs.ikey || f.key, rs.total+f.val
from rs join f on (f.rn = rs.rn+1)
)
,
-- to add the additional rows required for the r2 col
-- when either r2 is not empty and not the same as r1 in rs
frs(irn, ikey, ival, rn, key, val, r1, r2, total) as
(
select * from rs
union all
select irn, ikey, ival, rn, key, val, r2, r2, ival+val
from frs
where r2 is not null and r1 != r2
)
select r1, total from frs
order by 1
;
Here is one way - using recursive subquery factoring, available since Oracle 11.2:
with
-- Begin test data
test_data ( key, val ) as (
select 'A', 10 from dual union all
select 'B', 20 from dual union all
select 'C', 30 from dual union all
select 'D', 40 from dual
),
-- End of test data (not part of the solution).
-- SQL query begins with the keyword "with" from above and continues below this line.
rec_cte ( label, total, last_symbol ) as (
select key, val, key -- anchor member
from test_data
union all
select r.label || t.key, r.total + t.val, t.key -- recursive member
from rec_cte r join test_data t on r.last_symbol < t.key
)
select label, total
from rec_cte
order by label -- if needed
;
Output:
LABEL TOTAL
----- -----
A 10
AB 30
ABC 60
ABCD 100
ABD 70
AC 40
ACD 80
AD 50
B 20
BC 50
BCD 90
BD 60
C 30
CD 70
D 40
15 rows selected.
You can use a PL/SQL block/Proceduere to achieve the same.
Assuming t_perm is your table, this proc will give you what you want. But currently it is displaying it on dbms_output. You can insert it in a table or display by a cursor etc. But this is the logic
create or replace procedure perm as
v_hold varchar2(20);
v_sum integer:=0;
cursor crs is select * from t_perm;
cursor crs1 is select * from t_perm;
begin
for rec in crs
loop
for rec1 in crs1
loop
if rec.key <= rec1.key then
v_hold:=v_hold||rec1.key;
v_sum:=v_sum+rec1.val;
dbms_output.put_line(v_hold||' '||v_sum);
end if;
end loop;
v_hold:='';
v_sum:=0;
end loop;
end;
Output
A 10
AB 30
ABC 60
ABCD 100
B 20
BC 50
BCD 90
C 30
CD 70
D 40

oracle sql group count

SELECT a,b,count(*)
FROM t
GROUP BY rollup(a,b)
result:
a1, b1, 10
a1, b2, 90
a1, , 100
i need:
a1, b1, 10, 100
a1, b2, 90, 100
how?
This table simulates your situation:
SQL> create table t (a,b)
2 as
3 select 'a1', 'b1'
4 from dual
5 connect by level <= 10
6 union all
7 select 'a1', 'b2'
8 from dual
9 connect by level <= 90
10 /
Table created.
Your result with only three rows misses the grand total, so that's a little inaccurate: rollup(a,b) results in 3 grouping sets with 4 rows.
SQL> select a
2 , b
3 , count(*)
4 from t
5 group by rollup(a,b)
6 /
A B COUNT(*)
-- -- ----------
a1 b1 10
a1 b2 90
a1 100
100
4 rows selected.
With a regular group by and an analytic function on top, you can achieve your desired result set:
SQL> select a
2 , b
3 , count(*)
4 , sum(count(*)) over (partition by a)
5 from t
6 group by a
7 , b
8 /
A B COUNT(*) SUM(COUNT(*))OVER(PARTITIONBYA)
-- -- ---------- -------------------------------
a1 b2 90 100
a1 b1 10 100
2 rows selected.
Regards,
Rob.
Use:
SELECT t.a,
t.b,
COUNT(*),
(SELECT COUNT(*)
FROM YOUR_TABLE
GROUP BY t.a)
FROM YOUR_TABLE t
GROUP BY t.a, t.b