我需要根据 Oracle 数据库 (v19.20) 中保存的数据生成一系列报告(csv 格式)。
通过在可以访问数据库的本地服务器上运行 shell 脚本来生成报告,该脚本将执行 SQL*Plus 命令来提取数据。
例如:
生成报告.sh
#!/bin/sh
function logger() {
local ts=$(date +%D\ %R:%S)
echo "${ts}:${0##*/} - ${1}"
echo "${ts}:${0##*/} - ${1}" >> logs/generateReports.log
}
function prop {
grep "${1}" database.properties|cut -d'=' -f2
}
function isOracleError(){
countOfORAErrors=`grep "ORA-" logs/ *.log | wc -l`
countOfSP2Errors=`grep "SP2-" logs/ *.log | wc -l`
countofcomplileerrors=`grep "compilation errors" logs/ *.log | wc -l`
if [[ $countOfORAErrors -gt 0 || $countOfSP2Errors -gt 0 || countofcomplileerrors -gt 0 ]]
then
logger "Last Script failed with $countOfORAErrors : ORA- and $countOfSP2Errors :SP2 Errors, halting execution"
exit
else
logger "Script Completed Successfuly"
fi
}
USERID="$(prop 'USERID')"
PASSW="$(prop 'PASSW')"
HOST="$(prop 'HOST')"
PORT="$(prop 'PORT')"
SERVICE="$(prop 'SERVICE')"
TNS_STRING="'(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=${HOST})(PORT=${PORT}))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=${SERVICE})))'"
sqlplus ${USERID}/${PASSW}@${TNS_STRING} @generateReports.sql</dev/null 2>&1 | tee -a logs/dbScripts.log
logger "######################################################################################################"
logger "Process completed : Successfully. "
logger "######################################################################################################"
exit
要生成的报告数量取决于表“REPORT_CATEGORY”中保存的数据,其设置如下:
CATEGORY_CODE | 描述 |
---|---|
A | 报告类别A |
B | 报告类别 B |
C | 报告类别 C |
对于上表中找到的每个类别代码,我需要执行单独的查询(过滤包含我需要报告的数据的表中的类别值)并将结果写入本地服务器上的 csv 文件,其中shell 脚本正在运行。 例如
目前这是使用游标循环代码、执行 SQL 语句并使用“utl_file”包写入 CSV 输出来完成的:
生成报告.sql:
SET SERVEROUTPUT ON
SET ECHO ON
WHENEVER SQLERROR EXIT FAILURE ROLLBACK
CREATE OR REPLACE DIRECTORY report_dir AS '/opt/reports';
--##########################################################################################################################
DECLARE
v_file_handle utl_file.file_type;
c_category_code report_category.category_code%type;
CURSOR c_report_category is
select category_code
from report_category;
BEGIN
OPEN c_report_category;
LOOP
FETCH c_report_category into c_category_code;
EXIT WHEN c_report_category%notfound;
v_file_handle:=utl_file.fopen('report_dir',c_category_code||'.csv', 'a');
-- Execute Generic Query with filter on c_category_code and write results to v_file_handle using utl_file.put_line
utl_file.fclose (v_file_handle);
END LOOP;
CLOSE c_report_category;
END;
/
--##########################################################################################################################
EXIT
每当我尝试执行此操作时,都会遇到错误:
ORA-04088: error during execution of trigger 'RDSADMIN.RDS_DDL_TRIGGER2'
ORA-00604: error occurred at recursive SQL level 1
ORA-20900: Invalid path used for directory: /opt/reports
ORA-06512: at "RDSADMIN.RDSADMIN_TRIGGER_UTIL", line 631
ORA-06512: at line 1
ORA-06512: at line 12
似乎
utl_file
只允许我写入托管数据库的服务器上的目录,但我需要将其写入本地服务器。
另一种方法似乎是使用假脱机,但我很难用这种方法关闭不需要的消息/日志记录并拆分为多个文件。
在同一个 SQL*Plus 执行中写入多个 CSV 文件的最佳方法是什么?
数据库中的utl_file 似乎只允许我写入托管数据库的服务器上的目录。
DIRECTORY
对象指的是数据库服务器上的目录。数据库服务器没有任何客户端可以访问的目录的概念(如果可以的话可能会导致巨大的安全问题)。
UTL_FILE
只能写入
DIRECTORY
对象内的文件,因此,是的,它只能写入数据库服务器上的文件。
替代方案似乎是使用假脱机,但我很难用这种方法关闭不需要的消息/日志记录并拆分为多个文件。是的,使用 开始假脱机到文件,然后使用
SPOOL OFF
停止假脱机到文件,然后您可以开始假脱机到另一个文件。您可以参考 和
COLUMN
命令(除其他外)用于更改输出的格式以及显示或不显示的内容。