最近做一个SqlCE项目,需要频繁地从SqlServer2008服务器上提取数据到SqlCE数据库。
SqlServer2008中有生成数据脚本功能,可以通过脚本向导(右键点击数据库-〉任务-〉生成脚本)实现。但经常这么导数据太麻烦,一天导10几次能把你导崩溃。。。
突然想到动软代码生成器里边有生成数据脚本的功能。于是拿来参考着做了个SqlServer到SqlCE的数据转工具。
先晒一下界面:
主要功能:
1、输入数据库名和表名,生成创建SQLCE数据库的脚本和向SQKCE数据库中插入数据的脚本。
2、将脚本输出到文件。
3、直接生成SQLCE数据库。
实现方法
1、SqlServer中表结构的获取(根据动软代码生成器 )
要从创建表,首先要提取表的结构信息。以下代码组织了一个用于查询表结构的Sql语句。
- StringBuilder strSql = new StringBuilder();
- strSql.Append("SELECT ");
- strSql.Append("colorder=C.column_id,");
- strSql.Append("ColumnName=C.name,");
- strSql.Append("TypeName=T.name, ");
-
- strSql.Append("Length=CASE WHEN T.name='nchar' THEN C.max_length/2 WHEN
- T.name='nvarchar' THEN C.max_length/2 ELSE C.max_length END,");
- strSql.Append("Preci=C.precision, ");
- strSql.Append("Scale=C.scale, ");
- strSql.Append("IsIdentity=CASE WHEN C.is_identity=1 THEN N'√'ELSE N'' END,");
- strSql.Append("isPK=ISNULL(IDX.PrimaryKey,N''),");
-
- strSql.Append("Computed=CASE WHEN C.is_computed=1 THEN N'√'ELSE N'' END, ");
- strSql.Append("IndexName=ISNULL(IDX.IndexName,N''), ");
- strSql.Append("IndexSort=ISNULL(IDX.Sort,N''), ");
- strSql.Append("Create_Date=O.Create_Date, ");
- strSql.Append("Modify_Date=O.Modify_date, ");
-
- strSql.Append("cisNull=CASE WHEN C.is_nullable=1 THEN N'√'ELSE N'' END, ");
- strSql.Append("defaultVal=ISNULL(D.definition,N''), ");
- strSql.Append("deText=ISNULL(PFD.[value],N'') ");
-
- strSql.Append("FROM sys.columns C ");
- strSql.Append("INNER JOIN sys.objects O ");
- strSql.Append("ON C.[object_id]=O.[object_id] ");
- strSql.Append("AND (O.type='U' or O.type='V') ");
- strSql.Append("AND O.is_ms_shipped=0 ");
- strSql.Append("INNER JOIN sys.types T ");
- strSql.Append("ON C.user_type_id=T.user_type_id ");
- strSql.Append("LEFT JOIN sys.default_constraints D ");
- strSql.Append("ON C.[object_id]=D.parent_object_id ");
- strSql.Append("AND C.column_id=D.parent_column_id ");
- strSql.Append("AND C.default_object_id=D.[object_id] ");
- strSql.Append("LEFT JOIN sys.extended_properties PFD ");
- strSql.Append("ON PFD.class=1 ");
- strSql.Append("AND C.[object_id]=PFD.major_id ");
- strSql.Append("AND C.column_id=PFD.minor_id ");
-
- strSql.Append("LEFT JOIN sys.extended_properties PTB ");
- strSql.Append("ON PTB.class=1 ");
- strSql.Append("AND PTB.minor_id=0 ");
- strSql.Append("AND C.[object_id]=PTB.major_id ");
-
- strSql.Append("LEFT JOIN ");
- strSql.Append("( ");
- strSql.Append("SELECT ");
- strSql.Append("IDXC.[object_id], ");
- strSql.Append("IDXC.column_id, ");
- strSql.Append("Sort=CASE INDEXKEY_PROPERTY(IDXC.[object_id],
- IDXC.index_id,IDXC.index_column_id,'IsDescending') ");
- strSql.Append("WHEN 1 THEN 'DESC' WHEN 0 THEN 'ASC' ELSE '' END, ");
- strSql.Append("PrimaryKey=CASE WHEN IDX.is_primary_key=1
- THEN N'√'ELSE N'' END, ");
- strSql.Append("IndexName=IDX.Name ");
- strSql.Append("FROM sys.indexes IDX ");
- strSql.Append("INNER JOIN sys.index_columns IDXC ");
- strSql.Append("ON IDX.[object_id]=IDXC.[object_id] ");
- strSql.Append("AND IDX.index_id=IDXC.index_id ");
- strSql.Append("LEFT JOIN sys.key_constraints KC ");
- strSql.Append("ON IDX.[object_id]=KC.[parent_object_id] ");
- strSql.Append("AND IDX.index_id=KC.unique_index_id ");
- strSql.Append("INNER JOIN ");
-
- strSql.Append("( ");
- strSql.Append("SELECT [object_id], Column_id, index_id=MIN(index_id) ");
- strSql.Append("FROM sys.index_columns ");
- strSql.Append("GROUP BY [object_id], Column_id ");
- strSql.Append(") IDXCUQ ");
- strSql.Append("ON IDXC.[object_id]=IDXCUQ.[object_id] ");
- strSql.Append("AND IDXC.Column_id=IDXCUQ.Column_id ");
- strSql.Append("AND IDXC.index_id=IDXCUQ.index_id ");
- strSql.Append(") IDX ");
- strSql.Append("ON C.[object_id]=IDX.[object_id] ");
- strSql.Append("AND C.column_id=IDX.column_id ");
- strSql.Append("WHERE O.name=N'" + TableName + "' ");
- strSql.Append("ORDER BY O.name,C.column_id ");
TableName为用户输入的表名称。查询结果是一个包含了表详细信息的DataTable(通过ADO.NET的SqlDataAdapter将数据Fill到DataSet中)。
有了这些信息我们就可以生成创建表的语句了。(注:这些语句不适合SqlServer2000。具体这些语句是什么意思,问老李吧。。我也看不是很明白。)
2、自定义一个SqlServer中数据类型到SQLCE数据类型的映射。
SqlCE只支持18种数据类型,比SqlServer少的多。因此如果原数据库中有SqlCE不支持的数据类型,需要手动将数据类型映射为SqlCE数据类型。具体怎样映射,应可以根据需要手动设置。
还有一些用户自定义的数据类型,在SqlCE中是不可以使用的。也需要映射。下面这个类完成数据的映射功能。
首先编辑一个数据映射表(TypeMap.txt),保存一些如下面格式的数据:
bigint=bigint;
binary=binary;
char=nchar;
date=datetime;
decimal=float;
nvarchar=nvarchar;
varchar=nvarchar;
real=real;
smalldatetime=datetime;
smallint=smallint;
smallmoney=money;
text=ntext;
tinyint=tinyint;
uniqueidentifier=uniqueidentifier;
varbinary=varbinary;
NGUD=uniqueidentifier
前面是SqlServer中的数据类型,后边是想要映射到SqlCE中的数据类型,中间一分号隔开。这个映射可以根据需要手动调整。
下边类中的GetMapedDataType()函数返回映射后的数据类型。
- public class TypeMap
- {
-
- private static string datafile =
- System.Windows.Forms.Application.StartupPath + "\\TypeMap.txt";
-
-
- private static string[] LoadTypeMap()
- {
- string s = string.Empty;
- if (File.Exists(datafile))
- {
- StreamReader sr = new StreamReader(datafile, Encoding.UTF8);
-
- s = sr.ReadToEnd();
- sr.Close();
- }
-
- s= s.Replace("\r\n", "");
- return s.Split(';');
- }
-
-
-
-
-
-
- public static string GetMapedDataType(string sourceType)
- {
- string[] ss = TypeMap.LoadTypeMap();
-
- foreach (string s in ss)
- {
- string[] sss = s.Split('=');
- if (sss[0].ToLower() == sourceType.ToLower())
- {
-
- return sss[1];
- }
- }
-
- return sourceType;
- }
- }
3、生成创建表的语句(create table….)
- StringBuilder strclass = new StringBuilder();
-
-
-
-
-
-
- StringBuilder ColdefaVal = new StringBuilder();
-
- Hashtable FildtabList = new Hashtable();
- StringBuilder FildList = new StringBuilder();
-
- DataTable dt = (1中语句查询后得到的表);
-
-
-
- strclass.AppendLine("CREATE TABLE [" + tablename + "] (");
-
- if (dt != null)
- {
- DataRow[] dtrows;
- dtrows = dt.Select();
-
-
-
-
-
-
-
- foreach (DataRow row in dtrows)
- {
- string columnName = row["ColumnName"].ToString();
- string columnType = TypeMap.GetMapedDataType(row["TypeName"].ToString());
-
-
- string IsIdentity = row["IsIdentity"].ToString();
- string Length = row["Length"].ToString();
- string Preci = row["Preci"].ToString();
- string Scale = row["Scale"].ToString();
- string ispk = row["isPK"].ToString();
- string isnull = row["cisNull"].ToString();
- string defaultVal = row["defaultVal"].ToString();
-
- strclass.Append("[" + columnName + "] [" + columnType + "] ");
-
- switch (columnType.Trim())
- {
- case "varchar":
- case "char":
- case "nchar":
- case "binary":
- case "nvarchar":
- case "varbinary":
- {
- string len = this.GetDataTypeLenVal(columnType.Trim(), Length);
-
- strclass.Append(" (" + len + ")");
- }
- break;
- case "decimal":
- case "numeric":
- strclass.Append(" (" + Preci + "," + Scale + ")");
- break;
- }
- if (isnull == "√")
- {
- strclass.Append(" NULL");
- }
- else
- {
- strclass.Append(" NOT NULL");
- }
- if (defaultVal != "")
- {
- strclass.Append(" DEFAULT " + defaultVal);
- }
- strclass.AppendLine(",");
-
- FildtabList.Add(columnName, columnType);
- FildList.Append("[" + columnName + "],");
-
-
-
-
-
-
-
-
-
-
- }
- }
- string s = strclass.ToString();
-
-
- int n = s.LastIndexOf(',');
- string ss = s.Substring(0, n) + ")";
- strclass.Remove(0, strclass.ToString().Length);
- strclass.Append(ss);
- strclass.Append(";");
- strclass.AppendLine("");
- strclass.AppendLine("");
-
-
- s = FildList.ToString();
- n = s.LastIndexOf(',');
- ss = s.Substring(0, n);
- FildList.Remove(0, FildList.ToString().Length);
- FildList.Append(ss);
-
-
- DataTable dtdata = this.getData(dbname,tablename);
-
- if (dtdata != null)
- {
- foreach (DataRow row in dtdata.Rows)
- {
- StringBuilder strfild = new StringBuilder();
- StringBuilder strdata = new StringBuilder();
-
- string[] split = FildList.ToString().Split(new Char[] { ',' });
-
- foreach (string fild in split)
- {
- string colname = fild.Substring(1, fild.Length - 2);
- string coltype = "";
- foreach (DictionaryEntry myDE in FildtabList)
- {
- if (myDE.Key.ToString() == colname)
- {
- coltype = myDE.Value.ToString();
- }
- }
- string strval = "";
- switch (coltype)
- {
- case "binary":
- {
-
- }
- break;
- case "bit":
- {
- strval = (row[colname].ToString().ToLower() == "true") ? "1" : "0";
- }
- break;
- default:
- strval = row[colname].ToString().Trim().Replace(';',';');
- break;
- }
- strdata.Append("'" + strval.Replace('\'', '°') + "',");
- strfild.Append("[" + colname.Replace('\'', '°') + "],");
-
- }
-
- n = strdata.ToString().LastIndexOf(',');
- ss = strdata.ToString().Substring(0, n);
- strdata.Remove(0,strdata.ToString().Length);
- strdata.Append(ss);
-
- n = strfild.ToString().LastIndexOf(',');
- ss = strfild.ToString().Substring(0, n);
- strfild.Remove(0, strfild.ToString().Length);
- strfild.Append(ss);
-
-
-
- strclass.Append("INSERT [" + tablename + "] (");
- strclass.Append(strfild.ToString());
- strclass.Append(") VALUES ( ");
- strclass.Append(strdata.ToString().Replace(';',';');
-
- strclass.AppendLine(");");
-
- }
- }
-
-
- n = strclass.ToString().LastIndexOf(';');
- ss = strclass.ToString().Substring(0,n);
- strclass.Remove(0, strclass.ToString().Length);
- strclass.Append(ss);
-
- return strclass.ToString();
- }
注:1、这里使用分号将每条语句隔开,是为了使用的方便。因为SqlCE不能一次执行多条语句,向表中插入数据只能一条条地循环。用分号分割后方便以后的使用。
2、GetDataTypeLenVal函数来自动软代码生成器。没必要列出
4、使用脚本生成SqlCE数据库。
代码如下,说明很详细就不啰嗦了
首先添加引用System.Data.SqlServerCe的引用:
之后再代码中添加对SqlServerCE的引用:
using System.Data.SqlServerCe;
开始:
- public class SDF
- {
- string fileName = "C:\\123.sdf";
- public SDF(string file)
- {
- fileName = file;
- }
-
-
-
-
- private void CreateDB(string file)
- {
- fileName = file;
-
- string connStr = "Data Source=" + file;
- try
- {
- if (File.Exists(file))
- {
- File.Delete(file);
- }
-
- SqlCeEngine eng = new SqlCeEngine();
- eng.LocalConnectionString = connStr;
- eng.CreateDatabase();
- }
- catch (Exception exp)
- {
- throw exp;
- }
- }
-
-
- private void CreateTab(string ScriptStr)
- {
- string connStr = "Data Source=" + fileName;
- string[] ss = ScriptStr.Split(';');
- using (SqlCeConnection conn = new SqlCeConnection(connStr))
- {
-
- SqlCeCommand cmd = new SqlCeCommand();
- cmd.Connection = conn;
- conn.Open();
- foreach (string sqlstr in ss)
- {
- cmd.CommandText = sqlstr;
- cmd.ExecuteNonQuery();
- }
- conn.Close();
- }
- }
-
-
- public void Create(string File, string Script)
- {
- this.CreateDB(File);
- this.CreateTab(Script);
- }
- }
在程序中调用方法
- if (this.saveToFile.ShowDialog() == DialogResult.OK)
- {
- SDF sdf = new SDF("");
- string filaname = saveToFile.FileName;
-
- sdf.Create(filaname, this.GetCode());
- }
5、字段类型映射的设置方法:
在下边界面中输入映射类型,前边是原始字段类型后边是SqlCE支持的字段类型
6、总结
分析表的结构,然后组织创建表语句-〉查询数据,组织插入语句。读取文件中字符串映射,替换原来映射。
为了省事,没有设置主键;对所有字段都进行操作,不管是不是空。
虽然效率不高,比手工快多了。。呵呵
测试中,对九千多条数据2-3秒就可以完事。数据量太大就不行了。
60万条数据能让我的几近崩溃。。。原因很简单:string字符串太长,而且全部保存在内存中。可是谁家PDA中方60万条数据呢?暂时不动了。。。
不会插附件,本来想把程序包也放上去。。。。
最后感谢李天平老师的无私奉献。。。
(hanshuyujifen) |