SQL Inserting in loop using value (sysguid) multiple times - sql

I currently have the following script for inserting some dummy data into one of my tables for testing purposes.
I can call it and pass the number of entries I would like to have. That's working well.
Now the problem is that some of the data is not working.
The CRecordID should have in all three lines (12, 17, 19) the same value.
Is there a way to archive this?
WHENEVER SQLERROR EXIT
PROMPT inserting
INSERT INTO MySchema.MyTable (MYRECORD, TMSTAMP, SHORTRECORD, CRecordID) (
SELECT
'<MYRECORD>
<TimeStamp>'||TO_CHAR(SYSDATE,'DD.MM.YY')||' '||to_char(to_date('2000-01-01', 'yyyy-mm-dd')+dbms_random.value(1,1000), 'HH24:MI:SS')||'</TimeStamp>
<User>Test_User_1</User>
<Application>Application1</Application>
<Action>Dummy action text</Action>
<INFO>dummy info text</INFO>
<CRecordID>'||'CID_'||sys_guid()||'</CRecordID> -- line 12
</MYRECORD>',
to_date(SYSDATE,'DD.MM.YY'),
'<SHORTRECORD>
<User>Test_User_1</User>
<CRecordID>'||'CID_'||sys_guid()||'</CRecordID> -- line 17
</SHORTRECORD>',
'CID_'||sys_guid() -- line 19
FROM DUAL connect by level <= &1
);
COMMIT;
PROMPT inserting done
Note: The Database is an Oracle DB.

Select SYS_GUID() separately (using a CTE, for example, as in my example) and concatenate its value with your columns (I used L12 and L17 to indicate lines 12 and 17 you mentioned).
SQL> with gujda as
2 (select sys_guid() guj
3 from dual
4 connect by level <= 4
5 )
6 select 'L12_' || g.guj l12,
7 'L17_' || g.guj l17
8 from gujda g;
L12 L17
------------------------------------ ------------------------------------
L12_EFB5A4947D2E4B7BBE6017E57C673ABF L17_EFB5A4947D2E4B7BBE6017E57C673ABF
L12_3E2D5B50D7C44C7FA6073A9F739687CF L17_3E2D5B50D7C44C7FA6073A9F739687CF
L12_724C21F7914B423B8CBDDC6A44AD2016 L17_724C21F7914B423B8CBDDC6A44AD2016
L12_F15D6C9865424E5C8FFFEA9C09DD6D37 L17_F15D6C9865424E5C8FFFEA9C09DD6D37
SQL>

Related

How do you insert multiple copies of a record in PL/SQL based on conditions

