PL/SQL procedure output stored in file and then email in Oracle 11g - plsql

I have a procedure where I am checking whether there are new codes. If there are then insert them into a table. And also save the new data into a csv or txt file and email them to me.
I can't get the logic for how to redirect the new data to file or just even put the data as simple text in the email. Thanks
create or replace
PROCEDURE new_codes_test( today_date IN VARCHAR2 DEFAULT NULL, v_proc_return OUT NUMBER) AS
sql_str VARCHAR2(4000);
.....
BEGIN
v_start_time := SYSDATE;
v_proc_return := 0;
....
INSERT INTO NEW_CODES_test
SELECT DISTINCT Sy_ID ,P_CODE, SYSDATE
FROM X.B
WHERE NOT EXISTS (SELECT DISTINCT Sy_ID, P_CODE
FROM X.C
WHERE today = today_date) ;
COMMIT;
--SELECT ___ into ___ from X.B;
sql_str := 'UTL_MAIL.send(sender => ''
,recipients => ''
,cc => ''
,subject => 'New codes'
,MESSAGE => '' )';
--EXECUTE IMMEDIATE sql_str;
p_proc_return := v_proc_return;
EXCEPTIONS
....
END;

To write to a file the UTL_FILE package will come in handy. To write an email you'll need to put the text to be sent into some sort of string before passing it to the MESSAGE argument of UTL_MAIL.SEND. You'll also need to be sure UTL_MAIL is installed and set up on your server; see this FAQ.
So something like the following may be useful:
CREATE OR REPLACE FUNCTION NEW_CODES_TEST(today_date IN VARCHAR2)
RETURN NUMBER
AS
strMessage VARCHAR2(32767);
nRows NUMBER := 0;
fHandle UTL_FILE.FILE_TYPE;
BEGIN
v_start_time := SYSDATE;
fHandle := UTL_FILE.FOPEN(someDirectory, someFilename, 'w');
FOR aRow IN (SELECT DISTINCT SY_ID ,P_CODE
FROM X.B
WHERE NOT EXISTS (SELECT DISTINCT Sy_ID, P_CODE
FROM X.C
WHERE today = today_date)
LOOP
INSERT INTO NEW_CODES_test
VALUES (aRow.SY_ID, aRow.P_CODE, SYSDATE);
UTL_FILE.PUT_LINE(fHandle, aRow.SY_ID || ', ' || aRow.P_CODE);
strMessage := strMessage || 'Added ' || aRow.SY_ID || ', ' ||
aRow.P_CODE || CHR(10);
nRows := nRows + 1;
END LOOP;
COMMIT;
UTL_FILE.FCLOSE(fHandle);
UTL_MAIL.SEND(sender => 'me#mycompany.com',
recipients => 'you#someplaceelse.net',
subject => 'New codes',
message => strMessage);
RETURN 0;
EXCEPTIONS
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
RETURN SQLCODE;
END NEW_CODES_TEST;
Share and enjoy.

Related

Stored Procedure not reading from variable in IF-ELSE statement

I started writing this stored procedure and I faced some issues when I try to pass a variable in my conditional statement.
I can use the parameter BANKN which works fine, but when never I passed the declared variable AC_t somehow the PL/SQL ignores it.
Any idea please what I'm missing here?
create or replace PROCEDURE TTEST1 (
CR IN VARCHAR2,
BANKN IN VARCHAR2,
P_CURSOR OUT SYS_REFCURSOR
)
AS
G_AC CHAR(10);
AC_t Billing.Account %Type;
BEGIN
IF BANKN = 'WLNV' AND AC_t = 'Private'
THEN
IF CR IN (
'EUR',
'CZK',
'USD'
)
THEN
OPEN P_CURSOR
FOR
SELECT G_AC AS GL_ACC,
Billing.Account AS ACC_Type
INTO
G_AC,
AC_t
FROM Billing
INNER JOIN invoice ON Billing.ACC_NO = invoice.ACC_NO;
END IF ;
END IF ;
END;
My aim here is to expand this code by using AC_t value from Billing.ACCount and retrieve what ever data that can be 'Private' or 'Public'.
To do this, I need to use case or IF statement, however when I use
Billing.ACCount, I got an error "not allowed in this context", for this reason I use synonym AC_t but this don't read values from Billing table unless I use it in WHERE clause.
ACC_NO
Account
1
Private
2
Public
Extended code:
IF BANKN = 'WLNV' AND AC_t = 'Private'
THEN
...
...
ELSIF IF BANKN = 'WLNV' AND AC_t = 'Public'
THEN
....
...
You cannot use SELECT ... INTO with a cursor and you need to declare a value for the ac_t variable (but since it is a column in the table you may want a WHERE clause in the cursor). Like this:
CREATE PROCEDURE TTEST1 (
p_CR IN VARCHAR2,
p_BANKN IN VARCHAR2,
P_CURSOR OUT SYS_REFCURSOR
)
AS
BEGIN
IF p_BANKN = 'WLNV'
AND p_CR IN ( 'EUR', 'CZK', 'USD' )
THEN
OPEN P_CURSOR FOR
SELECT G_AC AS GL_ACC,
b.account AS ACC_Type
FROM Billing b
INNER JOIN invoice i
ON b.ACC_NO = i.ACC_NO
WHERE b.account = 'Private';
END IF;
END;
/
Which, if you have the sample data:
CREATE TABLE invoice (acc_no, g_AC) AS
SELECT 1, 2 FROM DUAL;
CREATE TABLE billing (acc_no, account) AS
SELECT 1, 'Private' FROM DUAL;
Then you can call the procedure and print the contents of the cursor using:
DECLARE
cur SYS_REFCURSOR;
v_g_ac INVOICE.G_AC%TYPE;
v_ac_t BILLING.ACCOUNT%TYPE;
BEGIN
ttest1('EUR', 'WLNV', cur);
LOOP
FETCH cur INTO v_g_ac, v_ac_t;
EXIT WHEN cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE( v_g_ac || ', ' || v_ac_t );
END LOOP;
CLOSE cur;
END;
/
Which outputs:
2, Private
db<>fiddle here

BLOB to CLOB Conversion : Cyrillic characters are not getting converted properly

BLOB to CLOB Conversion : Cyrillic characters are not getting converted properly
Based on our product migration, I have an requirement to replace below mentioned single byte character to Double byte character in each and every record but the respective column is BLOB datatype encoded with AL32UTF8 so I'm using the below procedure to convert column from BLOB to CLOB and replace the single byte character to Double byte character and convert back to CLOB to BLOB.
NLS_CHAR--> AL32UTF8
For ex:
when I tried with CL8ISO8859P5 character set in CLOB conversion function, Bulgarian data is corrupted and If I tried with AL32UTF8 character set then the respective Cyrillic character got corrupted.
select convert_to_clob(XMLRECORD) From F_TEC_OUTPUT_T
CLOB conversion with AL32UTF8 characterset:
ONE�АНУЛИРАНЕ НА ОПРОСТЕНА ФАКТУРА_�THREE
CLOB conversion with CL8ISO8859P5 characterset:
ONEўаааЃааа ааа аа ааа ааЁаЂааа аЄаааЂаЃа а_ўTHREE
select recid, dbms_lob.substr(XMLRECORD) as raw_xrec
from F_TEC_OUTPUT_T
[enter image description here][1]
Please suggest the proper way to convert this.
Single byte ---> Double byte
ў --> яЃО
§ --> яЃН
ќ --> яЃМ
In RAW:
FE --> EFA3BE
FD --> EFA3BD
FC --> EFA3BC
FB --> EFA3BB
l_clob CLOB;
l_dest_offset NUMBER := 1;
l_src_offset NUMBER := 1;
l_lang_context NUMBER := dbms_lob.default_lang_ctx;
l_warning NUMBER;
BEGIN
dbms_lob.createtemporary(l_clob, TRUE);
dbms_lob.converttoclob(dest_lob => l_clob,
src_blob => l_blob,
amount => dbms_lob.lobmaxsize,
dest_offset => l_dest_offset,
src_offset => l_src_offset,
blob_csid => nls_charset_id('CL8ISO8859P5'),
lang_context => l_lang_context,
warning => l_warning);
RETURN l_clob;
END convert_to_clob;
CREATE OR REPLACE FUNCTION convert_to_blob(l_clob CLOB) RETURN BLOB IS
l_blob BLOB;
l_dest_offset NUMBER := 1;
l_src_offset NUMBER := 1;
l_lang_context NUMBER := dbms_lob.default_lang_ctx;
l_warning NUMBER;
BEGIN
dbms_lob.createtemporary(l_blob, TRUE);
dbms_lob.converttoblob(dest_lob => l_blob,
src_clob => l_clob,
amount => dbms_lob.lobmaxsize,
dest_offset => l_dest_offset,
src_offset => l_src_offset,
blob_csid => nls_charset_id('AL32UTF8'),
lang_context => l_lang_context,
warning => l_warning);
RETURN l_blob;
END convert_to_blob;
CREATE OR REPLACE PROCEDURE convert_blob_file(p_tname in varchar2,parallel_no_cnt in number default 1,record_cmt_cnt in number default 1) IS
TYPE cur_typ IS REF CURSOR;
getcursordata cur_typ;
ConversionRecID VARCHAR2(2000);
BinaryValueSource BLOB;
BinaryValueDest BLOB;
BinaryValueTemp CLOB;
cnt NUMBER := 1;
Dicttablename VARCHAR2(2000);
Marker_R_count NUMBER := 0;
XmlrecordTemp CLOB;
Selectstring VARCHAR2(2000);
select_table_query VARCHAR2(1000):= 'SELECT /*+ PARALLEL('''||parallel_no_cnt||''') */ RECID,XMLRECORD FROM ' || p_tname || ' WHERE XMLRECORD IS NOT NULL';
BEGIN
OPEN getcursordata FOR select_table_query;
LOOP
FETCH getcursordata INTO ConversionRecID, BinaryValueSource;
EXIT WHEN getcursordata%NOTFOUND;
BinaryValueTemp := convert_to_clob(BinaryValueSource);
BinaryValueTemp := REPLACE(BinaryValueTemp,CHR(53662),CHR(15705022)); -- #FM Separator
BinaryValueTemp := REPLACE(BinaryValueTemp,CHR(49831),CHR(15705021)); -- #VM Separator
BinaryValueTemp := REPLACE(BinaryValueTemp,CHR(53660),CHR(15705020)); -- #SM Separator
BinaryValueTemp := REPLACE(BinaryValueTemp,CHR(53659),CHR(15705019)); -- #TM Separator
BinaryValueDest := convert_to_blob(BinaryValueTemp);
EXECUTE IMMEDIATE 'UPDATE /*+ PARALLEL('''||parallel_no_cnt||''') */'||p_tname||' SET XMLRECORD = :v1 WHERE RECID= :v2' USING BinaryValueDest,ConversionRecID;
IF cnt=record_cmt_cnt THEN
cnt:=1;
COMMIT;
ELSE
cnt := cnt + 1;
END IF;
END LOOP;
CLOSE getcursordata;
DBMS_OUTPUT.PUT_LINE('Time------'||to_char(sysdate, 'HH24:MI:SS'));
END;```
[1]: https://i.stack.imgur.com/t9w19.jpg

Loop cursor of ALL_MVIEWS generate PL/SQL Error ORA-06502

i wrote a procedure that deals with writing the contents of the QUERY column of ALL_MVIEWS to a file:
DECLARE
v_out_dir_name VARCHAR2(30) := 'DIR_TEST';
v_out_dir_path VARCHAR2(60);
v_count_object_elab NUMBER := 0;
CURSOR c_mviews IS
SELECT
LOWER(MVIEW_NAME) || '.sql' AS FILE_NAME
, QUERY AS SCRIPT
FROM ALL_MVIEWS
;
v_file UTL_FILE.file_type;
BEGIN
FOR r_mview IN c_mviews LOOP
v_file := UTL_FILE.fopen (v_out_dir_name, r_mview.FILE_NAME, 'w');
UTL_FILE.putf (v_file, r_mview.SCRIPT);
UTL_FILE.fclose (v_file);
v_count_object_elab := v_count_object_elab + 1;
END LOOP;
IF v_count_object_elab = 0
THEN
DBMS_OUTPUT.PUT_LINE('NESSUN FILE ELABORATO');
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ERRORE = ' || SQLERRM);
IF UTL_FILE.IS_OPEN (v_file) THEN
UTL_FILE.FCLOSE (v_file);
END IF;
RAISE;
END;
/
But the "FOR r_mview IN c_mviews LOOP" statement generates the following error:
Report error -
ORA-06502: PL/SQL: errore di numero o valore
ORA-06512: a line 35
ORA-06512: a line 16
ORA-06512: a line 16
06502. 00000 - "PL/SQL: numeric or value error%s"
*Cause: An arithmetic, numeric, string, conversion, or constraint error
occurred. For example, this error occurs if an attempt is made to
assign the value NULL to a variable declared NOT NULL, or if an
attempt is made to assign an integer larger than 99 to a variable
declared NUMBER(2).
*Action: Change the data, how it is manipulated, or how it is declared so
that values do not violate constraints.
The error is thrown on a materialized view that has QUERY_LEN = 39000.
How can I solve the problem?
Many thanks in advance.
I don't think you are using UTL_FILE correctly. The putf() procedure is for writing and formatting (using printf() style). Try using just put() instead. I did run your script on my database and it executed fine with putf() and put(), but put() is more appropriate.
Also, beware that QUERY is a LONG. If you have a query over 32k I think you will get the error you are seeing. There are some great write-ups online about how awful LONGs are to work with.
I think easiest thing to do is convert the LONG to CLOB with a CREATE TABLE, read that table and then drop it. Here is a routine to do just that. It will create a copy of all_mviews and convert the query to a CLOB and write that in chunks to a file per view.
DECLARE
v_out_dir_name VARCHAR2(30) := 'MVIEW';
v_count_object_elab NUMBER := 0;
v_cursor SYS_REFCURSOR;
v_file utl_file.file_type;
v_start INTEGER;
v_buffer VARCHAR2(1024);
v_file_name VARCHAR2(1024);
v_query CLOB;
table_or_view_does_not_exist EXCEPTION;
PRAGMA EXCEPTION_INIT(table_or_view_does_not_exist,
-00942);
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE all_mviews_clob';
EXCEPTION
WHEN table_or_view_does_not_exist THEN
NULL;
END;
EXECUTE IMMEDIATE 'CREATE TABLE all_mviews_clob AS SELECT mview_name, to_lob(query) AS query FROM all_mviews';
OPEN v_cursor FOR q'[SELECT lower(mview_name) || '.sql' AS file_name,
query AS script
FROM all_mviews_clob]';
LOOP
FETCH v_cursor
INTO v_file_name,
v_query;
EXIT WHEN v_cursor%NOTFOUND;
v_file := utl_file.fopen(location => v_out_dir_name,
filename => v_file_name,
open_mode => 'w');
v_start := 1;
FOR i IN 1 .. ceil(dbms_lob.getlength(lob_loc => v_query) / 1024)
LOOP
v_buffer := dbms_lob.substr(lob_loc => v_query,
amount => 1024,
offset => v_start);
IF v_buffer IS NOT NULL THEN
utl_file.put(file => v_file,
buffer => v_buffer);
utl_file.fflush(file => v_file);
END IF;
v_start := v_start + 1024;
END LOOP;
utl_file.fclose(v_file);
v_count_object_elab := v_count_object_elab + 1;
END LOOP;
IF v_count_object_elab = 0 THEN
dbms_output.put_line('no mviews');
END IF;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('eror = ' || SQLERRM);
IF utl_file.is_open(v_file) THEN
utl_file.fclose(v_file);
END IF;
RAISE;
END;
/

PLS-00653 Error on an empty line (Aggregate/table functions are not allowed in PL/SQL scope)

I'm trying to run a PL/SQL Script I made but I'm getting an error:
PLS-00653: aggregate/table functions are not allowed in PL/SQL scope
The problem here is not the error in itself but the line where it's being thrown from.
Here's my PL/SQL block which throws this error:
DECLARE
TYPE l_code_arr_typ IS VARRAY(3) OF VARCHAR2(100);
l_code_arr l_code_arr_typ;
l_objet objet_interface_pkg.objet_interface_typ;
l_resultat CLOB;
l_valeur valeur_pkg.valeur_typ;
l_liens lien_objet_interface_pkg.lien_objet_interface_tab;
l_rows NUMBER;
BEGIN
l_code_arr := l_code_arr_typ('champ.fonctions.dossier.particulier',
'champ.fonctions.region.admin',
'champ.fonctions.ministere.organisme');
l_resultat := '{';
FOR l_idx IN 1 .. l_code_arr.count LOOP
l_objet := objet_interface_pkg.obtenir_fnc(p_code => l_code_arr(l_idx),
p_acron_sys => :acronyme);
l_resultat := l_resultat || '"' || l_objet.code || '":[';
l_liens := lien_objet_interface_pkg.obtenir_par_objet_fnc(p_id_objet => l_objet.id_obj);
FOR l_lien IN (SELECT * FROM TABLE(l_liens)) LOOP
l_valeur := valeur_pkg.obtenir_fnc(p_id => l_lien.id_valeur);
l_resultat := l_resultat || '"' || l_valeur.valeur || '"';
FOR l_enfant IN (SELECT *
FROM TABLE(valeur_pkg.obtenir_enfants_fnc(p_id_parent => l_lien.id_valeur,
p_code => l_valeur.valeur))) LOOP
l_resultat := l_resultat || ',"' || l_enfant.valeur || '"';
END LOOP;
END LOOP;
l_resultat := l_resultat || '],';
END LOOP;
<<<<<<<<<< ERROR THROWN HERE (EMPTY LINE)
l_resultat := substr(l_resultat, 1, length(l_resultat) - 1) || '}';
dbms_output.put_line(l_resultat);
END;
The error throws on 34:17 (line:column) which is the fourth line from the end of the code block. As you can see the real problem is that this line is an empty line. Moreover, none of the lines near that empty line contains a call to an aggregate function. So where's the aggregate/table function's call located?
I'm wondering if the problem really comes from my code or if my PL/SQL Developer is broken.
I hope someone can help me...
I found the solution!
The problem is that the function lien_objet_interface_pkg.obtenir_par_objet is PIPELINED and a pipelined return cannot be stored in a variable.
So these 2 lines...
l_liens := lien_objet_interface_pkg.obtenir_par_objet_fnc(p_id_objet => l_objet.id_obj);
FOR l_lien IN (SELECT * FROM TABLE(l_liens)) LOOP
...need to be merge into one single line like so:
FOR l_lien IN (SELECT * FROM TABLE(lien_objet_interface_pkg.obtenir_par_objet_fnc(p_id_objet => l_objet.id_obj))) LOOP
However, I still don't know why PL\SQL Developer said my error comes from line 34 and It'll probably remain a mystery. If anyone have answer to this mystery, please feel free to let me know.

How to use an array in PL/SQL?

How do I make arrays in PL / SQL?
I have a string that I want to split on spaces and then loop through them all.
Declare your array like this:
"your array" apex_application_global.vc_arr2;
"your array" := APEX_UTIL.STRING_TO_TABLE("your string",' ');
FOR i IN 1.. "your array".COUNT LOOP
"Your string" := "Your string"|| "your array"(i);
END LOOP;
and there you have it
And this is the ultimate and universal solution without using any packages, just oracle SQL. See a full featured solution (including a pipelining function) over here: http://www.armbruster-it.org/index.php/12-it/pl-sql/20-string-tokenizer-with-oracle-pl-sql
declare
cursor c_tokenizer(ci_string in varchar2, ci_delimiter in varchar2) is
SELECT regexp_substr(str, '[^' || ci_delimiter || ']+', 1, LEVEL) AS splitted_element,
LEVEL AS element_no
FROM (SELECT rownum AS id, ci_string str FROM dual)
CONNECT BY instr(str, ci_delimiter, 1, LEVEL - 1) > 0
AND id = PRIOR id
AND PRIOR dbms_random.value IS NOT null;
l_string varchar2(100) := 'Hello World, I like PL/SQL';
l_delimiter varchar2(1) := ' ';
begin
-- extract each word of the string above (delimited by blank)
for c1 in c_tokenizer(l_string, l_delimiter) loop
dbms_output.put_line(c1.splitted_element);
end loop;
end;
The Result is:
Hello
World,
I
like
PL/SQL

Resources