In my case I store and retrieve Cyrillic characters into / from Oracle 11g db using NVARCHAR2 columns (see below a simple case)
create table cyrillic (texte varchar2(1000), ntexte nvarchar2(1000));
insert into cyrillic (texte, ntexte) values ('Hello World',N'Привет мир');
commit;
select * from cyrillic;
My aim now is to use the NVARCHAR2 column to prepare and send an email using UTL_SMTP.
I found several posts related to similar things but none is working (ie: Send mail in oracle with UTF-8 encoding in subject)
I always have a conversion issue in received email.
Is someone had the same need and found a solution ?
After several days of work and plenty of google search, I found the solution that works in my case.
Here the code:
create or replace procedure pr_send_mail_by_rawdata(p_subject nvarchar2, p_body nvarchar2) as
lv_server varchar2(255) := 'localhost';
lv_rcpt varchar2(255) := 'you#gmail.com';
lv_from varchar2(255) := 'me <me#gmail.com>';
lv_subject varchar2(4000):= 'cyrillic';
lv_conn utl_smtp.connection;
begin
lv_conn := utl_smtp.open_connection(lv_server);
UTL_SMTP.helo (lv_conn, lv_server);
UTL_SMTP.mail (lv_conn, lv_from);
UTL_SMTP.rcpt (lv_conn, lv_rcpt);
UTL_SMTP.open_data (lv_conn);
UTL_SMTP.WRITE_DATA(lv_conn, 'From' || ': ' || lv_from || UTL_TCP.CRLF);
UTL_SMTP.WRITE_DATA(lv_conn, 'To' || ': ' || lv_rcpt || UTL_TCP.CRLF);
lv_subject := '=?UTF8?Q?'||replace(utl_encode.TEXT_ENCODE(p_subject,'UTF8'),'='||chr(13) || chr(10)) || '?=';
UTL_SMTP.write_raw_data(lv_conn, utl_raw.cast_to_raw('Subject' || ': ' || lv_subject||utl_tcp.crlf ));
utl_smtp.write_data(lv_conn, 'MIME-version: 1.0' || utl_tcp.CRLF);
utl_smtp.write_data(lv_conn, 'Content-Type: text/html; charset=KOI8-R;'||utl_tcp.CRLF);
utl_smtp.write_data(lv_conn, 'Content-Transfer-Encoding: QUOTED-PRINTABLE'||utl_tcp.CRLF);
utl_smtp.write_data(lv_conn, utl_encode.TEXT_ENCODE(p_body,'CL8KOI8R'));
utl_smtp.write_data(lv_conn, utl_tcp.crlf);
utl_smtp.close_data(lv_conn);
utl_smtp.quit(lv_conn);
end;
Two things have been managed differently:
The subject:
Text is converted in utf8.
The body:
Text is converted in KOI8-R (charset : CL8KOI8R)
Related
I want to create or rename table with current_date.
For example:
1) create table TABLENAME || CURRENT_DATE
2) rename TABLE_NAME TO TABLE_NAME_||CURRENT_DATE.
How can I do it? Could you give an example?
This will append the current date in YYYYMMDD format to the table name. If YYYYMMDD is already appended to the name it's replaced with the new date.
REPLACE PROCEDURE rename_table_yyyymmdd
(
IN db_name VARCHAR(128) CHARACTER SET Unicode,
IN tbl_name VARCHAR(128) CHARACTER SET Unicode, -- defaults to current database
OUT msg VARCHAR(600) CHARACTER SET Unicode
) SQL SECURITY INVOKER
BEGIN
DECLARE old_name VARCHAR(261) CHARACTER SET Unicode;
DECLARE new_name VARCHAR(261) CHARACTER SET Unicode;
DECLARE sql_stmt VARCHAR(600) CHARACTER SET Unicode;
SET old_name = '"' || Coalesce(db_name,DATABASE) || '"."'
|| Coalesce(tbl_name, '') || '"';
SET new_name = '"' || Coalesce(db_name,DATABASE) || '"."'
-- remove an existing "_YYYYMMDD" at the end of the table name
|| Coalesce(RegExp_Replace(tbl_name, '_[0-9]{8}$'),'')
|| '_' || To_Char(Current_Date, 'yyyymmdd') || '"';
SET sql_stmt = 'RENAME TABLE ' || old_name || ' AS ' || new_name || ';';
EXECUTE IMMEDIATE sql_stmt;
SET msg = 'Table ' || old_name || ' renamed to ' || new_name;
END;
CALL rename_table_yyyymmdd('myDB', 'tablename', msg);
CALL rename_table_yyyymmdd(null, 'tablename', msg);
No error handling, simply fails on errors, e.g. when you run it twice a day or the table doesn't exists or the user has no Drop Table right, etc.
Step 1:
create table dat
(
saledate CURRENT_DATE
)
Step 2:
CREATE TABLE database.new_table AS
database.dat WITH DATA;
You can use sysdate or getdate to get current date.
I'm stuck on this block of statements and don't know how to program the output when the code runs to show the amount of the payment as $10.00 and the balance remaining in the same format. My code also prompts me twice for idpledge... I've got some markup in the code for topics I don't quite understand. Using Oracle 11g 2e and the book isn't very helpful. If there is a recommendation for beginner books or resources I am listening (reading). Code is below:
SET SERVEROUTPUT ON;
DECLARE
pay_num NUMBER(2) := 0; /*initialize to 0 */
loop_count NUMBER(2) := 12; /*intialize to max number of months */
pay_amt NUMBER(8,2);
pledge_bal NUMBER(8,2);
v_pledgeamt NUMBER(8,2);
start_date DATE := '04-OCT-2012';
due_date DATE;
v_due_date VARCHAR2(15);
BEGIN
SELECT pledgeamt INTO v_pledgeamt
FROM DD_PLEDGE
WHERE idpledge=&idpledge;
pay_amt := (v_pledgeamt / loop_count);
due_date := start_date;
pledge_bal := (v_pledgeamt - pay_amt);
FOR i IN 1..loop_count LOOP
pay_num := pay_num + 1;
v_due_date := TO_CHAR(due_date, 'MM-DD-YYYY');
due_date := add_months(due_date, 1);
pledge_bal := (pledge_bal - pay_amt);
DBMS_OUTPUT.PUT_LINE (
'Payment Num: ' || pay_num
|| ' ' || 'Due Date: ' || v_due_date
|| ' ' || 'Amount Due: ' || pay_amt
|| ' ' || 'Balance: ' || TO_CHAR(pledge_bal, '$')); /* how to format with $*/
END LOOP;
END;
As mentioned at Docs you can use the to_char with a proper formatter. In your case it would be to_char(pledge_bal, '$9,999.99')
Using the format as '$9,999.99' fulfills the following objectives :
Returns a comma in the specified position. You can specify multiple commas in a number format model.
Returns a decimal point, which is a period (.) in the specified position.
Returns value with a leading dollar sign.
If required you can check for more parameters at the link mentioned.
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.
Hello I exported database using datapump another schema so this synonyms must be in another new schema.
My old one is KTECH and new one is LTECH
DECLARE
strSynonyms_KTECH VARCHAR2(3000) := 'KTECH ';
strSynonyms_LTECH VARCHAR2(3000) := 'LTECH';
strCommand VARCHAR2(33865);
BEGIN
LOOP
FOR Synonym IN (SELECT * FROM ALL_SYNONYMS WHERE OWNER = strSynonyms_KTECH)
strCommand := 'CREATE OR REPLACE SYNONYM ' ||
Synonym.KTECH || '.' || Synonym.SYNONYM_NAME ||
' FOR ' || strSynonyms_LTECH || '.' ||
Synonym.TABLE_NAME;
EXECUTE IMMEDIATE strCommand;
END LOOP;
END;
I tried to run it but it shows me error.
you can export database synonyms from old schema as text file by using pl/sql or TOAD, then save the exported file as script, edit it then execute it on new schema.
by plsql developer:
by Toad:
In a tabular form in Oracle Apex, how would you make it only update the checked off rows, and where would you put this process?
Do you add it to the existing ApplyMRU process as a condition?
I'm just currently stuck where I have a tabular form and I only want it to update the checked off rows and then send an email out to let people know that the status for that record has changed.
I've tried doing it based on time and date from a suggestion, but it's sending out every record verse just the ones I need.
Here's the current email code that's not working right, I don't want to pull it from the table directly, I'd rather it use the fields on the tabular forms and only email out the rows that have been checked off.
DECLARE
l_id NUMBER;
l_index NUMBER;
l_vc_arr2 apex_application_global.vc_arr2;
lc_message VARCHAR2 (4000);
l_pkey NUMBER;
l_date_wrote DATE;
l_sales VARCHAR2 (4000);
l_client VARCHAR2 (4000);
l_job VARCHAR2 (4000);
l_who VARCHAR2 (4000);
l_date_covered DATE;
BEGIN
FOR c1
-- Retrieve reqs primary key that have been covered
-- in the last 2 seconds by the salesman
IN (SELECT pkey
FROM reqs
WHERE TO_CHAR ( (SYSDATE), 'MM/DD/YYYY HH:MI:SS') < (TO_CHAR ( (date_covered + 1 / 10800), 'MM/DD/YYYY HH:MI:SS'))
AND sales = :p14_sales
AND covered IS NOT NULL)
-- Send an email for each req that has been covered
LOOP
SELECT date_wrote,
sales,
client,
job,
Who,
date_covered
INTO l_date_wrote,
l_sales,
l_client,
l_job,
l_who,
l_date_covered
FROM reqs
WHERE pkey = c1.pkey;
lc_message := 'Date Written :' || l_date_wrote || CHR (10);
lc_message := lc_message || 'Sales :' || l_sales || CHR (10);
lc_message := lc_message || 'Client :' || l_client || CHR (10);
lc_message := lc_message || 'Position :' || l_job || CHR (10);
lc_message := lc_message || 'Who Covered :' || l_who || CHR (10);
lc_message := lc_message || 'Date Covered :' || l_date_covered || CHR (10);
l_id := APEX_MAIL.SEND(
p_to => 'some.name#somedomain.com',
p_from => 'DO_NOT_REPLY#REQS',
p_subj => ''
|| l_who
|| ' Has Covered '
|| l_job
|| ' at '
|| l_client
|| CHR (10),
p_body => lc_message);
COMMIT;
apex_mail.push_queue ();
END LOOP;
END;
I've been banging my head against a wall on this one.
Thanks!
You need to loop over the correct arrays with PLSQL. Items in tabular forms map to a couple of arrays in session state.
Apex documentation: Referencing Arrays
Checkboxes are special cases in this, as they will not create an entry in these arrays when they are not checked.
Apex documentation: Referencing Arrays in an on-submit process
For example, I have a tabular form on EMP and there is a "[row selector]" column present.
On webpage:
Then I have this on-submit process:
-- loop over array f01
-- this will be the array holding the values for a checkbox column (in this case, the row selector pseudo column)
FOR i IN 1..apex_application.g_f01.count
LOOP
-- show what is in array f01
-- this will be the index position of the checked row in case of the row selector
apex_debug.message('Value of array f01 at position'||i||': '||apex_application.g_f01(i));
-- array f02 will be the next editable (or session state saving) column, in my case a display-but-session-state-saving column with the EMPNO
apex_debug.message('Value of array f02 at position'||apex_application.g_f01(i)||': '||apex_application.g_f02(apex_application.g_f01(i)));
END LOOP;
The output in the debug:
Value of array f01 at position1: 2
Value of array f02 at position2: 7698
Value of array f01 at position2: 4
Value of array f02 at position4: 7566
So things to take note of:
as in any tabular form, be aware of how the columns map to an array.
position matters in this case, so switching columns around will alter
the array they will map to
checkbox columns do NOT create a row in their respective array when they are UNCHECKED
this matters when looping over arrays and a checkbox column has to be checked
These are techniques very commonly used when working with manual tabular forms, which use the APEX_ITEM api.
So to make your email system work: loop over the checkbox columns, retrieve a PK from another array, and send a mail much like you do in your current loop.
Eg:
I took your code sample and adapted it. Note that I changed a few things:
removed variables for each field and instead used a rowtype variable for ease of use
removed the commit
push the queue after the loop
DECLARE
l_checked_row NUMBER;
l_id NUMBER;
lc_message VARCHAR2 (4000);
l_pkey NUMBER;
l_r_reqs reqs%ROWTYPE;
BEGIN
FOR i IN 1..apex_application.g_f01.count
LOOP
l_checked_row := apex_application.g_f01(i);
-- assuming that array F02 maps to column PKEY from table REQS
l_pkey := apex_application.g_f02(l_checked_row);
-- get details required for creating the mail body
-- It's generally easier to just fetch the row instead of having to
-- define variables to cover every field you need.
SELECT *
INTO l_r_reqs
FROM reqs
WHERE pkey = l_pkey;
-- Dont forget that select into may generate no_data_found or too_many_rows !
lc_message := 'Date Written :' || l_r_reqs.date_wrote || CHR (10);
lc_message := lc_message || 'Sales :' || l_r_reqs.sales || CHR (10);
lc_message := lc_message || 'Client :' || l_r_reqs.client || CHR (10);
lc_message := lc_message || 'Position :' || l_r_reqs.job || CHR (10);
lc_message := lc_message || 'Who Covered :' || l_r_reqs.who || CHR (10);
lc_message := lc_message || 'Date Covered :' || l_r_reqs.date_covered || CHR (10);
l_id := APEX_MAIL.SEND(
p_to => 'TESTER#TEST.com',
p_from => 'DO_NOT_REPLY#REQS',
p_subj => ''
|| l_r_reqs.who
|| ' Has Covered '
|| l_r_reqs.job
|| ' at '
|| l_r_reqs.client
|| CHR (10),
p_body => lc_message);
-- avoid commits unless ab-so-lu-te-ly necessary. Apex implicit commits can make the flow hard enough to
-- understand as it is.
END LOOP;
apex_mail.push_queue ();
END;