I'm making a task tracking application in Oracle Apex. I'm having trouble figuring out how to insert multiple copies of the same record based on the RESOURCES value. When assigning a task, it may have more than one resource attached to it. I believe the delimiter in Apex is a colon ':' character. How do I insert a task record the same amount of times as there are resources attached to the task (and have each resource attached to one)? RESOURCES is of type VARCHAR2.
INSERT INTO TASKS_PM (TASK_ID,PROJECTID,START_DATE,END_DATE,DESCRIPTION,TASK_NAME,RESOURCES,PRIORITY,STATUS,PROGRESS,HAS_PARENT,PARENT_TASK)
VALUES (
null,
:P3_PROJECTID,
TO_DATE(:P3_START_DATE,'DD-MON-YYYY'),
TO_DATE(:P3_END_DATE,'DD-MON-YYYY'),
:P3_DESCRIPTION,
:P3_TASK_NAME,
:P3_RESOURCES,
:P3_PRIORITY,
:P3_STATUS,
:P3_PROGRESS,
:P3_HAS_PARENT,
:P3_PARENT_TASK);
For example, if the following resources were attached to an insert: "Bobby Joe" and "Tyrion Lannister", there would be two copies of this record made, one with Bobby Joe as the resource, and the other with Tyrion Lannister as the resource.
Thanks!
You'd split P3_RESOURCES into rows; here's one way to do it (I'm using a CTE which "simulates" some of the values you have in page items):
SQL> with temp (p3_projectid, p3_description, p3_resources) as
2 (select 1, 'Some description', 'Bobby Joe:Tyrion Lannister' from dual)
3 select p3_projectid,
4 p3_description,
5 regexp_substr(p3_resources, '[^:]+', 1, level) p3_resource
6 from temp
7 connect by level <= regexp_count(p3_resources, ':') + 1;
P3_PROJECTID P3_DESCRIPTION P3_RESOURCE
------------ ---------------- --------------------
1 Some description Bobby Joe
1 Some description Tyrion Lannister
SQL>
In your case, you'd
insert into tasks_pm (projectid, description, resources)
select :p3_projectid,
:p3_description,
regexp_substr(:p3_resources, '[^:]+', 1, level)
from dual
connect by level <= regexp_count(:p3_resources, ':') + 1;
(add other columns yourself)

Oracle get all matched occurrences from one column with REGEXP

Been using this query but I don't get what I want, anyone can help, please? I want to consolidate all the matched multiple occurrences and display the records in the table.
SELECT REGEXP_SUBSTR(STR,'^C-.+|^WHT.+',1, LEVEL,1,'m') AS CHARGE_NUMBER
FROM
(
SELECT DBMS_LOB.SUBSTR(COL_DATA, 2000,1) AS STR, TRANX_DATE, USER_ID
FROM TABLE_1
)
CONNECT BY LEVEL <= LENGTH(REGEXP_REPLACE(STR,'^C-.+|^WHT.+',1,1,'m'))+1
Data in one Column
A-DATE|27SEP2021|TRANSACTION_NUMBER|0001|USERID|1000
B-THE|QUICK|BROWN|123467899|OVER|THE|1234567899|DOG
C-50000000001
WHT639281234567
B-THE|123456789|BROWN|FOX|OVER|THE|LAZY|DOG
C-50000000002
WHT639281234568
B-THE|1234567899|BROWN|123457899|OVER|THE|LAZY|DOG
C-50000000003
B-THE|QUICK|BROWN|1234567899|OVER|1234567899|LAZY|DOG
WHT639281234569
B-THE|QUICK|BROWN|FOX|OVER|THE|LAZY|DOG
C-50000000004
WHT639281234570
B-THE|1234567899|BROWN|123467899|OVER|THE|1234567899|DOG
DESIRED RESULT:
CHARGE_NUMBER
50000000001
639281234567
50000000002
639281234568
50000000003
639281234569
50000000004
639281234570
Here's one option: select values you need (using regex), then remove superfluous characters (C- and WHT), looping through the string as many times as there are WHTs times 2 (because you have C- and WHTs to look for).
SQL> with test (col) as
2 (select 'A-DATE|27SEP2021|TRANSACTION_NUMBER|0001|USERID|1000
3 B-THE|QUICK|BROWN|FOX|OVER|THE|LAZY|DOG
4 C-50000000001
5 WHT639281234567
6 B-THE|QUICK|BROWN|FOX|OVER|THE|LAZY|DOG
7 C-50000000002
8 WHT639281234568
9 B-THE|QUICK|BROWN|FOX|OVER|THE|LAZY|DOG
10 C-50000000003
11 B-THE|QUICK|BROWN|FOX|OVER|THE|LAZY|DOG
12 WHT639281234569
13 B-THE|QUICK|BROWN|FOX|OVER|THE|LAZY|DOG
14 C-50000000004
15 WHT639281234570
16 B-THE|QUICK|BROWN|FOX|OVER|THE|LAZY|DOG' from dual
17 )
18 select replace(replace(regexp_substr(col, 'C-\d+|WHT\d+', 1, level), 'C-', ''), 'WHT', '') charge_number
19 from test
20 connect by level <= regexp_count(col, 'WHT') * 2;
CHARGE_NUMBER
--------------------------------------------------------------------------------
50000000001
639281234567
50000000002
639281234568
50000000003
639281234569
50000000004
639281234570
8 rows selected.
SQL>

Oracle Insert Value Into Middle of Existing Value

This is similar to my previous question. I have the following partial docket numbers:
docket_number
-------------
2012JV592
2016DR138
2018JV84
If the docket number is less than 10 digits, then I need to insert 0's after the second letter until the lenth is 10 digits. Updated docket numbers would look like this.
docket_number
-------------
2012JV0592
2016DR0138
2018JV0084
One option might be to
split docket_number into two parts: first part contains digits and letters, the second contains the trailing number
result is concatenation of the first part and the second part left padded with zeroes up to total length of 10 characters
SQL> with test (docket_number) as
2 (select '2012JV592' from dual union all
3 select '2016DR138' from dual union all
4 select '2018JV84' from dual
5 ),
6 temp as
7 (select docket_number,
8 regexp_substr(docket_number, '[[:digit:]]+[[:alpha:]]+') part1,
9 regexp_substr(docket_number, '[[:digit:]]+$') part2
10 from test
11 )
12 select case when length(docket_number) < 10 then
13 part1 || lpad(part2, 10 - length(part1), '0')
14 else docket_number
15 end result
16 from temp;
RESULT
--------------------------------------------------------------------------------
2012JV0592
2016DR0138
2018JV0084
SQL>
How to update rows in a table? By using such a SELECT in UPDATE, e.g.
SQL> select * from test;
DOCKET_NUM
----------
2012JV592
2016DR138
2018JV84
SQL> update test a set
2 a.docket_number =
3 (with temp as
4 (select b.docket_number,
5 regexp_substr(b.docket_number, '[[:digit:]]+[[:alpha:]]+') part1,
6 regexp_substr(b.docket_number, '[[:digit:]]+$') part2
7 from test b
8 )
9 select case when length(t.docket_number) < 10 then
10 t.part1 || lpad(t.part2, 10 - length(t.part1), '0')
11 else docket_number
12 end
13 from temp t
14 where t.docket_number = a.docket_number
15 );
3 rows updated.
SQL> select * from test;
DOCKET_NUM
----------
2012JV0592
2016DR0138
2018JV0084
SQL>
You can split the data into three parts as digit group1, letters group, and digit group2 by using regexp_substr() functions, and lpad() function in order to add zeroes just before the second digit group, and then concatenate them directly by using || operators, assuming that you have the same data model for the whole table,
UPDATE t
SET docket_number = regexp_substr(docket_number,'[[:digit:]]+')||
regexp_substr(docket_number,'[[:alpha:]]+')||
lpad('0',10-length(docket_number),'0')||
regexp_substr(docket_number,'[[:digit:]]+$')
Demo
Is it the case that your docket_number should always follow the format 4 digits (year?) followed by 2 letters followed by 4 digits. Then simple sub-string of the docket_number and subsequent re-concatenation is sufficient.
select docket_number
, substr(docket_number,1,6) || lpad(nvl(substr(docket_number,7),'0'),4,'0')
from test_dn
where length(docket_number) < 10
order by docket_number;
and for update:
update test_dn
set docket_number = substr(docket_number,1,6) || lpad(nvl(substr(docket_number,7),'0'),4,'0')
where length(docket_number) < 10;
If the format holds true then, depending on table size, this could be significantly faster as regular expressions are relative slow.

FETCH DATA BASED ON PARTICULAR STRING

I need to fetch records based on particular type(FFRT-TR= ) and then replace it. For example below are the record types
20017546 FFRT-TR= 3456
TT:SX 2398 FFRT-TR=6532
I need to fetch records which contains FFRT-TR= and then replace the whole the of FFRT-TR=3456 Required Output based on the above examples: 20017546(for 1st example) 2398(for 2nd example)
Please help. Thanks in advance
Something like this? Oracle-ish (you didn't specify the database you use), but - hopefully - you'll be able to rewrite it if necessary.
SQL> with test (col) as
2 (select '20017546 FFRT-TR= 3456' from dual union
3 select 'TT:SX 2398 FFRT-TR=6532' from dual
4 )
5 select
6 col,
7 replace(regexp_substr(col, '\d+ FFRT-TR='), 'FFRT-TR=', '') result
8 from test;
COL RESULT
----------------------- ----------------------------------------------------
20017546 FFRT-TR= 3456 20017546
TT:SX 2398 FFRT-TR=6532 2398
SQL>

oracle function call is being cached during bulk insert

when inserting bulk rows and using a function call as one of the column values i'm getting exactly the same values for every 10-11 rows from the function. the function is actually generating UUID values and returns unique results. If i replace the function call in the insert statement with the actual code of the function it never repeats.
So what i conclude from this is that oracle actually caches the result of the function and calls it only once for every 10-11 rows it's inserting. how can i change this behavior?
the function i'm calling i've taken from http://www.oracle-base.com/articles/9i/UUID9i.php :
create or replace
FUNCTION new_uuid RETURN VARCHAR2 AS
l_seed BINARY_INTEGER;
l_random_num NUMBER(5);
l_date VARCHAR2(25);
l_random VARCHAR2(4);
l_ip_address VARCHAR2(12);
BEGIN
l_seed := TO_NUMBER(TO_CHAR(SYSDATE,'YYYYDDMMSS'));
DBMS_RANDOM.initialize (val => l_seed);
l_random_num := TRUNC(DBMS_RANDOM.value(low => 1, high => 65535));
DBMS_RANDOM.terminate;
l_date := conversion_api.to_hex(TO_NUMBER(TO_CHAR(SYSTIMESTAMP,'FFSSMIHH24DDMMYYYY')));
l_random := RPAD(conversion_api.to_hex(l_random_num), 4, '0');
l_ip_address := conversion_api.to_hex(TO_NUMBER(REPLACE(NVL(SYS_CONTEXT('USERENV','IP_ADDRESS'), '123.123.123.123'), '.', '')));
RETURN SUBSTR(l_date, 1, 8) || '-' ||
SUBSTR(l_date, 9, 4) || '-' ||
SUBSTR(l_date, 13, 4) || '-' ||
RPAD(SUBSTR(l_date, 17), 4, '0') || '-' ||
RPAD(L_RANDOM || L_IP_ADDRESS, 12, '0');
END;
and here's the insert statement i'm using:
INSERT INTO My_TABLE(ID, NAME,)
SELECT NEW_UUID(), NAME
FROM MY_TABLE2;
COMMIT;
the select inside this statement produces lots of repeating UUIDs. while this statement produces unique ones:
SELECT RPAD(RPAD(my_schema.conversion_api.to_hex(TRUNC(DBMS_RANDOM.VALUE( 1, 65535))), 4, '0') || my_schema.conversion_api.to_hex(TO_NUMBER(REPLACE(NVL(SYS_CONTEXT('USERENV','IP_ADDRESS'), '123.123.123.123'), '.', ''))), 12, '0') sss
FROM my_schema.MY_TABLE
APC's diagnostic is correct. You need to have entropy in your random generator seed.
Though, Oracle already has a unique id generator which is SYS_GUID().
SELECT sys_guid(), name FROM my_table2;
You can try this which produces 9 GUIDs:
SELECT sys_guid() from dual connect by level < 10;
Don't try to reinvent the wheel when it already exists.
The thing is, "random" isn't actually random. Given the same seed for DBMS_RANDOM.INITIALISE(), subsequent calls to DBMS_RANDOM.VALUE() will return the same result. Check it out:
SQL> exec DBMS_RANDOM.initialize (val => 1)
PL/SQL procedure successfully completed.
SQL> select TRUNC(DBMS_RANDOM.value(low => 1, high => 65535)) from dual
2 /
TRUNC(DBMS_RANDOM.VALUE(LOW=>1,HIGH=>65535))
--------------------------------------------
49214
SQL> r
1* select TRUNC(DBMS_RANDOM.value(low => 1, high => 65535)) from dual
TRUNC(DBMS_RANDOM.VALUE(LOW=>1,HIGH=>65535))
--------------------------------------------
56385
SQL> r
1* select TRUNC(DBMS_RANDOM.value(low => 1, high => 65535)) from dual
TRUNC(DBMS_RANDOM.VALUE(LOW=>1,HIGH=>65535))
--------------------------------------------
23941
SQL> exec DBMS_RANDOM.initialize (val => 1)
PL/SQL procedure successfully completed.
SQL> select TRUNC(DBMS_RANDOM.value(low => 1, high => 65535)) from dual;
TRUNC(DBMS_RANDOM.VALUE(LOW=>1,HIGH=>65535))
--------------------------------------------
49214
SQL> r
1* select TRUNC(DBMS_RANDOM.value(low => 1, high => 65535)) from dual
TRUNC(DBMS_RANDOM.VALUE(LOW=>1,HIGH=>65535))
--------------------------------------------
56385
SQL> r
1* select TRUNC(DBMS_RANDOM.value(low => 1, high => 65535)) from dual
TRUNC(DBMS_RANDOM.VALUE(LOW=>1,HIGH=>65535))
--------------------------------------------
23941
SQL>
If we look at the code you got from Tim's site we see this line:
l_seed := TO_NUMBER(TO_CHAR(SYSDATE,'YYYYDDMMSS'));
From which we can surmise that your process is inserting 10-11 rows per second :)
If you replace SYSDATE with SYSTIMESTAMP and change the mask to go to millisecs (or smaller) then you should get a different seed each time, and hence a different value each time. Note that you still need to force the re-evaluation of the function to guarantee getting a different result for each row (see the demo below).
Um, did I say "guarantee". Uh-oh. It is in the nature of the random that it can produce the same result two goes running. So perhaps that should be "to minimize teh chances of getting the same result for each row".
Alternatively, remove the initialisation from the function and call it before you start your bulk inserts. Whether this is feasible depends entirely on your business logic.
Demonstration
Here is a function which generates a "random" number:
create or replace function get_random_number
(p_seed in number := 0)
return pls_integer
is
begin
if p_seed = 0
then
DBMS_RANDOM.initialize (val => TO_NUMBER(TO_CHAR(SYSDATE,'YYYYDDMMSS')));
else
DBMS_RANDOM.initialize (val => p_seed);
end if;
return TRUNC(DBMS_RANDOM.value(low => 1, high => 65535));
end;
/
If we call it twenty times with the default parameter it returns the same number each time:
SQL> select rownum
, get_random_number
from dual
connect by level <= 20
/
2 3 4 5
ROWNUM GET_RANDOM_NUMBER
---------- -----------------
1 10239
2 10239
3 10239
4 10239
5 10239
6 10239
7 10239
8 10239
9 10239
10 10239
11 10239
12 10239
13 10239
14 10239
15 10239
16 10239
17 10239
18 10239
19 10239
20 10239
20 rows selected.
SQL>
Whereas if we pass a value it uses a different seed each time and lo! we get a different result:
SQL> select rownum
, get_random_number(rownum)
from dual
connect by level <= 20
/
2 3 4 5
ROWNUM GET_RANDOM_NUMBER(ROWNUM)
---------- -------------------------
1 49214
2 6476
3 42426
4 2370
5 48546
6 52483
7 6964
8 46764
9 27569
10 7673
11 52446
12 50229
13 27861
14 31413
15 11518
16 13471
17 38766
18 9949
19 61656
20 25797
20 rows selected.
SQL>
This works because passing in ROWNUM forces the evaluation of the function for each row. You should not use ROWNUM as the seed in a production system: timestamps are better. Or concatenate the datetime with the rownum to provide a unique seed for each row.
Haven't tried it, but I believe Oracle is calculating the value for your new_uuid() function once, and outputting for each returned row (same as if you did select systimestamp, whatever from whatever... it would output the same timestamp for all rows.
So, you can modify your function to take some input from each row (for the seed perhaps?), or just use sequences.