Initially, I added the Quick Report SQL as an IAL object and scheduled it to run daily. Then, I created a custom event that listens for changes on the IAL_OBJECT_TAB entity. I filtered it to react only when the specific IAL object is updated or replicated and status is 'OK'. The event then triggers an online SQL block that sends out a CSV file as an email attachment to the recipients.
DECLARE
l_alias VARCHAR2(50) := 'Sender name';
l_subject VARCHAR2(50) := 'Subject name';
l_receivers VARCHAR2(200) := 'receiver@example.com';
l_msg CLOB := TO_CLOB('Message...');
l_bom_str VARCHAR2(3) := CHR(239) || CHR(187) || CHR(191);
l_msg_utf CLOB;
l_csv CLOB;
l_csv_utf CLOB;
-- Proper attachment structure
l_attachments COMMAND_SYS.attachment_arr;
l_rec COMMAND_SYS.attachment_rec;
-- Cursor and line variables
TYPE rc IS REF CURSOR;
c_csv rc;
v_line VARCHAR2(32767);
v_rn NUMBER;
BEGIN
-- Build CSV
DBMS_LOB.CREATETEMPORARY(l_csv, TRUE, DBMS_LOB.SESSION);
OPEN c_csv FOR
WITH results AS
(SELECT * FROM ifsinfo.ial_test_object),
hdr AS
(SELECT LISTAGG('"' || REPLACE(column_name, '"', '""') || '"', ';') WITHIN GROUP(ORDER BY column_id) AS line,
0 AS rn
FROM all_tab_columns
WHERE owner = 'IFSINFO'
AND table_name = 'IAL_TEST_OBJECT'),
data_lines AS
(SELECT '"' || REPLACE(NVL(CLM_1, ''), '"', '""') || '"' || ';' || '"' ||
REPLACE(NVL(CLM_2, ''), '"', '""') || '"' || ';' || '"' ||
REPLACE(NVL(CLM_3, ''), '"', '""') || '"' || ';' || '"' ||
REPLACE(NVL(CLM_4, ''), '"', '""') || '"' || ';' || '"' ||
REPLACE(TO_CHAR(CLM_5), '"', '""') || '"' AS line,
ROW_NUMBER() OVER(ORDER BY NULL) AS rn
FROM results),
all_lines AS
(SELECT line, rn FROM hdr UNION ALL SELECT line, rn FROM data_lines)
SELECT line, rn FROM all_lines ORDER BY rn;
LOOP
FETCH c_csv
INTO v_line, v_rn;
EXIT WHEN c_csv%NOTFOUND;
DBMS_LOB.APPEND(l_csv, v_line || CHR(13) || CHR(10));
END LOOP;
CLOSE c_csv;
-- Prepend UTF-8 BOM
l_csv_utf := TO_CLOB(l_bom_str) || l_csv;
-- Prepend UTF-8 BOM
l_msg_utf := TO_CLOB(l_bom_str) || l_msg;
-- Prepare attachment record
l_rec.clob_ := l_csv_utf;
l_attachments('Excel-Export-' || TO_CHAR(SYSDATE, 'DD-MM-YYYY') || '.csv') := l_rec;
COMMAND_SYS.Mail(sender_ => 'IFSAPP',
from_ => l_alias,
to_list_ => l_receivers,
cc_list_ => NULL,
bcc_list_ => NULL,
subject_ => l_subject,
text_ => l_msg_utf,
attachments_ => l_attachments,
mail_sender_ => NULL);
END;
However, this approach had a limitation: Excel didn't handle UTF-8 encoding properly when opening the CSV attachment, which was a blocker in my case.
To work around this, I mimicked the logic and dependencies of the original code and built my own version that can generate a proper Excel .xlsx file from any SQL query. This Excel file is then sent as an email attachment. Here's the improved version that builds and sends a real Excel attachment:
DECLARE
l_sender_name VARCHAR2(50) := 'Sender name';
l_subject VARCHAR2(50) := 'Subject name';
l_receiver_list VARCHAR2(100) := 'test@example.com';
l_message VARCHAR2(400) := 'Message...';
l_sql_to_execute VARCHAR2(400) := 'SELECT * FROM ifsinfo.IAL_TEST_OBJECT';
l_sheet_name VARCHAR2(50) := 'Sheet 1';
l_attachment_name VARCHAR2(50) := 'Excel-Export.xlsx';
workbook_ Dat_Excel_Builder_Util_API.tp_book;
l_excel_file BLOB;
l_attachments COMMAND_SYS.attachment_arr;
l_rec COMMAND_SYS.attachment_rec;
PROCEDURE Set_Col_Width(p_sheet_ PLS_INTEGER,
p_col_ PLS_INTEGER,
p_format_ VARCHAR2) IS
t_width_ NUMBER;
t_nr_chr_ PLS_INTEGER;
BEGIN
IF p_format_ IS NULL THEN
RETURN;
END IF;
IF INSTR(p_format_, ';') > 0 THEN
t_nr_chr_ := LENGTH(TRANSLATE(SUBSTR(p_format_,
1,
INSTR(p_format_, ';') - 1),
'a\"',
'a'));
ELSE
t_nr_chr_ := LENGTH(TRANSLATE(p_format_, 'a\"', 'a'));
END IF;
t_width_ := TRUNC((t_nr_chr_ * 7 + 5) / 7 * 256) / 256;
IF workbook_.sheets(p_sheet_).widths.exists(p_col_) THEN
workbook_.sheets(p_sheet_).widths(p_col_) := GREATEST(workbook_.sheets(p_sheet_).widths(p_col_),
t_width_);
ELSE
workbook_.sheets(p_sheet_).widths(p_col_) := GREATEST(t_width_, 8.43);
END IF;
END Set_Col_Width;
FUNCTION Get_X_FId(p_sheet_ PLS_INTEGER,
p_col_ PLS_INTEGER,
p_row_ PLS_INTEGER,
p_num_fmt_id_ PLS_INTEGER := NULL,
p_font_id_ PLS_INTEGER := NULL,
p_fill_id_ PLS_INTEGER := NULL,
p_border_id_ PLS_INTEGER := NULL,
p_alignment_ Dat_Excel_Builder_Util_API.tp_alignment := NULL)
RETURN VARCHAR2 IS
t_cnt_ PLS_INTEGER;
t_xf_id_ PLS_INTEGER;
t_xf_ Dat_Excel_Builder_Util_API.tp_XF_fmt;
t_col_xf_ Dat_Excel_Builder_Util_API.tp_XF_fmt;
t_row_xf_ Dat_Excel_Builder_Util_API.tp_XF_fmt;
BEGIN
IF workbook_.sheets(p_sheet_).col_fmts.exists(p_col_) THEN
t_col_xf_ := workbook_.sheets(p_sheet_).col_fmts(p_col_);
END IF;
IF workbook_.sheets(p_sheet_).row_fmts.exists(p_row_) THEN
t_row_xf_ := workbook_.sheets(p_sheet_).row_fmts(p_row_);
END IF;
t_xf_.numFmtId := COALESCE(p_num_fmt_id_,
t_col_xf_.numFmtId,
t_row_xf_.numFmtId,
0);
t_xf_.fontId := COALESCE(p_font_id_,
t_col_xf_.fontId,
t_row_xf_.fontId,
0);
t_xf_.fillId := COALESCE(p_fill_id_,
t_col_xf_.fillId,
t_row_xf_.fillId,
0);
t_xf_.borderId := COALESCE(p_border_id_,
t_col_xf_.borderId,
t_row_xf_.borderId,
0);
t_xf_.alignment := COALESCE(p_alignment_,
t_col_xf_.alignment,
t_row_xf_.alignment);
IF (t_xf_.numFmtId + t_xf_.fontId + t_xf_.fillId + t_xf_.borderId = 0 AND
t_xf_.alignment.vertical IS NULL AND
t_xf_.alignment.horizontal IS NULL AND
NOT NVL(t_xf_.alignment.wrapText, FALSE)) THEN
RETURN '';
END IF;
IF t_xf_.numFmtId > 0 THEN
Set_Col_Width(p_sheet_,
p_col_,
workbook_.numFmts(workbook_.numFmtIndexes(t_xf_.numFmtId)).formatCode);
END IF;
t_cnt_ := workbook_.cellXfs.count();
FOR i in 1 .. t_cnt_ LOOP
IF (workbook_.cellXfs(i)
.numFmtId = t_xf_.numFmtId AND workbook_.cellXfs(i)
.fontId = t_xf_.fontId AND workbook_.cellXfs(i)
.fillId = t_xf_.fillId AND workbook_.cellXfs(i)
.borderId = t_xf_.borderId AND
NVL(workbook_.cellXfs(i).alignment.vertical, 'x') =
nvl(t_xf_.alignment.vertical, 'x') AND
NVL(workbook_.cellXfs(i).alignment.horizontal, 'x') =
nvl(t_xf_.alignment.horizontal, 'x') AND
NVL(workbook_.cellXfs(i).alignment.wrapText, FALSE) =
nvl(t_xf_.alignment.wrapText, FALSE)) THEN
t_xf_id_ := i;
EXIT;
END IF;
END LOOP;
IF t_xf_id_ IS NULL THEN
t_cnt_ := t_cnt_ + 1;
t_xf_id_ := t_cnt_;
workbook_.cellXfs(t_cnt_) := t_xf_;
END IF;
RETURN 's="' || t_xf_id_ || '"';
END Get_X_FId;
FUNCTION Add_String(p_string_ VARCHAR2) RETURN PLS_INTEGER IS
t_cnt_ PLS_INTEGER;
BEGIN
IF workbook_.strings.exists(p_string_) THEN
t_cnt_ := workbook_.strings(p_string_);
ELSE
t_cnt_ := workbook_.strings.count();
workbook_.str_ind(t_cnt_) := p_string_;
workbook_.strings(NVL(p_string_, '')) := t_cnt_;
END IF;
workbook_.str_cnt := workbook_.str_cnt + 1;
RETURN t_cnt_;
END Add_String;
FUNCTION Get_Font(p_name_ VARCHAR2,
p_family_ PLS_INTEGER := 2,
p_fontsize_ NUMBER := 11,
p_theme_ PLS_INTEGER := 1,
p_underline_ BOOLEAN := FALSE,
p_italic_ BOOLEAN := FALSE,
p_bold_ BOOLEAN := FALSE,
p_rgb_ VARCHAR2 := NULL) RETURN PLS_INTEGER IS
t_ind_ PLS_INTEGER;
BEGIN
IF workbook_.fonts.count() > 0 THEN
FOR f IN 0 .. workbook_.fonts.count() - 1 LOOP
IF (workbook_.fonts(f)
.name = p_name_ AND workbook_.fonts(f).family = p_family_ AND workbook_.fonts(f)
.fontsize = p_fontsize_ AND workbook_.fonts(f).theme = p_theme_ AND workbook_.fonts(f)
.underline = p_underline_ AND workbook_.fonts(f)
.italic = p_italic_ AND workbook_.fonts(f).bold = p_bold_ AND
(workbook_.fonts(f).rgb = p_rgb_ OR (workbook_.fonts(f).rgb IS NULL AND
p_rgb_ IS NULL))) THEN
RETURN f;
END IF;
END LOOP;
END IF;
t_ind_ := workbook_.fonts.count();
workbook_.fonts(t_ind_).name := p_name_;
workbook_.fonts(t_ind_).family := p_family_;
workbook_.fonts(t_ind_).fontsize := p_fontsize_;
workbook_.fonts(t_ind_).theme := p_theme_;
workbook_.fonts(t_ind_).underline := p_underline_;
workbook_.fonts(t_ind_).italic := p_italic_;
workbook_.fonts(t_ind_).bold := p_bold_;
workbook_.fonts(t_ind_).rgb := p_rgb_;
RETURN t_ind_;
END Get_Font;
FUNCTION Get_NumFmt(p_format_ VARCHAR2 := NULL) RETURN PLS_INTEGER IS
t_cnt_ PLS_INTEGER;
t_num_fmt_id_ PLS_INTEGER;
BEGIN
IF p_format_ IS NULL THEN
RETURN 0;
END IF;
t_cnt_ := workbook_.numFmts.count();
FOR i IN 1 .. t_cnt_ LOOP
IF workbook_.numFmts(i).formatCode = p_format_ THEN
t_num_fmt_id_ := workbook_.numFmts(i).numFmtId;
EXIT;
END IF;
END LOOP;
IF t_num_fmt_id_ IS NULL THEN
t_num_fmt_id_ := CASE
WHEN t_cnt_ = 0 THEN
164
ELSE
workbook_.numFmts(t_cnt_).numFmtId + 1
END;
t_cnt_ := t_cnt_ + 1;
workbook_.numFmts(t_cnt_).numFmtId := t_num_fmt_id_;
workbook_.numFmts(t_cnt_).formatCode := p_format_;
workbook_.numFmtIndexes(t_num_fmt_id_) := t_cnt_;
END IF;
RETURN t_num_fmt_id_;
END Get_NumFmt;
PROCEDURE Cell(p_col_ PLS_INTEGER,
p_row_ PLS_INTEGER,
p_value_ NUMBER,
p_num_fmt_id_ PLS_INTEGER := NULL,
p_font_id_ PLS_INTEGER := NULL,
p_fill_id_ PLS_INTEGER := NULL,
p_border_id_ PLS_INTEGER := NULL,
p_alignment_ Dat_Excel_Builder_Util_API.tp_alignment := NULL,
p_sheet_ PLS_INTEGER := NULL) IS
t_sheet_ PLS_INTEGER := NVL(p_sheet_, workbook_.sheets.count());
BEGIN
IF p_value_ IS NOT NULL THEN
workbook_.sheets(t_sheet_).rows(p_row_)(p_col_).value_ := p_value_;
workbook_.sheets(t_sheet_).rows(p_row_)(p_col_).style := NULL;
workbook_.sheets(t_sheet_).rows(p_row_)(p_col_).style := Get_X_FId(t_sheet_,
p_col_,
p_row_,
p_num_fmt_id_,
p_font_id_,
p_fill_id_,
p_border_id_,
p_alignment_);
ELSIF workbook_.sheets(t_sheet_).rows(p_row_).exists(p_col_) THEN
workbook_.sheets(t_sheet_).rows(p_row_).delete(p_col_);
END IF;
END Cell;
PROCEDURE Cell(p_col_ PLS_INTEGER,
p_row_ PLS_INTEGER,
p_value_ VARCHAR2,
p_num_fmt_id_ PLS_INTEGER := NULL,
p_font_id_ PLS_INTEGER := NULL,
p_fill_id_ PLS_INTEGER := NULL,
p_border_id_ PLS_INTEGER := NULL,
p_alignment_ Dat_Excel_Builder_Util_API.tp_alignment := NULL,
p_sheet_ PLS_INTEGER := NULL) IS
t_sheet_ pls_integer := NVL(p_sheet_, workbook_.sheets.count());
t_alignment_ Dat_Excel_Builder_Util_API.tp_alignment := p_alignment_;
BEGIN
IF p_value_ IS NOT NULL THEN
workbook_.sheets(t_sheet_).rows(p_row_)(p_col_).value_ := Add_String(Dat_Excel_Builder_Util_API.Clean_String(p_value_));
IF t_alignment_.wrapText IS NULL AND instr(p_value_, CHR(13)) > 0 THEN
t_alignment_.wrapText := TRUE;
END IF;
workbook_.sheets(t_sheet_).rows(p_row_)(p_col_).style := 't="s" ' ||
Get_X_FId(t_sheet_,
p_col_,
p_row_,
p_num_fmt_id_,
p_font_id_,
p_fill_id_,
p_border_id_,
t_alignment_);
ELSIF workbook_.sheets(t_sheet_).rows(p_row_).exists(p_col_) THEN
workbook_.sheets(t_sheet_).rows(p_row_).delete(p_col_);
END IF;
END Cell;
PROCEDURE Cell(p_col_ PLS_INTEGER,
p_row_ PLS_INTEGER,
p_value_ DATE,
p_num_fmt_id_ PLS_INTEGER := NULL,
p_font_id_ PLS_INTEGER := NULL,
p_fill_id_ PLS_INTEGER := NULL,
p_border_id_ PLS_INTEGER := NULL,
p_alignment_ Dat_Excel_Builder_Util_API.tp_alignment := NULL,
p_sheet_ PLS_INTEGER := NULL) IS
t_num_fmt_id_ PLS_INTEGER := p_num_fmt_id_;
t_sheet_ PLS_INTEGER := NVL(p_sheet_, workbook_.sheets.count());
BEGIN
workbook_.sheets(t_sheet_).rows(p_row_)(p_col_).value_ := p_value_ -
to_date('01-01-1904',
'DD-MM-YYYY');
IF t_num_fmt_id_ IS NULL AND NOT
(workbook_.sheets(t_sheet_).col_fmts.exists(p_col_) AND workbook_.sheets(t_sheet_).col_fmts(p_col_).numFmtId IS NOT NULL) AND NOT
(workbook_.sheets(t_sheet_).row_fmts.exists(p_row_) AND workbook_.sheets(t_sheet_).row_fmts(p_row_).numFmtId IS NOT NULL) THEN
t_num_fmt_id_ := Get_NumFmt('dd/mm/yyyy');
END IF;
workbook_.sheets(t_sheet_).rows(p_row_)(p_col_).style := Get_X_FId(t_sheet_,
p_col_,
p_row_,
t_num_fmt_id_,
p_font_id_,
p_fill_id_,
p_border_id_,
p_alignment_);
END Cell;
FUNCTION Get_Fill(p_pattern_type_ VARCHAR2, p_fg_rgb_ VARCHAR2 := NULL)
RETURN PLS_INTEGER IS
t_ind_ PLS_INTEGER;
BEGIN
IF workbook_.fills.count() > 0 THEN
FOR f IN 0 .. workbook_.fills.count() - 1 LOOP
IF (workbook_.fills(f)
.patternType = p_pattern_type_ AND
NVL(workbook_.fills(f).fgRGB, 'x') = nvl(upper(p_fg_rgb_), 'x')) THEN
RETURN f;
END IF;
END LOOP;
END IF;
t_ind_ := workbook_.fills.count();
workbook_.fills(t_ind_).patternType := p_pattern_type_;
workbook_.fills(t_ind_).fgRGB := upper(p_fg_rgb_);
RETURN t_ind_;
END Get_Fill;
FUNCTION Get_Border(p_top_ VARCHAR2 := 'thin',
p_bottom_ VARCHAR2 := 'thin',
p_left_ VARCHAR2 := 'thin',
p_right_ VARCHAR2 := 'thin') RETURN PLS_INTEGER IS
t_ind_ PLS_INTEGER;
BEGIN
IF workbook_.borders.count() > 0 THEN
FOR b IN 0 .. workbook_.borders.count() - 1 LOOP
IF (NVL(workbook_.borders(b).top, 'x') = NVL(p_top_, 'x') AND
NVL(workbook_.borders(b).bottom, 'x') = NVL(p_bottom_, 'x') AND
NVL(workbook_.borders(b).left, 'x') = NVL(p_left_, 'x') AND
NVL(workbook_.borders(b).right, 'x') = NVL(p_right_, 'x')) THEN
RETURN b;
END IF;
END LOOP;
END IF;
t_ind_ := workbook_.borders.count();
workbook_.borders(t_ind_).top := p_top_;
workbook_.borders(t_ind_).bottom := p_bottom_;
workbook_.borders(t_ind_).left := p_left_;
workbook_.borders(t_ind_).right := p_right_;
RETURN t_ind_;
END Get_Border;
PROCEDURE New_Sheet(p_sheetname_ VARCHAR2 := NULL) IS
t_nr_ PLS_INTEGER := workbook_.sheets.count() + 1;
t_ind_ PLS_INTEGER;
BEGIN
workbook_.sheets(t_nr_).name := NVL(DBMS_XMLGEN.CONVERT(TRANSLATE(p_sheetname_,
'a/\ ]*:?',
'a')),
'Sheet' || t_nr_);
IF workbook_.strings.count() = 0 THEN
workbook_.str_cnt := 0;
END IF;
IF workbook_.fonts.count() = 0 THEN
t_ind_ := Get_Font('Calibri');
END IF;
IF workbook_.fills.count() = 0 THEN
t_ind_ := Get_Fill('none');
t_ind_ := Get_Fill('gray125');
END IF;
IF workbook_.borders.count() = 0 THEN
t_ind_ := Get_Border('', '', '', '');
END IF;
END New_Sheet;
PROCEDURE Query_To_Sheet(p_sql_ VARCHAR2,
p_column_headers_ BOOLEAN := TRUE,
p_directory_ VARCHAR2 := NULL,
p_filename_ VARCHAR2 := NULL,
p_sheet_ PLS_INTEGER := NULL,
p_sheetname_ VARCHAR2 := NULL) IS
t_sheet_ PLS_INTEGER;
t_c_ INTEGER;
t_col_cnt_ INTEGER;
t_desc_tab_ DBMS_SQL.DESC_TAB2;
d_tab_ DBMS_SQL.DATE_TABLE;
n_tab_ DBMS_SQL.NUMBER_TABLE;
v_tab_ DBMS_SQL.VARCHAR2_TABLE;
t_bulk_size_ PLS_INTEGER := 200;
t_r_ INTEGER;
t_cur_row_ PLS_INTEGER;
BEGIN
IF p_sheet_ IS NULL THEN
New_Sheet(p_sheetname_);
END IF;
t_c_ := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(t_c_, p_sql_, DBMS_SQL.NATIVE);
DBMS_SQL.DESCRIBE_COLUMNS2(t_c_, t_col_cnt_, t_desc_tab_);
FOR c IN 1 .. t_col_cnt_ LOOP
IF p_column_headers_ THEN
Cell(c, 1, t_desc_tab_(c).col_name, p_sheet_ => t_sheet_);
END IF;
CASE
WHEN t_desc_tab_(c).col_type IN (2, 100, 101) THEN
DBMS_SQL.DEFINE_ARRAY(t_c_, c, n_tab_, t_bulk_size_, 1);
WHEN t_desc_tab_(c).col_type IN (12, 178, 179, 180, 181, 231) THEN
DBMS_SQL.DEFINE_ARRAY(t_c_, c, d_tab_, t_bulk_size_, 1);
WHEN t_desc_tab_(c).col_type in (1, 8, 9, 96, 112) THEN
DBMS_SQL.DEFINE_ARRAY(t_c_, c, v_tab_, t_bulk_size_, 1);
ELSE
NULL;
END CASE;
END LOOP;
t_cur_row_ := CASE
WHEN p_column_headers_ THEN
2
ELSE
1
END;
t_sheet_ := NVL(p_sheet_, workbook_.sheets.count());
t_r_ := DBMS_SQL.EXECUTE(t_c_);
LOOP
t_r_ := DBMS_SQL.FETCH_ROWS(t_c_);
IF t_r_ > 0 THEN
FOR c IN 1 .. t_col_cnt_ LOOP
CASE
WHEN t_desc_tab_(c).col_type IN (2, 100, 101) THEN
DBMS_SQL.COLUMN_VALUE(t_c_, c, n_tab_);
FOR i IN 0 .. t_r_ - 1 LOOP
IF n_tab_(i + n_tab_.first()) IS NOT NULL THEN
Cell(c,
t_cur_row_ + i,
n_tab_(i + n_tab_.first()),
p_sheet_ => t_sheet_);
END IF;
END LOOP;
n_tab_.delete;
WHEN t_desc_tab_(c).col_type in (12, 178, 179, 180, 181, 231) THEN
DBMS_SQL.COLUMN_VALUE(t_c_, c, d_tab_);
FOR i IN 0 .. t_r_ - 1 LOOP
IF d_tab_(i + d_tab_.first()) IS NOT NULL THEN
Cell(c,
t_cur_row_ + i,
d_tab_(i + d_tab_.first()),
p_sheet_ => t_sheet_);
END IF;
END LOOP;
d_tab_.delete;
WHEN t_desc_tab_(c).col_type in (1, 8, 9, 96, 112) THEN
DBMS_SQL.COLUMN_VALUE(t_c_, c, v_tab_);
FOR i IN 0 .. t_r_ - 1 LOOP
IF v_tab_(i + v_tab_.first()) IS NOT NULL THEN
Cell(c,
t_cur_row_ + i,
v_tab_(i + v_tab_.first()),
p_sheet_ => t_sheet_);
END IF;
END LOOP;
v_tab_.delete;
ELSE
NULL;
END CASE;
END LOOP;
END IF;
EXIT WHEN t_r_ != t_bulk_size_;
t_cur_row_ := t_cur_row_ + t_r_;
END LOOP;
DBMS_SQL.CLOSE_CURSOR(t_c_);
EXCEPTION
WHEN OTHERS THEN
IF DBMS_SQL.IS_OPEN(t_c_) THEN
DBMS_SQL.CLOSE_CURSOR(t_c_);
END IF;
RAISE;
END Query_To_Sheet;
BEGIN
Query_To_Sheet(p_sql_ => l_sql_to_execute,
p_sheetname_ => l_sheet_name);
l_excel_file := Dat_Excel_Builder_Util_API.Finish(workbook_);
l_rec.blob_ := l_excel_file;
l_attachments(l_attachment_name) := l_rec;
COMMAND_SYS.Mail(sender_ => l_sender_name,
from_ => NULL,
to_list_ => l_receiver_list,
cc_list_ => NULL,
bcc_list_ => NULL,
subject_ => l_subject,
text_ => TO_CLOB(l_message),
attachments_ => l_attachments,
mail_sender_ => NULL);
END;