SHELL+SQLLDR读取文件更新数据至ORACLE

  • SQLLDR
  • 实现目标
  • 数据源文件test.txt
  • sqlldr的test.ctl文件
  • 解释:
  • shell代码:输入两个参数日期、表名--->执行:从指定路径读取数据源文件,读取数据库的表结构(可空和不可空等),创建该表对应的ctl文件,执行sqlldr脚本,完成数据插入-->返回数据是否全部成功插入


SQLLDR

实现目标

1.拿到一个test.txt的文件,通过shell脚本使用sqlldr命令,读取文件的内容插入到Oracle数据库中
2.注意点

  • sqlldr命令可以从.ctl文件中设置数据源的格式、也可以通过sqlldr命令中写入数据
  • 本文主要涉及三个文件”:
  • 数据源文件test.txt(格式:123456|+|主题bala|+|xxx|+|2019 每个字段以|+|分割)
  • slldr格式文件,test.ctl
  • shell脚本文件:读取参数,生成新的ctl文件,执行sqlldr脚本,判断sqlldr脚本是否执行成功并返回

数据源文件test.txt

123456|+|主题bala|+|xxx|+|2019  
123457|+|主题bala|+|xxx|+|2020
123458|+|主题bala|+|xxx|+|2021

sqlldr的test.ctl文件

OPTIONS(DIRECT=true,parallel=true,ERRORS=50,skip=0 ,bindsize=512000,readsize=1024000,skip_index_maintenance=true)
LOAD DATA
CHARACTERSET UTF8
INFILE "/home/file/test_20191018.txt"
APPEND INTO TABLE test
FIELDS TERMINATED BY '|+|'
TRAILING NULLCOLS (
 UUID
,TITLE                      CHAR(2000)
,URL                        CHAR(1000)
,CRAWL_TIME
)

解释:

部分关键字:

    userid -- ORACLE username/password

   control -- 控制文件

       log -- 记录的日志文件

       bad -- 坏数据文件

      data -- 数据文件

   discard -- 丢弃的数据文件

