首页
>
动态语言,
挨踢(IT),
数据库 > 多表关联下的测试数据制造方法(三):使用SQL Loader导入数据
多表关联下的测试数据制造方法(三):使用SQL Loader导入数据
多表关联下的测试数据制造方法系列
- 多表关联下的测试数据制造方法(一):思路详解
- 多表关联下的测试数据制造方法(二):将数据导出为CSV文件
在上一节,D瓜哥给大家介绍了一下如何使用存储过程,将数据导出成CSV文件。既然有数据文件了,我们该如何将这些数据导入到数据库中呢?!Oracle数据库提供了SQL Loader这样一个将数据文件导入到数据库中的功能。这篇文章将给大家介绍一下SQL Loader的用法。另外,再炒炒剩饭,把D瓜哥以前使用TCL语言写的脚本分享给大家。
SQL Loader简介
概念
SQL Loader是ORACLE的数据加载工具,通常用来将操作系统文件(数据)迁移到ORACLE数据库中。SQL Loader是大型数据仓库选择使用的加载方法,因为它提供了最快速的途径(DIRECT,PARALLEL)。
用法
首先,建立需要导入数据的表。如果存在就不需要新建了。
其次,编写一个控制文件。内容如下:
08 | FIELDS TERMINATED BY "," |
10 | empno NUMBER(4) NOT NULL , |
14 | hiredate DATE 'YYYYMMDD' , |
最后,SQL Loader的使用也很简单,只需要一条指令。如下:
1 | sqlldr USERID=scott/tiger CONTROL=load1.ctl DIRECT=TRUE PARALLEL= true |
补充一句,控制文件高亮的一行是控制SQL Loader进行何种操作的。SQL Loader共提供四种操作分类:
- insert,为缺省方式,在数据装载开始时要求表为空;
- append,在表中追加新记录
- replace,删除旧记录,替换成新装载的记录;
- truncate,同上
这里,只给大家介绍这么多。如果想了解详细情况,请阅读Oracle提供的官方文档,文档中详细描述了SQL Loader的方方面面。
TCL导入脚本
上面简单介绍了一些SQL Loader的一些知识。下面给大家分享要给由TCL写成的使用SQL Loader导入数据的脚本。
关于TCL语言运行环境的配置,请看“D瓜哥的开源项目:CompareDatamodel”,D瓜哥在这篇文章里做过介绍。
配置文件
这个配置文件不是上面提到的”控制文件”。废话不多,直接上内容:
01 | data_repeat_msg|已经汇总过数据,请勿重复汇总。 |
09 | insert_table_failure_prompt|导入失败 |
读取配置文件
有配置文件了,需要有读取配置文件的脚本,如下:
07 | set fid [ open "$absoluteCurrentPath//data_dic.txt" r] |
08 | while {[eof $fid] != 1} { |
09 | set curLine [gets $fid] |
14 | set curLineList [ split $curLine "|" ] |
16 | set index [lindex $curLineList 0] |
18 | set value [lindex $curLineList 1] |
19 | set label($index) $value |
注意,这个脚本不能直接使用。需要导入到其他文件才能用。具体用法见下面。
导入脚本
上面所有的铺垫工作已经做完。现在该主角登场了。导入脚本如下:
003 | exec tclsh "$0" ${1+ "$@" } |
012 | encoding system identity |
014 | package require Oratcl |
017 | set currentPath [ file dirname [info script]] |
020 | set absoluteCurrentPath [ pwd ] |
023 | source "$absoluteCurrentPath//read_data_dic.tcl" |
032 | proc query_tableFieldtype {sInsertTableName columnNamesList fhandleSelect {dateFormat "YYYYMMDDHH24MISS" } } { |
035 | set listFieldLength [llength $columnNamesList] |
036 | set strSql "select column_name, decode(data_type, 'VARCHAR2', 'CHAR NULLIF (' || column_name || '=BLANKS) \"trim(:' || column_name || ')\"', 'FLOAT', 'DECIMAL EXTERNAL NULLIF(' || column_name || '=BLANKS)', 'NUMBER', decode(data_precision, 0, 'INTEGER EXTERNAL NULLIF (' || column_name || '=BLANKS)', decode(data_scale, 0, 'INTEGER EXTERNAL NULLIF (' || column_name || '=BLANKS)', 'DECIMAL EXTERNAL NULLIF (' || column_name || '=BLANKS)')), 'DATE', 'DATE \"[string trim $dateFormat]\" NULLIF (' || column_name || '=BLANKS)', null) data_type" |
037 | append strSql " from USER_TAB_COLUMNS where table_name =upper('" |
038 | append strSql $sInsertTableName "') and column_name in (" |
039 | append strSql upper( '[join $columnNamesList ' ),upper( ']' )) |
043 | if [catch {orasql $fhandleSelect $strSql} errmsg] { |
045 | } elseif {$errmsg != 0 && $errmsg != 100 && $errmsg != 1403} { |
049 | set listFieldAndType [list] |
050 | while {[oramsg $fhandleSelect] == 0} { |
051 | orafetch $fhandleSelect -datavariable rowResult - command { |
052 | set sFieldName [lindex $rowResult 0] |
053 | lappend listFieldAndType $sFieldName |
054 | set sFieldType [lindex $rowResult 1] |
055 | lappend listFieldAndType $sFieldType |
060 | set listFieldLength [llength $columnNamesList] |
061 | set listFieldType [list] |
062 | for { set i 0} {$i<$listFieldLength} {incr i} { |
063 | set fieldName [lindex $columnNamesList $i] |
064 | set fieldName [string trim $fieldName '] |
065 | for { set j 0} {$j<$listFieldLength} {incr j} { |
066 | set dbFieldName [lindex $listFieldAndType [ expr $j*2]] |
067 | if {[string toupper $fieldName]==[string toupper $dbFieldName]} { |
068 | lappend listFieldType [lindex $listFieldAndType [ expr $j*2+1]] |
073 | return $listFieldType |
090 | proc creat_sqlLoaderCtl {tableName columnName columnType dataFileName ctlFileName separater enclosedSymbol {isAppend true } } { |
091 | set ctlFile [ open $ctlFileName w] |
092 | set fieldLength [llength $columnName] |
093 | puts $ctlFile "LOAD DATA" |
094 | puts $ctlFile "INFILE \"$dataFileName\"" |
096 | puts $ctlFile "APPEND" |
098 | puts $ctlFile "TRUNCATE" |
100 | puts $ctlFile "INTO TABLE $tableName" |
101 | puts $ctlFile "FIELDS TERMINATED BY \"[string trim $separater]\"" ; |
102 | puts $ctlFile "OPTIONALLY ENCLOSED BY '[string trim $enclosedSymbol]'" ; |
105 | for { set i 0} {$i<$fieldLength } {incr i} { |
106 | if {$i==[ expr $fieldLength-1]} { |
107 | puts $ctlFile "[string toupper [lindex $columnName $i]] [lindex $columnType $i] " |
109 | puts $ctlFile "[string toupper [lindex $columnName $i]] [lindex $columnType $i]," |
117 | proc getDealFileInfo {sInsertTableName columnNamesList isAppend periodType indiDate fileNamesList} { |
127 | if [catch { set dbhandle [db_connect $label(db_sid) $label(db_user) $label(db_password)]} logon_errmsg] { |
131 | if [catch { set fhandleSelect [db_open $dbhandle] } open_errmsg] { |
139 | set sFilePath [string trim "$label(file_path)" ] |
140 | set sWorkFilePath [string trim "$label(file_path)" ] |
145 | set sqldrLog $ env (AGENTAPPLOG)/[lindex $argv 1]\_[lindex $argv 3]_ |
147 | set listFieldType [query_tableFieldtype $sInsertTableName $columnNamesList $fhandleSelect] |
150 | foreach sFileName $fileNamesList { |
151 | set sPathAndFileName "" |
152 | append sPathAndFileName $sFilePath |
153 | append sPathAndFileName [string trim $sFileName] |
155 | puts $sPathAndFileName |
157 | if { ! [ file exists $sPathAndFileName] } { |
158 | error "文件不存在,请检查文件:$sPathAndFileName" |
161 | set ctlFileName [string trim $sWorkFilePath]sqlloader/sql_loader_[string trim $sInsertTableName]_[string trim $indiDate]_[string trim $periodType].ctl |
162 | puts "控制文件:$ctlFileName" |
164 | set ctlFileName [creat_sqlLoaderCtl $sInsertTableName $columnNamesList $listFieldType $sPathAndFileName $ctlFileName $label(separater) $label(enclosedSymbol) $isAppend] |
167 | append sqldrLog [clock format [clock seconds] - format "%Y%m%d%H%M%S" ] |
168 | append sqldrLog ".sqldr.log" |
171 | set loadHasError false |
173 | if {[catch { exec sqlldr $label(db_user)/$label(db_password)@$label(db_sid) control=$ctlFileName log=$sqldrLog errors=0 parallel= true } selectError]} { |
174 | set loadHasError true |
175 | set errorInfo $selectError |
179 | error "$label(insert_table_failure_prompt)$errorInfo" |
185 | set sFileDescPath $indiDate |
187 | puts "[string trim $sFileDescPath][string trim $sFileName]" |
201 | set tableName "CMN_COL_CFG" |
202 | set columnList [ split "COL_CFG_ID,COL_NAME,CREATE_DATE" "," ] |
204 | set periodType [lindex $argv 7] |
205 | set date [lindex $argv 5] |
206 | set dataFiles "td.csv" ; |
207 | set dataFileList [ split $dataFiles "," ] |
210 | getDealFileInfo $tableName $columnList $isAppend $periodType $ date $dataFileList |
由于,SyntaxHighlighter不能提供TCL语言的高亮显示。所以,这里的高亮可能还有一些问题。抱歉!
D瓜哥最近接触了一些Ruby语言,Ruby真是一个灵活的语言,同样也很好玩。非常适合自定义DSL,以后有时间D瓜哥把这个脚本再用Ruby重写一遍。
到这里,这个系列基本结束。可能还有一些问题。例如:
- 使用先制作成数据文件,然后在使用SQL Loader导入到数据库中这种方式,需要监控数据文件的生成完成时间,然后再使用进行导入。但是,制作和导入,前者在数据库中使用存储过程完成;后者使用TCL(当然也可以使用其他语言完成)。横跨两个环境,监控起来不方便;
- 使用这种方式,需要在数据库服务器上安装TCL运行环境;
综上,D瓜哥认为使用Shell脚本应该可以完整完成这个全部过程。不过,D瓜哥目前还不会写Shell,这个就留给感兴趣的朋友自己练手吧。当然,如果以后D瓜哥使用Shell实现了,还会发布出来的。
参考资料
- sqlldr的介绍
- SQL*Loader 详解
- 使用SQL Loader导入大量数据,避免使用SQL频繁写库
- Oracle官方文档:SQL*Loader
修改日志
2013年3月28日 补充问题列表。
哈哈瓜哥被你QQ空间引过来了,我这还有最新一版sqlloader要不要