我目前遇到的问题是客户端会上传一个excel文档,文档里包含了要导入到db中的数据,我是直接把excel中的数据读取到dataset中,然后循环遍历table将数据一行一行的插入,我发现性能非常的差,我用的数据库是oracle,我想咨询一下如何高效的导入大量数据,比如plsql中有直接将excel作为数据源通过odbc导入到db中,要是可以通过在页面文件中调用plsql的那些功能性能应该会提升很多,只是不知道从什么地方着手,我页面所使用的语言是asp.net,大侠指点啊!!
解决方案 »
- 这样的SQL 还能优化吗?
- 在线等~十万火急
- 游标使用select 的参数问题
- 菜鸟问题,送分啦。toad可以连接oracle数据库,但是在cmd下无法使用“sqlplus”
- 换了机器名,oms中无法看到数据库节点了,怎么办?
- exp的错误,这是什么原因
- 新手,望高手指教!oracle中怎么存储图片,音频以及视频等,再有能否直接存储ppt,doc等格式的文件!
- 一个简单的触发器问题,请大家帮忙。
- 提供一个好的oracle数据库备份工具好吗(除了imp80\exp80)?
- [Err] ORA-01704: 字符串文字太长 求助
- oracle 游标是否可以与另一个表做交叉????
- 一道及其变态的Oracle期末考试题,唯有高手能解答呀!!!
那就是使用oracle的sql*loader功能,而sqlldr只支持类似csv格式的数据,所以要自己把excel转换一下。
实现步骤:
用com组件读取excel-保存为csv格式-处理最后一个字段为null的情况和表头-根据excel结构建表-生成sqlldr的控制文件-用sqlldr命令导入数据
这个性能虽然没有sql的bcp快,但还是相当可观的,在我机器上1万多数据不到4秒,而且导入过程代码比较简单,也同样没有循环拼接sql插入那么难以维护。这里也提个问题:处理csv文件的表头和最后一个字段为null的情况是否可以优化?除了我代码中的例子,我实在想不出其他办法。using System;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Microsoft.Office.Interop.Excel;
using System.Data.OleDb;
//引用-com-microsoft excel objects 11.0
namespace WindowsApplication5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
///
/// excel导入到oracle
///
/// 文件名
/// sheet名
/// oracle命令sqlplus连接串
public void TransferData(string excelFile, string sheetName, string sqlplusString)
{
string strTempDir = System.IO.Path.GetDirectoryName(excelFile);
string strFileName = System.IO.Path.GetFileNameWithoutExtension(excelFile);
string strCsvPath = strTempDir +"\\"+strFileName + ".csv";
string strCtlPath = strTempDir + "\\" + strFileName + ".Ctl";
string strSqlPath = strTempDir + "\\" + strFileName + ".Sql";
if (System.IO.File.Exists(strCsvPath))
System.IO.File.Delete(strCsvPath); //获取excel对象
Microsoft.Office.Interop.Excel.Application ObjExcel = new Microsoft.Office.Interop.Excel.Application(); Microsoft.Office.Interop.Excel.Workbook ObjWorkBook; Microsoft.Office.Interop.Excel.Worksheet ObjWorkSheet = null; ObjWorkBook = ObjExcel.Workbooks.Open(excelFile, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); foreach (Microsoft.Office.Interop.Excel.Worksheet sheet in ObjWorkBook.Sheets)
{
if (sheet.Name.ToLower() == sheetName.ToLower())
{
ObjWorkSheet = sheet;
break;
}
}
if (ObjWorkSheet == null) throw new Exception(string.Format("{0} not found!!", sheetName)); //保存为csv临时文件
ObjWorkSheet.SaveAs(strCsvPath, Microsoft.Office.Interop.Excel.XlFileFormat.xlCSV, Type.Missing, Type.Missing, false, false, false, Type.Missing, Type.Missing, false);
ObjWorkBook.Close(false, Type.Missing, Type.Missing);
ObjExcel.Quit(); //读取csv文件,需要将表头去掉,并且将最后一列为null的字段处理为显示的null,否则oracle不会识别,这个步骤有没有好的替换方法?
System.IO.StreamReader reader = new System.IO.StreamReader(strCsvPath,Encoding.GetEncoding("gb2312"));
string strAll = reader.ReadToEnd();
reader.Close();
string strData = strAll.Substring(strAll.IndexOf("\r\n") + 2).Replace(",\r\n",",Null"); byte[] bytes = System.Text.Encoding.Default.GetBytes(strData);
System.IO.Stream ms = System.IO.File.Create(strCsvPath);
ms.Write(bytes, 0, bytes.Length);
ms.Close(); //获取excel表结构
string strConn = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + excelFile + ";" + "Extended Properties=Excel 8.0;";
OleDbConnection conn = new OleDbConnection(strConn);
conn.Open();
System.Data.DataTable table = conn.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Columns,
new object[] { null, null, sheetName+"$", null }); //生成sqlldr用到的控制文件,文件结构参考sql*loader功能,本示例已逗号分隔csv,数据带逗号的用引号括起来。
string strControl = "load data\r\ninfile '{0}' \r\nappend into table {1}\r\n"+
"FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"'\r\n(";
strControl = string.Format(strControl, strCsvPath,sheetName);
foreach (System.Data.DataRow drowColumns in table.Select("1=1", "Ordinal_Position"))
{
strControl += drowColumns["Column_Name"].ToString() + ",";
} strControl = strControl.Substring(0, strControl.Length - 1) + ")";
bytes=System.Text.Encoding.Default.GetBytes(strControl);
ms= System.IO.File.Create(strCtlPath); ms.Write(bytes, 0, bytes.Length);
ms.Close(); //生成初始化oracle表结构的文件
string strSql = @"drop table {0};
create table {0}
(";
strSql = string.Format(strSql, sheetName);
foreach (System.Data.DataRow drowColumns in table.Select("1=1", "Ordinal_Position"))
{
strSql += drowColumns["Column_Name"].ToString() + " varchar2(255),";
}
strSql = strSql.Substring(0, strSql.Length - 1) + ");\r\nexit;";
bytes = System.Text.Encoding.Default.GetBytes(strSql);
ms = System.IO.File.Create(strSqlPath); ms.Write(bytes, 0, bytes.Length);
ms.Close(); //运行sqlplus,初始化表
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo = new System.Diagnostics.ProcessStartInfo();
p.StartInfo.FileName = "sqlplus";
p.StartInfo.Arguments = string.Format("{0} @{1}", sqlplusString, strSqlPath);
p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.Start();
p.WaitForExit(); //运行sqlldr,导入数据
p = new System.Diagnostics.Process();
p.StartInfo = new System.Diagnostics.ProcessStartInfo();
p.StartInfo.FileName = "sqlldr";
p.StartInfo.Arguments = string.Format("{0} {1}", sqlplusString, strCtlPath);
p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.Start();
System.IO.StreamReader r = p.StandardOutput;//截取输出流
string line = r.ReadLine();//每次读取一行
textBox3.Text += line + "\r\n";
while (!r.EndOfStream)
{
line = r.ReadLine();
textBox3.Text += line + "\r\n";
textBox3.Update();
}
p.WaitForExit(); //可以自行解决掉临时文件csv,ctl和sql,代码略去
} private void button1_Click(object sender, EventArgs e)
{
TransferData(@"D:\test.xls", "Sheet1", "username/password@servicename");
}
}
}
2. 如果没有,那么通过正常的方式插入Oracle的速度,在一般情况下,也就是1k~3k数量的记录,超过4k就算很好了。在网络上可能就更慢一点了。
3. 如果没有,估计最好的办法是写一个存储过程,然后将数据批量性的喂给存储过程,不过要达到1w还是很困难的。试试看吧。
using System;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Microsoft.Office.Interop.Excel;
using System.Data.OleDb;
//引用-com-microsoft excel objects 11.0
namespace WindowsApplication5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
} ///
/// excel导入到oracle
///
/// 文件名
/// sheet名
/// oracle命令sqlplus连接串
public void TransferData(string excelFile, string sheetName, string sqlplusString)
{
string strTempDir = System.IO.Path.GetDirectoryName(excelFile);
string strFileName = System.IO.Path.GetFileNameWithoutExtension(excelFile);
string strCsvPath = strTempDir +"\\"+strFileName + ".csv";
string strCtlPath = strTempDir + "\\" + strFileName + ".Ctl";
string strSqlPath = strTempDir + "\\" + strFileName + ".Sql";
if (System.IO.File.Exists(strCsvPath))
System.IO.File.Delete(strCsvPath); //获取excel对象
Microsoft.Office.Interop.Excel.Application ObjExcel = new Microsoft.Office.Interop.Excel.Application(); Microsoft.Office.Interop.Excel.Workbook ObjWorkBook; Microsoft.Office.Interop.Excel.Worksheet ObjWorkSheet = null; ObjWorkBook = ObjExcel.Workbooks.Open(excelFile, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); foreach (Microsoft.Office.Interop.Excel.Worksheet sheet in ObjWorkBook.Sheets)
{
if (sheet.Name.ToLower() == sheetName.ToLower())
{
ObjWorkSheet = sheet;
break;
}
}
if (ObjWorkSheet == null) throw new Exception(string.Format("{0} not found!!", sheetName)); //保存为csv临时文件
ObjWorkSheet.SaveAs(strCsvPath, Microsoft.Office.Interop.Excel.XlFileFormat.xlCSV, Type.Missing, Type.Missing, false, false, false, Type.Missing, Type.Missing, false);
ObjWorkBook.Close(false, Type.Missing, Type.Missing);
ObjExcel.Quit(); //读取csv文件,需要将表头去掉,并且将最后一列为null的字段处理为显示的null,否则oracle不会识别,这个步骤有没有好的替换方法?
System.IO.StreamReader reader = new System.IO.StreamReader(strCsvPath,Encoding.GetEncoding("gb2312"));
string strAll = reader.ReadToEnd();
reader.Close();
string strData = strAll.Substring(strAll.IndexOf("\r\n") + 2).Replace(",\r\n",",Null"); byte[] bytes = System.Text.Encoding.Default.GetBytes(strData);
System.IO.Stream ms = System.IO.File.Create(strCsvPath);
ms.Write(bytes, 0, bytes.Length);
ms.Close(); //获取excel表结构
string strConn = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + excelFile + ";" + "Extended Properties=Excel 8.0;";
OleDbConnection conn = new OleDbConnection(strConn);
conn.Open();
System.Data.DataTable table = conn.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Columns,
new object[] { null, null, sheetName+"$", null }); //生成sqlldr用到的控制文件,文件结构参考sql*loader功能,本示例已逗号分隔csv,数据带逗号的用引号括起来。
string strControl = "load data\r\ninfile '{0}' \r\nappend into table {1}\r\n"+
"FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"'\r\n(";
strControl = string.Format(strControl, strCsvPath,sheetName);
foreach (System.Data.DataRow drowColumns in table.Select("1=1", "Ordinal_Position"))
{
strControl += drowColumns["Column_Name"].ToString() + ",";
} strControl = strControl.Substring(0, strControl.Length - 1) + ")";
bytes=System.Text.Encoding.Default.GetBytes(strControl);
ms= System.IO.File.Create(strCtlPath); ms.Write(bytes, 0, bytes.Length);
ms.Close(); //生成初始化oracle表结构的文件
string strSql = @"drop table {0};
create table {0}
(";
strSql = string.Format(strSql, sheetName);
foreach (System.Data.DataRow drowColumns in table.Select("1=1", "Ordinal_Position"))
{
strSql += drowColumns["Column_Name"].ToString() + " varchar2(255),";
}
strSql = strSql.Substring(0, strSql.Length - 1) + ");\r\nexit;";
bytes = System.Text.Encoding.Default.GetBytes(strSql);
ms = System.IO.File.Create(strSqlPath); ms.Write(bytes, 0, bytes.Length);
ms.Close(); //运行sqlplus,初始化表
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo = new System.Diagnostics.ProcessStartInfo();
p.StartInfo.FileName = "sqlplus";
p.StartInfo.Arguments = string.Format("{0} @{1}", sqlplusString, strSqlPath);
p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.Start();
p.WaitForExit(); //运行sqlldr,导入数据
p = new System.Diagnostics.Process();
p.StartInfo = new System.Diagnostics.ProcessStartInfo();
p.StartInfo.FileName = "sqlldr";
p.StartInfo.Arguments = string.Format("{0} {1}", sqlplusString, strCtlPath);
p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.Start();
System.IO.StreamReader r = p.StandardOutput;//截取输出流
string line = r.ReadLine();//每次读取一行
textBox3.Text += line + "\r\n";
while (!r.EndOfStream)
{
line = r.ReadLine();
textBox3.Text += line + "\r\n";
textBox3.Update();
}
p.WaitForExit(); //可以自行解决掉临时文件csv,ctl和sql,代码略去
} private void button1_Click(object sender, EventArgs e)
{
TransferData(@"D:\test.xls", "Sheet1", "username/password@servicename");
}
}
}