PL/SQL 下SQL结果集以html形式发送邮件

简介:       在运维的过程中,有时候需要定时将SQL查询的数据结果集以html表格形式发送邮件,因此需要将SQL查询得到的结果集拼接成html代码。对于这种情形通常有二种方式来完成。

      在运维的过程中,有时候需要定时将SQL查询的数据结果集以html表格形式发送邮件,因此需要将SQL查询得到的结果集拼接成html代码。对于这种情形通常有二种方式来完成。一是直接使用cron job来定时轮询并借助os级别的邮件程序来完成。其查询结果集可以直接在SQL*Plus下通过设置html标签自动实现html表格形式。一种方式是在Oracle中使用scheduler job来定时轮询。这种方式需要我们手动拼接html代码。本文即是对第二种情形展开描述。

      关于PL/SQL下如何发送邮件可参考: PL/SQL 下邮件发送程序
      OS 下发送邮件可参考:不可或缺的 sendEmail

 

1、代码描述

--下面的代码段主要主要是用于发送数据库A部分数据同步到数据库B是出现的错误信息
--表syn_data_err_log_tbl主要是记录错误日志,也就是说只要表中出现了新的记录或者旧记录且mailed列标志为N,即表示需要发送邮件
--下面逐一描述代码段信息,该代码段可以封装到package.
 PROCEDURE email_on_syn_data_err_log (err_num   OUT NUMBER,
                                        err_msg   OUT VARCHAR2)
   AS
      v_msg_txt        VARCHAR2 (32767);
      v_sub            VARCHAR2 (100);
      v_html_header    VARCHAR (4000);
      v_html_content   VARCHAR (32767);
      v_count          NUMBER;
      v_log_seq        NUMBER (12);
      v_loop_count     NUMBER := 0;

      CURSOR cur_errlog    --使用cursor来生成表格标题部分
      IS
           SELECT '<tr >
                            <td style="vertical-align:top;padding: 5px;"> '
                  || TO_CHAR (sd.log_seq)
                  || '</td>
                            <td style="vertical-align:top;padding: 5px;"> '
                  || sd.process
                  || '</td>'
                  || '<td  style="vertical-align:top;padding: 5px;"> '
                  || sd.rec_id
                  || '</td> '
                  || '<td style="padding: 5px;"> '
                  || REPLACE (REPLACE (sd.err_msg, '<', ';'), '>', ';')
                  || '</td>'
                  || '<td  style="vertical-align:top;padding: 5px;">'
                  || TO_CHAR (sd.log_time, 'yyyy-mm-dd hh24:mi:ss')
                  || '</td>
                            </tr>',
                  sd.log_seq
             FROM syn_data_err_log_tbl sd
            WHERE sd.mailed = 'N'
         ORDER BY sd.log_seq;
   BEGIN
      err_num := common_pkg.c_suc_general;

      SELECT COUNT (*)     
        INTO v_count        -->统计当次需要发送的总记录数
        FROM syn_data_err_log_tbl sd
       WHERE sd.mailed = 'N';

      IF v_count > 0        --> 表示有记录需要发送邮件
      THEN
         SELECT 'Job process failed on ' || instance_name || '/' || host_name
           INTO v_sub       -->生成邮件的subject  
           FROM v$instance;

         v_html_header :=             -->定义表格的header部分信息
            '<html><header><style>
                    #log-table {
                    margin: 0;
                    padding: 0;
                    width: 90%;
                    border-collapse: collapse;
                    font: 12px "Lucida Grande", Helvetica, Sans-Serif;
                    border:1px solid #CCC;
                    }
                    #log-table td {
                    padding: 5px;
                    border:1px solid #CCC;
                    }
                    #log-table th {
                    padding: 5px;
                    background: black;
                    color: white;
                    text-align: left;
                    }
                    #log-table tr:nth-child(even) td {
                    background: #eee;
                    }
                    </style></header><body>
                             <table id="log-table"  style="width: 100%;border-collapse: collapse;font-size:12px;">';
         v_html_header :=              -->下面是拼接每一个字段的信息
            v_html_header
            || '<tr style="background: black;">
                     <th  style="color: white;width:100px;padding: 5px;">Log sequence</th>
                     <th  style="color: white;width:100px;padding: 5px;">Process</th>
                     <th  style="color: white;width:100px;padding: 5px;">Rec ID</th>
                     <th  style="color: white;width:100px;padding: 5px;">Error message</th>
                     <th  style="color: white;padding: 5px;">Log time</th></tr>';

         OPEN cur_errlog;     -->打开游标

         LOOP
            FETCH cur_errlog   
            INTO v_msg_txt, v_log_seq;

            EXIT WHEN cur_errlog%NOTFOUND;
            v_loop_count := v_loop_count + 1;
            v_html_content := v_html_content || v_msg_txt;   --->注意这里,不断地把从原表中的err_msg拿出来进行拼接通过v_msg_txt

            --Maximun record = 50 --
            IF v_loop_count > 50              --->这里的判断就是用于控制表格总共显示多少行
            THEN                              --->主要是用于如果由于需要拼接的行太多导致超过字符长度32767,因此从50行处截断
               v_html_content :=
                  v_html_header || v_html_content || '</table></body></html>';  --->这里添加html尾部
               SENDMAIL_PKG.sendmail (
                  bo_system_pkg.get_sys_para_value ('EMAIL_SENDER_HC_EMAIL'),   --->调用函数获得邮件的接收者,此处可以直接写接收者
                  v_sub,
                  v_html_content,
                  err_num,
                  err_msg);
               v_msg_txt := '';             --->注,此处对三个本地变量置空
               v_html_content := '';
               v_loop_count := 0;               

               UPDATE syn_data_err_log_tbl sd     --->根据log_seq字段对已经发送过的记录标记为Y
                  SET mailed = 'Y'
                WHERE sd.mailed = 'N' AND log_seq <= v_log_seq;
            -- COMMIT;
            ELSIF v_count = cur_errlog%ROWCOUNT   --->当v_count与游标取得记录数相等时,拼接表格尾部html代码,发送邮件以及更新mailed列
            THEN
               v_html_content :=
                  v_html_header || v_html_content || '</table></body></html>';
               SENDMAIL_PKG.sendmail (
                  bo_system_pkg.get_sys_para_value ('EMAIL_SENDER_HC_EMAIL'),
                  v_sub,
                  v_html_content,
                  err_num,
                  err_msg);
               v_msg_txt := '';
               v_html_content := '';

               UPDATE syn_data_err_log_tbl sd
                  SET mailed = 'Y'
                WHERE sd.mailed = 'N' AND log_seq <= v_log_seq;
            END IF;
         END LOOP;

         COMMIT;

         CLOSE cur_errlog;
      END IF;
   EXCEPTION
      WHEN NO_DATA_FOUND
      THEN
         err_num := common_pkg.c_fail_data_not_found;
      WHEN OTHERS
      THEN
         err_num := common_pkg.c_fail_user_define;
         err_msg := 'Fail in process SENDMAIL_PKG.email_on_syn_data_err_log. ';
   END; 

