今天从oracle10g将包含clob字段的数据导入oracle 9i中,在网上搜到的方法是说从9i的客户端导出的bmp文件可以直接导入9i的数据库,但试了N次都没成功,最后只好自己动手写代码导,现将主要思路公布:
思路:
先生成建表的语句,然后自己写代码来实现生成insert语句和执行insert语句的功能。
具体步骤如下:
一、进入pl/sql,将高版本的数据库的表导出(因为表中包含了clob类型的字段,所以用pl/sql导出insert语句的时候没法导出)
二、写代码生成insert语句
首先用select table_name from user_tables语句查询表空间中的所有用户表的表名
SqlLib DoSql = new SqlLib();//SqlLib类自己实现吧,其实就是一个数据库操作类,其中的DoExecuteSqlString方法用来执行sql语句并返回datatable类型的数据
DataTable tnames = DoSql.DoExecuteSqlString("select table_name,num_rows from user_tables");
接下来循环生成所有表的insert语句
1)查询指定表的所有字段以及字段的数据类型
2)查询表的所有数据
for (int i = 0; i < tnames.Rows.Count; i++)//循环所有表
{
//查询指定表的所有字段以及字段的数据类型
DataTable tclos = DoSql.DoExecuteSqlString("select t.table_name,t.column_name,t.data_type,c.COMMENTS from user_tab_columns t,user_col_comments c where t.table_name = c.table_name and t.column_name = c.column_name and t.table_name = '" + SqlLib.GetString(tnames.Rows[i]["table_name"]) + "'");
//查询该表的所有数据
DataTable dbList = DoSql.DoExecuteSqlString("select * from " + SqlLib.GetString(tnames.Rows[i]["table_name"]));
//这里生成insert语句
}
3)生成inert语句
StringBuilder sb = new StringBuilder();
for (int db = 0; db < dbList.Rows.Count; db++)
{
sb.Append("insert into " + SqlLib.GetString(tnames.Rows[i]["table_name"]) + "(");
for (int c = 0; c < tclos.Rows.Count; c++)
{
if (c > 0)
sb.Append("," + SqlLib.GetString(tclos.Rows[c]["column_name"]));
else
sb.Append(SqlLib.GetString(tclos.Rows[c]["column_name"]));
}
sb.Append(")values(");
for (int c = 0; c < tclos.Rows.Count; c++)
{
string dbtype = SqlLib.GetString(tclos.Rows[c]["data_type"]).ToUpper();
if (c > 0) sb.Append( ",");
switch (dbtype)
{
case "NUMBER":
sb.Append(SqlLib.GetString(dbList.Rows[db][SqlLib.GetString(tclos.Rows[c]["column_name"])]));
break;
case "DATE":
sb.Append("'" + SqlLib.GetString(dbList.Rows[db][SqlLib.GetString(tclos.Rows[c]["column_name"])]) + "'");
break;
case "CLOB":
sb.Append("$$$_____$$$【@" + SqlLib.GetString(tclos.Rows[c]["column_name"]) + "@C@'" + SqlLib.GetString(dbList.Rows[db][SqlLib.GetString(tclos.Rows[c]["column_name"])]) + "'】$$$_____$$$");
break;
default:
sb.Append("$$$_____$$$【@" + SqlLib.GetString(tclos.Rows[c]["column_name"]) + "@V@'" + SqlLib.GetString(dbList.Rows[db][SqlLib.GetString(tclos.Rows[c]["column_name"])]) + "'】$$$_____$$$");
break;
}
}
sb.Append(");\n----------------------------------------\n\r\n\r\n\r");
}
Utils.WriteText(Utils.GetMapPath("/test.sql"), sb.ToString());//将生成的insert语句写入文本,这里的Utils.WriteText方法即为将制定内容写入文本文件的代码,具体代码请实现
其中的“$$$_____$$$【@字段名@数据类型@'数据内容'】$$$_____$$$"是为了让导入程序提取字段名、数据类型、数据内容而设置的特殊符号
”;\n----------------------------------------\n\r\n\r\n\r“是为了让导入程序识别单一的一条insert语句的结尾
三、写一个导入数据的程序
步骤二说明了如何生成insert语句,这一步说明如何识别步骤二生成的insert语句并导入数据库
1)读取步骤二生成的test.sql文件
string html = Utils.LoadFile(Utils.GetMapPath("/test.sql")).Trim();//读取test.sql,Utils.LoadFile方法请实现
string[] arr = Utils.SplitString(html, ";\n----------------------------------------\n\r\n\r\n\r");//每条insert语句作为数组的一个元素
下面贴出 Utils.SplitString方法的定义
public static string[] SplitString(string strContent, string strSplit)
{
if (strContent.IndexOf(strSplit) < 0)
{
string[] tmp = { strContent };
return tmp;
}
if (strContent == null || strContent == "")
{
string[] tmp = { strContent };
return tmp;
}
RegexOptions options;
if (Environment.Version.Major == 1)
options = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline;
else
options = RegexOptions.IgnoreCase | RegexOptions.Singleline;
return Regex.Split(strContent, @strSplit, options);
}
2)开始拼接sql语句
List<string> sqlList = new List<string>();//sql语句
Regex r = null;Match m = null;
string rpt = @"\$\$\$_____\$\$\$【@([^@]+)@([^@]+)@'([\s\S]*?)'】\$\$\$_____\$\$\$";//用正则式来识别字段名、数据类型、数据内容
RegexOptions options;
if (Environment.Version.Major == 1)
options = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline;
else
options = RegexOptions.IgnoreCase | RegexOptions.Singleline;
//开始拼接
for (int i = 0; i < arr.Length; i++)
{
因为Utils.LoadFile(Utils.GetMapPath("/test.sql")).Trim();将最后的\n\r\n\r\n\r去掉了,所以下面的一句是为了去掉最后面的;\n----------------------------------------
string val = arr[i].Replace(";\n----------------------------------------", "").Trim();
ArrayList spArray = new ArrayList();//用来存储每条sql语句的OracleParameter参数
r = new Regex(rpt, options);
for (m = r.Match(arr[i]); m.Success; m = m.NextMatch())
{
string __type = m.Groups[2].ToString();//数据类型
string __tem = m.Groups[3].ToString();//数据内容
if (__type == "C")
{//C表示CLOB
if (__tem == "")
__tem = " ";//因为CLOB无法插入空字串,因此插入一个html的空格
//DoSql.CreateParameter方法是创建一个OracleParameter对象,具体代码请实现
spArray.Add(DoSql.CreateParameter(":" + m.Groups[1].ToString(), OracleType.Clob, __tem));
}
else if (__type == "V")
{//V表示VARCHAR类型
spArray.Add(DoSql.CreateParameter(":" + m.Groups[1].ToString(), OracleType.VarChar, __tem));
}
}
sqlList.Add(Regex.Replace(val, rpt, ":$1"));
spList.Add(spArray);
}
程序执行到这里就已经生成了可以执行的sql语句
其中sqlList是一个泛型,泛型的每个元素为一条sql语句
spList是一个ArrayList 类型(ArrayList 的单个元素依然是ArrayList 类型),其中的每个元素对应泛型中某条sql语句的OracleParameter参数
编写一个执行事务的方法去执行这些sql语句即可实现导入功能
if (DoSql.DoTransaction(sqlList, spList) > 0)
{
base.Alert("数据导入成功 ^_^", "default.aspx");
}else{
base.Alert("系统错误,无法完成导入", "default.aspx");
}
#region 执行事务
public int DoTransaction(List<string> sqlString, List<ArrayList> spList)
{
int result = 0;
this.CreateConn();//CreateConn方法请实现,下面的代码并不能直接运行,仅供参考
OracleTransaction st = this.conn.BeginTransaction(IsolationLevel.Serializable);
try
{
for (int i = 0; i < sqlString.Count; i++)
{
this.comm = new OracleCommand(sqlString[i], this.conn, st);
this.comm.CommandType = CommandType.Text;
if (spList != null && spList.Count > i)
{
OracleParameter[] oparams = (OracleParameter[])spList[i].ToArray(typeof(OracleParameter));
for (int k = 0; k < oparams.Length; k++)
{
this.comm.Parameters.Add(oparams[k]);
}
}
result = this.comm.ExecuteNonQuery();
this.comm.Dispose();
}
//result = Convert.ToInt32(this.comm.ExecuteScalar());
st.Commit();
}
catch (Exception)
{
result = 0;
st.Rollback();
}
finally
{
st.Dispose();
}
this.Close();
return result;
}
public int DoTransaction(List<string> sqlString)
{
return DoTransaction(sqlString, null);
}
#endregion