I am looking to speed up the following PL/SQL function. Right now it has run for over 2 hours with no sign of completing. We aborted that one and attempting it again with a EXIT WHEN of 20 and it still shows no signs of actually completing.
We are running these through SQLDeveloper 17.3, and each of the (4) tables has about 15k rows.
The goal is to grab all of the SSN's in our database and change the first character to an illegal char and the last 2 characters to a random A-Z combination. We then have to update that SSN in every table that uses it (4).
declare
v_random varchar2(2);
v_origin_ssn varchar2(100);
v_working_start varchar2(100);
v_working_middle varchar2(100);
v_new_ssn varchar2(100);
begin
for o in (
select distinct ssn --loop all rows in tbl_customer
from program_one.tbl_customer
)
loop
if regexp_like(o.ssn, '^[A-Za-z9].*[A-Z]$') then continue; --if this is already scrambled, skip
else
select dbms_random.string('U', 2) --create random 2 cap letters
into v_random
from dual;
v_origin_ssn := o.ssn; --set origin ssn with the existing ssn
if regexp_like(o.ssn, '^[A-Za-z]') --if first char is already alpha, leave it alone, otherwise 9
then v_working_start := substr(o.ssn, 1, 1);
else v_working_start := 9;
end if;
v_working_middle := substr(o.ssn, 2, 6); --set middle ssn with the unchanged numbers
v_new_ssn := v_working_start||v_working_middle||v_random; --create new sanitized ssn
update program_one.tbl_customer --update if exists in tbl_customer
set ssn = v_new_ssn
where ssn = v_origin_ssn;
commit;
update program_one.tbl_mhc_backup --update if exists ssn tbl_mhc_backup
set ssn = v_new_ssn
where ssn = v_origin_ssn;
commit;
update program_two.tbl_waiver --update if exists ssn tbl_waiver
set ssn = v_new_ssn
where ssn = v_origin_ssn;
commit;
update program_two.tbl_pers --update if exists in tbl_pers
set ssan = v_new_ssn
where ssan = v_origin_ssn;
commit;
end if;
--dbms_output.put_line(v_origin_ssn||' : '||v_new_ssn); --output test string to verify working correctly
end loop;
end;
I'd do it without a function in plain SQL:
Create a table with old and new ssn:
CREATE TABLE tmp_ssn AS
SELECT ssn, '9'||substr(ssn,2,6)||dbms_random.string('U',2) as new_ssn
FROM (SELECT distinct ssn FROM program_one.tbl_customer);
CREATE UNIQUE INDEX ui_tmp_ssn ON tmp_ssn(ssn, new_ssn);
EXEC DBMS_STATS.GATHER_TABLE_STATS(null,'tmp_ssn');
... and then update the tables one by one:
MERGE INTO program_one.tbl_customer z USING tmp_ssn q ON (z.ssn=q.ssn)
WHEN MATCHED THEN UPDATE z.ssn = q.new_ssn;
COMMIT;
MERGE INTO program_one.tbl_mhc_backup z USING tmp_ssn q ON (z.ssn=q.ssn)
WHEN MATCHED THEN UPDATE z.ssn = q.new_ssn;
COMMIT;
etc
If that is still to slow, I'd do
RENAME tbl_customer to tbl_customer_old;
CREATE TABLE tbl_customer as
SELECT s.new_ssn as ssn, t.col1, t.col2, ... , t.coln
FROM tbl_customer_old t JOIN tmp_ssn s USING(ssn);
DROP TABLE tbl_customer_old;
Related
Morning folks, could really use your help on this one.
I have a query from a table I need to send out as a CSV attachment on an email using oracle apex. that's the goal of all of this - happy to explore different options/sql to get the same result (through oracle apex)
Example query: select user, title, dept from user_db where dept = :DEPARTMENT
My query brings back a huge amount of data (intended)
Based on this, my idea was to make this query into a BLOB and save it to a different table (TBL_EMAIL_CONTENT),which I could then use the APEX_MAIL.ADD_ATTACHMENT function to bring into an email.
I took the query and created a CLOB, then used a separate function to change the CLOB to a BLOB
declare
CSV_CONTENT clob;
l_BLOB BLOB;
crlf varchar2(2) := chr(13) || chr(10);
CSVFILENAME varchar2(255);
begin
CSV_CONTENT := 'User,'||'Title,'||'Dept,'||crlf;
for c5 in (select user, title, dept from user_db where dept = :DEPARTMENT)
LOOP
CSV_CONTENT := CSV_CONTENT||c5.CONTENT_csv;
CSVFILENAME := 'Example.csv';
END LOOP;
L_BLOB := clob_to_blob(CSV_CONTENT);
for c6 in (select * from TBL_EMAILCONTENT where EMAIL_ID = 1)
LOOP
Update TBL_EMAILCONTENT
set
filename = CSVFILENAME||'.CSV',
CONTENT2 = csv_content,
CONTENT = L_BLOB;
END LOOP;
END;
The clob_to_blob function is as follows:
create or replace FUNCTION clob_to_blob(src_clob CLOB) RETURN BLOB IS
tgt_blob BLOB;
amount INTEGER := DBMS_LOB.lobmaxsize;
dest_offset INTEGER := 1;
src_offset INTEGER := 1;
blob_csid INTEGER := nls_charset_id('UTF8');
lang_context INTEGER := DBMS_LOB.default_lang_ctx;
warning INTEGER := 0;
begin
if src_clob is null then
return null;
end if;
DBMS_LOB.CreateTemporary(tgt_blob, true);
DBMS_LOB.ConvertToBlob(tgt_blob, src_clob, amount, dest_offset, src_offset, blob_csid, lang_context, warning);
return tgt_blob;
end clob_to_blob;
I would then send this out using :
APEX_MAIL.SEND(
p_to => 'UKSERVERPATCHING#VODAFONE.COM',
p_from => 'ukserverpatching#vodafone.com',
p_bcc => 'ukserverpatching#vodafone.com',
p_body => to_clob(' '),
p_body_html => l_body,
p_subj => l_subj);
for c8 in(
SELECT CONTENT,FILENAME,MIMETYPE
FROM TBL_EMAILCONTENT
where EMAIL_ID = 1)
loop
APEX_MAIL.ADD_ATTACHMENT(
p_mail_id => l_id,
p_attachment => c8.CONTENT,
p_filename => c8.FILENAME,
p_mime_type => c8.MIMETYPE);
end loop;
Unfortunately, this result makes a CSV that isnt readable with garbled characters in it. Other options ive tried online so far seem to run into problems where the query result is too long to be dealt with via a varchar value.
has anyone came across this before or can point me to an article online that looks to do this sort of thing?
Thanks in advance, really appreciated.
I have a sas dataset called list that contains all files/path/filename of a directory.
sample dataset
I want to create a new column base on the suffix of column the_name to add 1, so 01 will become 02 and 02 will become 03.
For example:
the_name: FOR_PROCESSING_1234562020042002
new_name: FOR_PROCESSING_1234562020042003
the_name: FOR_PROCESSING_1234562020042101
new_name: FOR_PROCESSING_1234562020042102
Thanks for your help.
Recy:
A safer increment would be to scan then entire number off the end of the _name, and not rely on incrementing just a single tail end digit.
data _null_;
the_name = 'FOR_PROCESSING_1234562020042002';
suffix = scan(the_name,-1,'_');
nextnum = input(suffix,best20.)+1;
new_name = cats(transtrn(the_name,trim(suffix),''),nextnum);
put the_name= / new_name= ;
run;
--- LOG ---
the_name=FOR_PROCESSING_1234562020042002
new_name=FOR_PROCESSING_1234562020042003
At the moment I am not working as efficient as I could be. For the problem I have I almost know certain that there is a smarter and better way to fix it.
What I am trying to do:
I got a string like this:
'NL 4633 4809 KTU'
The NL is a country code from an existing table and KTU is an university code from an existing table. I need to put this string in my function and check if the string is validated.
In my function (to validate the string) this is what I am working on. I have managed to split up the string with this:
countryCode := checkISIN; -- checkISIN is the full string ('NL 4633 4809 KTU') and I am giving the NL value to this variable. countryCode is the type varchar2(50)
countryCode := regexp_substr(countryCode, '[^ ]+', 1, 1);
Now that I have the country code as shown below:
NL
Has valid country code
I want to validate/check the country code for it's existence from it's own table. I tried this:
if countryCode in ('NL', 'FR', 'DE', 'GB', 'BE', 'US', 'CA')
then dbms_output.put_line('Has valid country code');
else
dbms_output.put_line('Has invald country code. Change the country code to a valid one');
end if;
This works, but it's not dynamically. If someone adds a country code then I have to change the function again.
So is there a (smart/dynamically) way to check the country codes for their existing tables?
I hope my question is not too vague
Cheers
If you have Country codes table and it looks like this:
ID | NAME
----------
1 | NL
2 | FR
3 | BE
when you parse string, you can make like this :
select count(1)
into v_quan
from CountryCodes cc
where nvl(countryCode,'') = cc.name
if v_quan > 0 then
dbms_output.put_line('Has valid country code');
else
dbms_output.put_line('Has invald country code. Change the country code to a valid one');
end if;
I have a table temp that have a column name "REMARKS"
Create script
Create table temp (id number,remarks varchar2(2000));
Insert script
Insert into temp values (1,'NAME =GAURAV Amount=981 Phone_number =98932324 Active Flag =Y');
Insert into temp values (2,'NAME =ROHAN Amount=984 Phone_number =98932333 Active Flag =N');
Now , i want to fetch the corresponding value of NAME ,Amount ,phone_number, active_flag from the remarks column of the table.
I thought of using regular expression ,but i am not comfortable in using it .
I tried with substr and instr to fetch the name from the remakrs column ,but if i want to fetch all four, i need to write a pl sql .Can we achieve this using Regular expression.
Can i get output(CURSOR) like
id Name Amount phone_number Active flag
------------------------------------------
1 Gaurav 981 98932324 Y
2 Rohan 984 98932333 N
-------------------------------------------
Thanks for your help
you can use something like :
SQL> select regexp_replace(remarks, '.*NAME *=([^ ]*).*', '\1') name,
2 regexp_replace(remarks, '.*Amount *=([^ ]*).*', '\1') amount,
3 regexp_replace(remarks, '.*Phone_number *=([^ ]*).*', '\1') ph_number,
4 regexp_replace(remarks, '.*Active Flag *=([^ ]*).*', '\1') flag
5 from temp;
NAME AMOUNT PH_NUMBER FLAG
-------------------- -------------------- -------------------- --------------------
GAURAV 981 98932324 Y
ROHAN 981 98932324 N
I'm using the following blog post to help me export a report. It works great however i wondered is it possible to set column heading names?
http://spendolini.blogspot.co.uk/2006/04/custom-export-to-csv.html
If all you want to do is to provide an initial row with column headers in it:
begin
-- Set the MIME type
owa_util.mime_header( 'application/octet', FALSE );
-- Set the name of the file
htp.p('Content-Disposition: attachment; filename="emp.csv"');
-- Close the HTTP Header
owa_util.http_header_close;
-- Send the initial row with headers
htp.prn('Ename,Empno,Department'||chr(13));
-- Loop through all rows in EMP
for x in (select e.ename, e.empno, d.dname
from emp e, dept d where e.deptno = d.deptno
and e.deptno like :P1_DEPTNO)
loop
-- Print out a portion of a row,
-- separated by commas and ended by a CR
htp.prn(x.ename ||','|| x.empno ||','||
x.dname || chr(13));
end loop;
-- Send an error code so that the
-- rest of the HTML does not render
--htmldb_application.g_unrecoverable_error := true;
--use stop apex_engine
apex_application.stop_apex_engine;
end;
Basically you just emit one extra row before you emit the data rows.
I commented htmldb_application in favor of apex_application.stop_apex_engine. See documentation