2、调用示例及邮件样式  

gx_admin@SYBO2SZ> DECLARE 
  2    ERR_NUM NUMBER;
  3    ERR_MSG VARCHAR2(32767);
  4  
  5  BEGIN 
  6    ERR_NUM := NULL;
  7    ERR_MSG := NULL;
  8  
  9    GX_ADMIN.SENDMAIL_PKG.EMAIL_ON_SYN_DATA_ERR_LOG ( ERR_NUM, ERR_MSG );
 10    COMMIT; 
 11  END;
 12  /

PL/SQL procedure successfully completed.


 

Oracle&nbsp;牛鹏社    Oracle DBsupport

更多参考

使用 DBMS_PROFILER 定位 PL/SQL 瓶颈代码

使用PL/SQL Developer剖析PL/SQL代码

对比 PL/SQL profiler 剖析结果

PL/SQL Profiler 剖析报告生成html

DML Error Logging 特性 

PL/SQL --> 游标

PL/SQL --> 隐式游标(SQL%FOUND)

批量SQL之 FORALL 语句

批量SQL之 BULK COLLECT 子句

PL/SQL 集合的初始化与赋值

PL/SQL 联合数组与嵌套表
PL/SQL 变长数组
PL/SQL --> PL/SQL记录

SQL tuning 步骤

