.net导入Oracle数据优化小记

.net导入Oracle数据优化小记

工作中遇到一个项目需要每次部署时导入Oracle数据库约4万条数据,原计划使用dmp格式导入,但是这种方式需要依赖数据库的imp.exe文件,环境影响度比较大,于是决定使用Excel进行导入。最初使用Excel导入时每次平均耗时7分钟,不符合项目要求,经过优化后导入时间缩短到30秒左右。以下为本次优化研究的优化小记,希望在以后遇到此类问题时有所帮助。欢迎各位大神评论指教!

一、数据环境

数据库版本为Oracle11g,导入数据涉及数据库中18张表,约4万条数据。数据预先保存为Excel文档,一个sheet为一张表数据,每个sheet第一行为表字段名称,第二行为表字段类型,用于导入时进行数据格式转换。

二、引用组件

1、NOPI,使用 NPOI 可以在没有安装 Office 或者相应环境的机器上对 Excel文档进行读写。本文的研究主要用到了Excel文档的读取,代码如下:

using (FileStream fs = File.OpenRead(@"d:/myMergeCell.xls"))   //打开myxls.xls文件
{
        HSSFWorkbook wk = new HSSFWorkbook(fs);   //把xls文件中的数据写入wk中
        for (int i = 0; i < wk.NumberOfSheets; i++)  //NumberOfSheets是myxls.xls中总共的表数
        {
                   ISheet sheet = wk.GetSheetAt(i);   //读取当前表数据
                   IRow rowName = sheet.GetRow(0);//表字段名
                   IRow rowType = sheet.GetRow(1);//表字段类型
                   for (int j = 2; j <= sheet.LastRowNum; j++)  //前两行为字段行,从第3行开始为数据行
                   {
                            IRow row = sheet.GetRow(j);  //读取当前行数据
                            if (row != null)
                            {
                                     for (int k = 0; k < row.LastCellNum; k++)  //LastCellNum 是当前行的总列数
                                     {
                                               ICell cell = row.GetCell(k); //读取当前单元格数据
                                               if (cell != null)
                                               {
                                             //对单元格进行操作......
                                                       //cell.DateCellValue获取日期格式的值
                                                       //cell.NumericCellValue获取数值格式的值
                                                       //cell.StringCellValue获取字符串格式的值
                                   }
                                     }
                            }
                   }
         }
}  

2、Oracle.DataAccess,Oracle公司提供的驱动,相对.net中的System.Data. OracleClient来说,能够为Oracle数据库提供更多更好的支持,其中包含支持批量导入的OracleBulkCopy类,能够快速的向Oracle数据库中导入大量的数据。

OracleBulkCopy类批量导入的代码如下:

using (var conn = new OracleConnection(connStr))
{
         conn.Open();//先打开数据库连接
         using (OracleBulkCopy bcp = new OracleBulkCopy(conn))
         {
                   bcp.BatchSize = 400;//这是批尺寸可以调整
                   bcp.BulkCopyTimeout = 1000; //设置超时
                   bcp.DestinationTableName = table;//要导入的数据库表名
                   bcp.ColumnMappings.Clear();
                   for (int k = 0; k < rowName.LastCellNum; k++)
                   {//设置表字段映射,不设置的话可能会导致导入的数据列错乱
                            bcp.ColumnMappings.Add(rowName.GetCell(k).StringCellValue, rowName.GetCell(k).StringCellValue);
                   }
                   bcp.WriteToServer(dataTable);//将表数据转化为DataTable再进行批量导入
         }
}

三、研究过程

1、逐条导入。首先遍历Excel中的sheet来获取每个表的数据,并根据sheetName和sheet的前两行数据拼接插入语句,注意在此之前需检查表是否存在,并先删除表中全部数据再进行导入。代码如下:

string table = sheet.SheetName;//表名
command.CommandText = "select * from all_tables where table_name=:table_name";
command.Parameters.Clear();
command.Parameters.Add(":table_name", table);
var reader = command.ExecuteReader();
if (!reader.HasRows)
{
         reader.Close();
         System.Console.WriteLine(table+"表不存在" );
         continue;
}
//删除表数据
command.CommandText = "delete from " + table;
command.Parameters.Clear();
command.ExecuteNonQuery();
string field = "";//字段名
string fieldValue = "";//字段值
for (int k = 0; k < rowName.LastCellNum; k++)
{
         field += rowName.GetCell(k).StringCellValue + ",";//拼接字段名
         fieldValue += ":" + rowName.GetCell(k).StringCellValue + ",";//拼接绑定字段值
}
command.CommandText = "insert into " + table + "(" + field.TrimEnd(‘,‘) + ") values(" + fieldValue.TrimEnd(‘,‘) + ")";

之后遍历数据行,根据字段类型获取不同类型的单元格数据进行绑定,代码如下:

