在 Oracle SQL 脚本中写入多个 CSV 文件

问题描述 投票:0回答:1

我需要根据 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 脚本正在运行。 例如

  • /opt/reports/category_a.csv
  • /opt/reports/category_b.csv
  • /opt/reports/category_c.csv

目前这是使用游标循环代码、执行 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 文件的最佳方法是什么?

oracle export-to-csv sqlplus utl-file
1个回答
0
投票

utl_file 似乎只允许我写入托管数据库的服务器上的目录。

数据库中的

DIRECTORY

对象指的是数据库服务器上的目录。数据库服务器没有任何客户端可以访问的目录的概念(如果可以的话可能会导致巨大的安全问题)。

UTL_FILE

 只能写入 
DIRECTORY
 对象内的文件,因此,是的,它只能写入数据库服务器上的文件。

替代方案似乎是使用假脱机,但我很难用这种方法关闭不需要的消息/日志记录并拆分为多个文件。

是的,使用

SPOOL filename

 开始假脱机到文件,然后使用 SPOOL OFF
 停止假脱机到文件,然后您可以开始假脱机到另一个文件。

您可以参考

SQL*Plus文档并使用SET

COLUMN
 命令(除其他外)用于更改输出的格式以及显示或不显示的内容。

© www.soinside.com 2019 - 2024. All rights reserved.