高效SQL语句必杀技

父游标、子游标及共享游标

绑定变量及其优缺点

dbms_xplan之display_cursor函数的使用

dbms_xplan之display函数的使用

执行计划中各字段各模块描述

使用 EXPLAIN PLAN 获取SQL语句执行计划

目录
相关文章
|
4天前
|
SQL 存储 Oracle
Oracle的PL/SQL定义变量和常量:数据的稳定与灵动
【4月更文挑战第19天】在Oracle PL/SQL中,变量和常量扮演着数据存储的关键角色。变量是可变的“魔术盒”,用于存储程序运行时的动态数据,通过`DECLARE`定义,可在循环和条件判断中体现其灵活性。常量则是不可变的“固定牌”,一旦设定值便保持不变,用`CONSTANT`声明,提供程序稳定性和易维护性。通过 `%TYPE`、`NOT NULL`等特性,可以更高效地管理和控制变量与常量,提升代码质量。善用两者,能优化PL/SQL程序的结构和性能。
|
28天前
|
Java
有关Java发送邮件信息(支持附件、html文件模板发送)
有关Java发送邮件信息(支持附件、html文件模板发送)
26 1
|
29天前
|
SQL Perl
PL/SQL经典练习
PL/SQL经典练习
13 0
|
29天前
|
SQL Perl
PL/SQL编程基本概念
PL/SQL编程基本概念
13 0
|
1月前
|
SQL Perl
PL/SQL Developer 注册机+汉化包+用户指南
PL/SQL Developer 注册机+汉化包+用户指南
16 0
|
4天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL游标属性:数据的“导航仪”与“仪表盘”
【4月更文挑战第19天】Oracle PL/SQL游标属性如同车辆的导航仪和仪表盘,提供丰富信息和控制。 `%FOUND`和`%NOTFOUND`指示数据读取状态,`%ROWCOUNT`记录处理行数,`%ISOPEN`显示游标状态。还有`%BULK_ROWCOUNT`和`%BULK_EXCEPTIONS`增强处理灵活性。通过实例展示了如何在数据处理中利用这些属性监控和控制流程,提高效率和准确性。掌握游标属性是提升数据处理能力的关键。
|
4天前
|
SQL Oracle 安全
Oracle的PL/SQL循环语句:数据的“旋转木马”与“无限之旅”
【4月更文挑战第19天】Oracle PL/SQL中的循环语句(LOOP、EXIT WHEN、FOR、WHILE)是处理数据的关键工具,用于批量操作、报表生成和复杂业务逻辑。LOOP提供无限循环,可通过EXIT WHEN设定退出条件;FOR循环适用于固定次数迭代,WHILE循环基于条件判断执行。有效使用循环能提高效率,但需注意避免无限循环和优化大数据处理性能。掌握循环语句,将使数据处理更加高效和便捷。
|
4天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL条件控制:数据的“红绿灯”与“分岔路”
【4月更文挑战第19天】在Oracle PL/SQL中,IF语句与CASE语句扮演着数据流程控制的关键角色。IF语句如红绿灯,依据条件决定程序执行路径;ELSE和ELSIF提供多分支逻辑。CASE语句则是分岔路,按表达式值选择执行路径。这些条件控制语句在数据验证、错误处理和业务逻辑中不可或缺,通过巧妙运用能实现高效程序逻辑,保障数据正确流转,支持企业业务发展。理解并熟练掌握这些语句的使用是成为合格数据管理员的重要一环。
|
4天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL表达式:数据的魔法公式
【4月更文挑战第19天】探索Oracle PL/SQL表达式,体验数据的魔法公式。表达式结合常量、变量、运算符和函数,用于数据运算与转换。算术运算符处理数值计算,比较运算符执行数据比较,内置函数如TO_CHAR、ROUND和SUBSTR提供多样化操作。条件表达式如CASE和NULLIF实现灵活逻辑判断。广泛应用于SQL查询和PL/SQL程序,助你驾驭数据,揭示其背后的规律与秘密,成为数据魔法师。
|
1月前
|
SQL Oracle 关系型数据库
Oracle系列十一:PL/SQL
Oracle系列十一:PL/SQL