if (cell != null)
{
         if (rowType.GetCell(k).StringCellValue == "DATE")
                   command.Parameters.Add(":" + rowName.GetCell(k).StringCellValue, cell.DateCellValue);
         else if (rowType.GetCell(k).StringCellValue == "NUMBER")
                   command.Parameters.Add(":" + rowName.GetCell(k).StringCellValue, cell.NumericCellValue);
         else
                   command.Parameters.Add(":" + rowName.GetCell(k).StringCellValue, cell.StringCellValue);
}
else
         command.Parameters.Add(":" + rowName.GetCell(k).StringCellValue, DBNull.Value);

最终执行sql语句并记录影响行数,代码如下:

try
{
         sumCount+=command.ExecuteNonQuery();
}
catch (Exception ex)
{
         Console.WriteLine(ex.Message);
}

程序运行多次记录导入所有数据所需时间,平均耗时7min ,而如果让网页端等待后台数据导入,等待时间7min显然是不合理的,所以需要对数据导入方法进行优化。

2、多线程逐条导入。每一张表创建一个线程,用第1点的方法进行导入,代码如下:

for (int i = 0; i < wk.NumberOfSheets; i++)  //NumberOfSheets是myxls.xls中总共的表数
{
         var t = new Thread(new ParameterizedThreadStart(impTheard));// impTheard方法中执行第1点的步骤
         t.Start(i);
}

运行多次平均耗时缩短了1分钟左右,导入时间依然太长。由于不同的表数据量相差较大,数据量最大的一张表数据有2万多条,所以又将数据量较大的表拆分为多个线程进行导入,通过控制调整线程导入数据量来控制线程数量,因为线程运行需传入较多参数,所以创建impTable类来保存参数,代码如下:

int c = 2;//sheet行号
int yczxl = 4000;//每个线程导入行数
for (; c <= sheet.LastRowNum; c += yczxl+1)
{
         var t = new Thread(new ThreadStart(new impTable(table, c, ((c + yczxl) > sheet.LastRowNum ? sheet.LastRowNum : (c + yczxl)), field, fieldValue, sheet, rowName, rowType).imp));
         t.Start();
}

多次运行程序导入数据平均耗时6min,和未拆分前相差不大,所以还需进一步优化。

3、分别使用Oracle.DataAccess和System.Data.OracleClient进行导入,Oracle.DataAccess中的OracleCommand在进行变量绑定时可以使用command.Parameters.Add(string name,object val)方法,但是在4.0版本的System.Data.OracleClient中Parameters.Add(string name,object val) 方法已被否决,需替换为Parameters.AddWithValue(string name,object val)。运行程序对比发现两者导入耗时并没有明显变化。

4、使用OracleBulkCopy类批量导入,导入时需先将Excel文档中的sheet转为DataTable对象,注意必须设置DataTable字段类型,否则导入时会因数据类型不匹配而出现很多错误。OracleBulkCopy类批量导入的代码上一节已贴,这里就不再贴出来了。Sheet转DataTable代码如下:

DataTable dt = new DataTable();

//第一行是字段
IRow headRow = sheet.GetRow(0);
IRow rowType = sheet.GetRow(1);//表字段类型
//设置datatable字段
for (int i = headRow.FirstCellNum, len = headRow.LastCellNum; i < len; i++)
{
         if (rowType.Cells[i].StringCellValue == "DATE")//设置DataTable字段为DATE类型
                   dt.Columns.Add(headRow.Cells[i].StringCellValue, typeof(DateTime));
         else if (rowType.Cells[i].StringCellValue == "NUMBER")//设置DataTable字段为NUMBER类型
                   dt.Columns.Add(headRow.Cells[i].StringCellValue, typeof(Int32));
         else
                   dt.Columns.Add(headRow.Cells[i].StringCellValue);
}
//遍历数据行
for (int i = 2, len = sheet.LastRowNum + 1; i < len; i++)
{
         IRow tempRow = sheet.GetRow(i);
         DataRow dataRow = dt.NewRow();
         //遍历一行的每一个单元格
         for (int r = 0, j = tempRow.FirstCellNum, len2 = tempRow.LastCellNum; j < len2; j++, r++)
         {
                 ICell cell = tempRow.GetCell(j);
                   if (cell != null)
                   {
                            if (rowType.GetCell(j).StringCellValue == "DATE")
                                     dataRow[r] = cell.DateCellValue;
                            else if (rowType.GetCell(j).StringCellValue == "NUMBER")
                                     dataRow[r] = cell.NumericCellValue;
                            else
                                     dataRow[r] = cell.StringCellValue;
                   }
                   else
                            dataRow[r] = DBNull.Value;
         }
         dt.Rows.Add(dataRow);
}
return dt;

OracleBulkCopy类批量导入无法得知导入受影响行数,所以在导入完成后,需再次访问数据库查询表中的数据总数作为数据导入受影响行数。

使用Stopwatch计时,代码如下:

 Stopwatch MyStopWatch = new Stopwatch();
 MyStopWatch.Start();
 //......
 MyStopWatch.Stop();
 decimal t = MyStopWatch.ElapsedTicks;
 System.Console.WriteLine(t / 10000000);