discardmax -- 允许丢弃数据的最大值        (默认全部)

      skip -- Number of logical records to skip  (默认0)

      load -- Number of logical records to load  (默认全部)

    errors -- 允许的错误记录数          (默认50)

      rows --(每次提交的记录数,默认: 常规路径 64, 直接路径 全部,所以使用直接路径的话,效率会比普通的好太多太多)

  bindsize --( 每次提交记录的缓冲区的大小,字节为单位,默认256000)

    silent -- 禁止输出信息 (header,feedback,errors,discards,partitions)

    direct -- 使用直通路径方式导入 (默认FALSE)

   parfile -- parameter file: name of file that contains parameter specifications

  parallel -- 并行导入  

  (默认FALSE,注意:parallel并不是让一个sqlldr语句起多个进程来加载数据,而是不锁住加载表,允许别的直接路径加载. 所以要使parallel起作用,应该先将要加载的数据文件分成多个,用多个sqlldr语句同时加载,如下例: 

skip_unusable_indexes -- disallow/allow unusable indexes or index partitions(默认FALSE)

skip_index_maintenance -- do not maintain indexes, mark affected indexes as unusable(默认FALSE,这个最好设置为TRUE)

file -- file to allocate extents from      

skip_unusable_indexes -- disallow/allow unusable indexes or index partitions (默认 FALSE)

skip_index_maintenance -- do not maintain indexes, mark affected indexes as unusable (默认 FALSE)

commit_discontinued -- commit loaded rows when load is discontinued (默认 FALSE)

readsize -- size of read buffer                  (默认 1048576)

external_table -- use external table for load; NOT_USED, GENERATE_ONLY, EXECUTE (默认 NOT_USED)

columnarrayrows -- number of rows for direct path column array (默认 5000)

streamsize -- size of direct path stream buffer in bytes (默认 256000)

multithreading -- use multithreading in direct path 

resumable -- enable or disable resumable for current session (默认 FALSE)

resumable_name -- text string to help identify resumable statement

resumable_timeout -- wait time (in seconds) for RESUMABLE (默认 7200)

date_cache -- size (in entries) of date conversion cache (默认 1000)
  • 入库方式
************* 以下是4种装入表的方式 

  APPEND // 原先的表有数据 就加在后面 

  INSERT // 装载空表 如果原先的表有数据 sqlloader会停止 默认值 

  REPLACE // 原先的表有数据 原先的数据会全部删除 

  TRUNCATE // 指定的内容和replace的相同 会用truncate语句删除现存数据

    *************

shell代码:输入两个参数日期、表名—>执行:从指定路径读取数据源文件,读取数据库的表结构(可空和不可空等),创建该表对应的ctl文件,执行sqlldr脚本,完成数据插入–>返回数据是否全部成功插入

#/bin/bash
export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK


input_date=$1
input_table_name=$2

#oracle数据库用户名、密码、地址
oracle_name=admin
oracle_password=admin
tns=127.0.0.1:1521/orcl

#sqlldr打日志
logdir=/usr/local/logfile
#sqlldr数据文件日志
baddir=/usr/local/badfile
#sqlldr存放ctlfile的位置
ctldir=/usr/local/ctlfile
set tw=100
set encoding=utf-8

#数据源文件存放位置,每天的存放一个文件夹里
localdir=/usr/local/$input_date

if [ -d $localdir ];then
echo 'file is existed'
else
echo 'file path is not existed'
mkdir -p $localdir
fi

#函数:create_ctl_file用来每天新建一个ctl文件,用来对每天新来的数据源文件进行指定格式、sqlldr配置
create_ctl_file(){
table_name=$1
temp=${ctldir}/temp/${table_name}.lst
table_upper_name=$(echo $table_name|tr '[a-z]' '[A-Z]')
table_lower_name=$(echo $table_name|tr '[A-Z]' '[a-z]')
src_data_file_path=${localdir}/${table_lower_name}_${input_date}.txt
echo 'create ctl:'$src_data_file_path

# 使用sqlplus读取系统结构表,获取每个字段的类型,如果某个字段的长度大于sqlldr一次最大能读取的255长度,那么要一定要指定类型CHAR(100),否则会报错
cols=`sqlplus -S $oracle_name/$oracle_password@$tns <<EOF
set pagesize 0
set heading off
set echo off
set trimspool on 
set feedback off
set linesize 1000
spool $temp
select case
             when t.COLUMN_ID = 1 then
              ' '
             else
              ','
           end || RPAD(RPAD(T.column_name, 32) || case
                         when t.DATA_TYPE in ('CHAR', 'VARCHAR', 'VARCHAR2') AND
                              T.DATA_LENGTH >= 255 THEN
                          'CHAR(' || T.DATA_LENGTH || ')'
                         ELSE
                          ''
                       END,
                      100) as colm
      from user_tab_columns t
      LEFT JOIN USER_COL_COMMENTS P
        ON T.COLUMN_NAME = P.column_name
       AND T.TABLE_NAME = P.table_name
     where T.table_name = '${table_upper_name}'
     ORDER BY T.COLUMN_ID;
spool off
EOF`                                  
#
ctlfile=${ctldir}/${table_lower_name}.ctl
#将字段类型、sqlldr的配置项写入到ctlfile里去
echo $ctlfile
echo "OPTIONS(DIRECT=true,parallel=true,ERRORS=50,skip=0 ,bindsize=512000,readsize=1024000,skip_index_maintenance=true)" > $ctlfile
echo 'run DATA' >>$ctlfile
echo 'CHARACTERSET UTF8' >>$ctlfile
echo 'INFILE '\"$src_data_file_path\" >>$ctlfile
echo 'APPEND INTO TABLE '$table_upper_name >>$ctlfile
echo "FIELDS TERMINATED BY '|+|'" >>$ctlfile
echo 'TRAILING NULLCOLS (' >>$ctlfile
cat $temp  >>$ctlfile
echo ')' >>$ctlfile
}


#函数,run:执行sqlldr,判断执行结果
run(){
table_lower_name=$(echo $1|tr '[A-Z]' '[a-z]')
table_upper_name=$(echo $1|tr '[a-z]' '[A-Z]')
table_name=$table_lower_name
ctlfile=${ctldir}/$table_name.ctl
echo 'start create :'$ctlfile

#执行创建ctlfile函数
create_ctl_file $1

 
#执行sqlldr命令
runer=`sqlldr $oracle_name/$oracle_password@$tns control=$ctlfile  log=${logdir}/${table_upper_name}_${input_date}.log bad=${baddir}/${table_upper_name}_${input_date}.bad `

#runer是执行的返回字段串,最后一个字段是插入的行数,比如"1234.",去除'.',得出成功插入的行数sql_inserted_num
sql_inserted_num=`echo  $runer|awk -F ' ' '{print $NF}'|head -c-2`

#判断是否执行成功,即判断数据源文件的数据是否都插入的表中 
file_nums=`cat $src_data_file_path | wc -l`

if [ $file_nums -eq $sql_inserted_num ];then
  echo ${table_upper_name}"插入成功"
  exit 0
else
  echo $file_nums
  echo $sql_inserted_num
  echo ${table_upper_name}"插入失败"
  exit 1
fi
}

#执行run函数
run $input_table_name