使用 sqlplus 轮询并将结果记录到文件中强制换行

Posted

技术标签:

【中文标题】使用 sqlplus 轮询并将结果记录到文件中强制换行【英文标题】:Polling with sqlplus and logging result to a file forces in lineshifts 【发布时间】:2016-09-05 15:50:20 【问题描述】:

我正在尝试制作一个我认为是简单的 bash 脚本,它定期轮询远程数据库并将以下元素记录到日志文件中,其中只有最后两个元素是来自 sql 的行元素-查询:

Readable timestamp, timestamp, string, Request Duration

示例结果应如下所示:

Mon Sep 5 13:28:30 CEST 2016,1473074940127,text content, 4
Mon Sep 5 13:28:40 CEST 2016,1473074940127,text content, 3
Mon Sep 5 13:28:50 CEST 2016,1473074940127,text content, 2

此脚本的目的是主动监控和记录从本地环境到远程数据库的响应时间,以便我们可以在测试时对应本地应用程序中的错误,以及最终的数据库连接问题或类似问题。出于这个原因,我们希望脚本也输出任何最终的错误消息。这些格式并不重要,但我们希望看到标准输出是“舒适”的单行。

我为这个场合编写了以下非常简单的脚本,但它有一些怪癖,无论我如何调整它似乎都无法消除:

sleep_duration=10
while true; do

SECONDS=0
timestamp="$(date),"
echo -n $timestamp >> $LOG_PATH


sqlplus -s $database << EOF >> $LOG_PATH
    whenever sqlerror exit sql.sqlcode

    set echo off
    set heading off
    set linesize 1000
    set pagesize 0
    set numformat 9999999999999
    set colsep ,
    set null null

    SELECT COLUMN1, COLUMN2 FROM TABLE1;
    exit;
EOF

echo ', '$SECONDS >> $LOG_PATH

sleep $sleep_duration
done

我们发送到数据库的查询总是返回恰好包含两个元素的一行,并且这两个元素(INTEGER、VARCHAR2)总是具有相同的固定宽度。如果我们的应用程序中的某些内容在本地中断,则它们都可以为 null。

我知道 SPOOL 和 TIMING 命令的存在,但已经测试并选择不使用这两个命令。假脱机查询不会向日志文件输出任何最终的意外错误,至少不会在没有错误终端输出的情况下进行欺骗,所以我首先使用了管道解决方案。看起来更简单。在数据库停机的情况下,TIMING 似乎无法正确显示查询经过的时间,所以我采用了 SECONDS 方法。我知道这对于包含的所有 sqlplus 开销来说并不精确,但无论如何我们对精确的指标不感兴趣,我们通常只对查看该过程是否需要不到一秒或几分钟感兴趣。毫秒基本上是无关紧要的。

然而,这有一个大问题,因为日志最终看起来像这样:

Mon Sep 5 14:50:47 CEST 2016, 1473079873483,text output

, 0
Mon Sep 5 14:50:57 CEST 2016, 1473079883483,text output

, 0
Mon Sep 5 14:51:07 CEST 2016, 1473079893489,text output

, 0

我希望set pagesize 0 从结果中删除所有此类尾随换行符,但无论如何,每次查询后似乎都有两个换行符,而且无论我尝试什么,我似乎都无法摆脱它。假脱机查询给出了相同的结果。

我考虑改为在 sqlplus-session 内循环,但由于脚本必须或多或少地永久运行,我担心这会永久保留一个连接槽,用于其他功能无意义的目的,这是不可取的。根据需要打开和关闭它似乎更明智。我根本没有测试过这种方法,主要是因为这个原因。

我希望避免对日志进行后处理,因为我不知道其中可能会弹出什么样的错误条目,所以稍后美化它就像是不必要的正则表达式噩梦。

是否有人对如何使输出在单行上整齐排列,没有不必要的换行和空格,同时确保不丢失任何最终错误有任何建议?

【问题讨论】:

字符串列的数据类型是什么 - 你说的是 varchar2 但真的不是 char 或 clob;它有多大;字符串值是否有尾随空格或嵌入的换行符? 字符串值是一个普通的 URL,字段声明为 varchar2(250),内容的实际长度为空,或大约 560 个字符长。没有空格或换行符,嵌入的,前导的或尾随的 - 尽管我会三重检查是否没有尾随换行符只是为了验证。应该没有,但是 URL 是部分生成的,所以我不是 100% 确定没有检查。 可以确认,文本字段中根本没有空格或换行符,并且字符串总是正好 36 个字符宽。 (如果发生爆炸,则为 null) 【参考方案1】:

这是一种解决方法,因为它实际上并没有解决我仍然无法解释的换行问题,但一个非常简单的解决方案是将 sqlplus 输出直接存储到bash 变量,而不是尝试直接从 sqlplus 打印到日志,并将其作为任何其他变量回显。

基本上,我必须在上面的脚本中进行更改,就是将 sqlplus 命令包装在变量 QUERY_OUTPUT=$() 中并删除输出的管道:

while true; do

SECONDS=0
timestamp=$(date) 
echo -n "$timestamp, " >> $LOG_PATH

QUERY_RESULT=$(sqlplus -s $database << EOF
    whenever sqlerror exit sql.sqlcode

    set echo off
    set heading off
    set linesize 1000
    set pagesize 0
    set numformat 9999999999999
    set colsep ', '
    set null null

    SELECT TIME, BROKER_NAME FROM ACTIVEMQ_LOCK;
    exit;
EOF
)

echo $QUERY_RESULT, $SECONDS seconds elapsed >> $LOG_PATH

sleep $sleep_duration
done

我自己偶然发现了这个解决方案,同时花了几个小时试图记录和验证我在上面的问题中首先尝试过和未尝试过的内容,并且经过一些测试,它似乎工作得很好。一切都干净且可控地打印在一行上,任何最终的错误似乎也干净地打印在这一行上。准确定位我正在寻找的内容。

考虑到自从我找到解决方案后一开始就没有问这个问题,但我认为其他人也可能从中看到一些价值,我至少无法找到任何明显的重复项。不妨分享一下!

【讨论】:

我没有你的系统来测试,但似乎你可以把它浓缩成一行,即 echo $QUERY_RESULT",$SECONDS seconds elapsed" &gt;&gt; $LOG_PATH 。可能有一种方法可以在 sql 处理中消除额外的新行,但这对于您的目的来说似乎已经足够了(但不确定为什么您有 '$SECONDS',因为这会产生文字值 $SECONDS。)祝你好运。 啊,当然可以。它最终出现在两条线上,本质上是因为我认为它是两个“问题”。将使用它进行编辑! 对于打勾的 SECONDS 位,刻度不是环绕秒,而是前导和尾随文本。我不是一个熟悉 bash 的本地老手,所以我什至没有意识到它们是不必要的,呵呵。将快速对其进行测试,并针对两者进行修复!如果有人能立即看到摆脱尾随换行符的技巧,那就太酷了

以上是关于使用 sqlplus 轮询并将结果记录到文件中强制换行的主要内容,如果未能解决你的问题,请参考以下文章

如何将选择的结果分配给 sqlplus 变量

如何将select的结果分配到sqlplus变量中

获取 sqlplus 的结果并放入脚本 .bat 上的变量中

SQLPlus 保存到 CSV 文件而不格式化

Oracle SqlPlus导出查询结果

在 sqlplus 中使用假脱机运行多个查询