调节批尺为不同大小,运行多次耗时20s~70s,已达到网页端可接受的范围。当批尺为400时,导入所需时间为32秒。

5、分别使用release和debug方式进行编译运行。Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于调试程序。Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。但运行多次对比发现两者导入耗时相差不大。

四、研究结果总结、影响因素

多线程可以加快导入速度,但是也要注意线程数的控制。

使用OracleBulkCopy类批量导入能够非常快速的将大量数据导入数据库,调节批尺大小可以达到更好的效果。

时间: 2024-08-08 13:46:00

.net导入Oracle数据优化小记的相关文章

使用MySQL Migration Toolkit快速导入Oracle数据

近来笔者有项目需要将原有的Oracle数据库中的数据导入到MySQL中,经过试用发现MySQL GUI Tools中的MySQL Migration Toolkit可以非常方便快捷的将Oracle数据导到MySQL中,特别是对CLOB.BLOB类型的数据也有非常好的支持.下面笔者来介绍一下MySQL Migration Toolkit的使用,该软件可以在http://dev.mysql.com/downloads/gui-tools/5.0.html处下载到. 1. 运行MySQL Migrat

linux 下使用exp/imp 或者expdp/impdp导出导入oracle数据表数据

一.环境配置 1.执行环境: exp/imp可以在客户端执行也可以在服务器端执行,在客户端执行需要先安装有oracle的客户端,如果是linux系统,就是以oracle用户登录,在控制台下执行.建议在服务器端执行exp,备份速度快. 2.如果没有配置oracle的exp/imp命令的环境变量,则进入到oracle的bin目录: # cd /opt/oracle_11/app/oracle/product/11.2.0/dbhome_1/bin 3.配置临时环境变量: # export ORACL

linux下导入oracle数据表

提前说明:这个是默认oracle已经安装好切数据库默认表空间已经创建好.之后将数据表dmp文件直接导入到默认表空间里(默认表空间不用再指定,因为创建数据库时已经指定默认表空间) linux命令如下: [[email protected] ~]$ imp test/test file=/home/oracle/tm_bus_passenger_updown.dmp ignore=y full=y //用户名和密码已经暗含默认表空间 文档结构如下 原文地址:https://www.cnblogs.c

建立用户,设置权限,导入oracle数据dmp

--建立用户 create user xxx identified by xxx account unlock; --设置权限 grant dba to xxx; --导入数据dmp imp aichannel/[email protected] file= d:datanewsmgnt.dmp full=y

cmd 导入oracle数据的dmp文件

在前面已经安装好orcale,现在导入数据库并开始使用. 步骤如下: 设置表空间自动扩容 登录 用system as sysdba 登录 密码为空 查询表SYSTEM表空间的数据文件的物理路径,语句为 SELECT FILE_NAME FROM DBA_DATA_FILES WHERE (TABLESPACE_NAME = 'SYSTEM') 结果为:D:\APP\ADMINISTRATOR\ORADATA\ORCL\SYSTEM01.DBF 创建表空间 CREATE TABLESPACE 表空

kettle将Excel数据导入oracle

导读 Excel数据导入Oracle数据库的方法: 1.使用PL SQL 工具附带的功能,效率比较低 可参考这篇文章的介绍:http://www.2cto.com/database/201212/176777.html 2.使用Kettle工具,免费,相比之下功能更丰富,可实现一定的业务逻辑,推荐使用 Kettle将Excel数据导入Oracle过程记录如下: 1.准备Excel文件: 注意:Excel文件要有字段名称说明:如这里的id,name,age 2.打开Kettle,文件-->新建--

Oracle数据导入导出基本操作示例

Oracle数据导入导出基本操作示例 数据导出 a.将数据库orcl完全导出,用户名user 密码password 导出到D:\dc.dmp中 exp user/[email protected]   file=d:\dc.dmp    full=y full=y   表示全库导出 b.将数据库中user1和user2用户导出 exp user/[email protected]  file=d:\dc.dmp    owner=(user1,user2) full方式可以备份所有用户的数据库对

简析将shp导入Oracle并利用geoserver将导入的数据发布

1.环境准备 1.1 软件准备 首先要安装有支持空间数据的Oracle,其次有安装版或免安装版的geoserver,两者都部署好后,我们将开始讨论进一步需要配置的环境. 这里我使用由Oracle提供的shp2sdo来进行shp导入,同时使用由geoserver提供的可以连接Oracle数据源的插件来进行数据的发布. Shp2sdo的下载地址为:http://down.51cto.com/data/223757. Geoserver的Oracle插件下载地址为:http://sourceforge

oracle数据导入

oracle 数据导入时 imp 用户名/密码@服务名 file =文件路径.dmp fromuser=          touser= 执行命令之前应将数据库里面 myobject下的所有view 索引 存储过程 等删除掉 在执行命令 不然会报错 oracle数据导入,布布扣,bubuko.com