Hello stackoverflow friends!
Please help, i have to assign a value since 100 to 300 in an cursor or a type or a table (i don't know which of them are better for this project). First i want to fill the type or cursor with those values (10M to 30M) and then choose randomly one of them but just once, i mean, it can't pick 102 twice, for example. There are millions of entries and i wouldn't want to affect the performance of the database. I tried do it with a cursor and n+1 but its so slow...
Thanks my Oracle's friends for your help and suggestions.
I believe n + 1
is slow; if you do it that way, it certainly takes time to insert 20 million rows one by one. Another option is to use row generator.
I have 21XE on laptop (nothing special; Intel i5, 8GB RAM) which isn't enough memory to do it in one go:
SQL> create table test (id number);
Table created.
SQL> insert into test
2 select 10e6 + level - 1
3 from dual
4 connect by level <= 20e6;
insert into test_20mil
*
ERROR at line 1:
ORA-30009: Not enough memory for CONNECT BY operation
SQL>
If you ask whether it really works?, the answer is yes - see for a small sample:
SQL> select level
2 from dual
3 connect by level <= 5;
LEVEL
----------
1
2
3
4
5
SQL>
Therefore, I used a loop to do it 20 times, each of them inserting 1 million rows at a time into a table (not row-by-row). Why not collection? Memory issues!
SQL> create table test (id number primary key, cb_picked number(1));
Table created.
SQL> set serveroutput on
SQL> set timing on
SQL> declare
2 l_mil number := 10e6;
3 begin
4 for i in 1 .. 20 loop
5 insert into test (id)
6 select l_mil + level - 1
7 from dual
8 connect by level <= 1e6;
9 dbms_output.put_Line('inserted ' || sql%rowcount);
10 l_mil := l_mil + 1e6;
11 end loop;
12 end;
13 /
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
inserted 1000000
PL/SQL procedure successfully completed.
Elapsed: 00:01:25.77
As you can see, it took slightly less than minute and a half.
SQL> set timing off
SQL> select count(*) from test;
COUNT(*)
----------------
20,000,000
SQL> select min(id), max(id) from test;
MIN(ID) MAX(ID)
---------------- ----------------
10,000,000 29,999,999
SQL>
That was inserting; what about fetching randomly picked rows? Use dbms_random.value
function. To avoid selecting already picked values twice, update table's cb_picked
column. To do that, create an autonomous transaction function (why? So that you could perform DML - update
- and return value).
SQL> create or replace function f_rnd
2 return number
3 is
4 pragma autonomous_transaction;
5 retval number;
6 begin
7 select id
8 into retval
9 from test
10 where cb_picked is null
11 and id = round(dbms_random.value(10e6, 20e6));
12 update test set cb_picked = 1
13 where id = retval;
14 commit;
15 return retval;
16 end;
17 /
Function created.
Let's try it:
SQL> select f_rnd from dual;
F_RND
----------
19191411
SQL> select f_rnd from dual;
F_RND
----------
16411522
SQL> select * from test where cb_picked = 1;
ID CB_PICKED
---------- ----------
16411522 1
19191411 1
SQL>
That's it, I guess.