SQLServerToSQLite 程序代码的分析翻译及学习(一、SQLServer数据
? ? ? ? ?本人是个小菜,转行当码农有一小段时间了,工作一段时间后,感觉提高水平的最好方法还是阅读大量的优质代码,以下是我对以色列大牛名字叫LironLevi写的开源程序SQLServerToSQLiteDBConverter进行的代码分析,相信各位大牛牛们已经看过了源代码,也有许多跟我一样只是用过但没仔细读过的菜菜们,闲言少叙了,步入正题了:? ? ? ? 读一段代码,先要弄清楚开发者的意图,Liron Levi为何要写这个程序,很简单,给懒得学SQLite的人预备:直接设计出SQLServer版本的库,一转换完事了,或者为方便转移和管理从母库中提取的部分数据。其二,还要知晓程序的依赖,即编写该程序除了相应编程语言的语法外,还需哪些技术,这个程序很明显需要了解SQLserver和SQLite的数据库的语法区别。其三,则是众所周知的面向对象化了,Liron Levi把数据库转换相关信息进行了实体对象化分类如下:
/// 数据库字段类(描述数据库字段) public class ColumnSchema { public string ColumnName; public string ColumnType; public int Length; public bool IsNullable; public string DefaultValue; public bool IsIdentity; public bool? IsCaseSensitivite = null; } /// 索引描述 public class IndexSchema { public string IndexName; public bool IsUnique; public List<IndexColumn> Columns; } /// 索引字段描述 public class IndexColumn { public string ColumnName; public bool IsAscending; } /// 外键描述 public class ForeignKeySchema { public string TableName;public string ColumnName; public string ForeignTableName; public string ForeignColumnName; public bool CascadeOnDelete; public bool IsNullable; } /// 数据表描述 public class TableSchema { public string TableName; public string TableSchemaName; public List<ColumnSchema> Columns; public List<string> PrimaryKey;public List<ForeignKeySchema> ForeignKeys; public List<IndexSchema> Indexes; } /// 视图描述 public class ViewSchema { /// 视图的名称 public string ViewName; /// 创建视图的声明语句 public string ViewSQL; } /// 触发事件枚举 public enum TriggerEvent { Delete,Update,Insert } /// 触发类型 public enum TriggerType { After,Before } /// 触发描述 public class TriggerSchema { public string Name; public TriggerEvent Event; public TriggerType Type; public string Body; public string Table; } /// 数据库描述 public class DatabaseSchema { public List<TableSchema> Tables = new List<TableSchema>(); public List<ViewSchema> Views = new List<ViewSchema>(); }实体类抽象出来了,剩下的就是思路了,思路很简单四个基本步骤: 一、从内存中读取SQLServer数据库创建数据库描述对象 下面对各个步骤进行实体方法的分析: 一、从内存中读取SQLServer数据库创建数据库描述对象 1、提取SqlServer的数据库名称:select distinct [name] from sysdatabases 2、从内存结构中读取SQLServer数据库的详细信息创建数据库描述对象:DatabaseSchema
SqlCommand cmd = new SqlCommand(@"select * from INFORMATION_SCHEMA.TABLES where TABLE_TYPE = 'BASE TABLE'",conn);2.1创建SQLServer数据表对象: 2.1.1创建字段对象: TableSchema res = new TableSchema(); res.TableName = tableName; res.TableSchemaName = tschma; res.Columns = new List<ColumnSchema>(); SqlCommand cmd = new SqlCommand(@"SELECT COLUMN_NAME,COLUMN_DEFAULT,IS_NULLABLE,DATA_TYPE," + @" (columnproperty(object_id(TABLE_NAME),COLUMN_NAME,'IsIdentity')) AS [IDENT]," + @"CHARACTER_MAXIMUM_LENGTH AS CSIZE " + "FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" + tableName + "' ORDER BY " + "ORDINAL_POSITION ASC",conn); using (SqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { object tmp = reader["COLUMN_NAME"]; if (tmp is DBNull) continue; string colName = (string)reader["COLUMN_NAME"]; tmp = reader["COLUMN_DEFAULT"]; string colDefault; if (tmp is DBNull) colDefault = string.Empty; else colDefault = (string)tmp; tmp = reader["IS_NULLABLE"]; bool isNullable = ((string)tmp == "YES"); string dataType = (string)reader["DATA_TYPE"]; bool isIdentity = false; if (reader["IDENT"] != DBNull.Value) isIdentity = ((int)reader["IDENT"]) == 1 ? true : false; int length = reader["CSIZE"] != DBNull.Value ? Convert.ToInt32(reader["CSIZE"]) : 0; ValidateDataType(dataType); //请注意,并不是所有的数据类型名称需要转换,因为SQLite建立搜索特定的字符串类型关联的类型名称。 if (dataType == "timestamp") dataType = "blob"; else if (dataType == "datetime" || dataType == "smalldatetime" || dataType == "date" || dataType == "datetime2" || dataType == "time") dataType = "datetime"; else if (dataType == "decimal") dataType = "numeric"; else if (dataType == "money" || dataType == "smallmoney") dataType = "numeric"; else if (dataType == "binary" || dataType == "varbinary" || dataType == "image") dataType = "blob"; else if (dataType == "tinyint") dataType = "smallint"; else if (dataType == "bigint") dataType = "integer"; else if (dataType == "sql_variant") dataType = "blob"; else if (dataType == "xml") dataType = "varchar"; else if (dataType == "uniqueidentifier") dataType = "guid"; else if (dataType == "ntext") dataType = "text"; else if (dataType == "nchar") dataType = "char"; if (dataType == "bit" || dataType == "int") { if (colDefault == "('False')") colDefault = "(0)"; else if (colDefault == "('True')") colDefault = "(1)"; } colDefault = FixDefaultValueString(colDefault); ColumnSchema col = new ColumnSchema(); col.ColumnName = colName; col.ColumnType = dataType; col.Length = length; col.IsNullable = isNullable; col.IsIdentity = isIdentity; col.DefaultValue = AdjustDefaultValue(colDefault); res.Columns.Add(col); } }2.1.2找到主键信息
SqlCommand cmd2 = new SqlCommand(@"EXEC sp_pkeys '" + tableName + "'",conn); using (SqlDataReader reader = cmd2.ExecuteReader()) { res.PrimaryKey = new List<string>(); while (reader.Read()) { string colName = (string)reader["COLUMN_NAME"]; res.PrimaryKey.Add(colName); } }2.1.3找到索引信息
SqlCommand cmd3 = new SqlCommand( @"exec sp_helpindex '" + tschma + "." + tableName + "'",conn); using (SqlDataReader reader = cmd3.ExecuteReader()) { res.Indexes = new List<IndexSchema>(); while (reader.Read()) { string indexName = (string)reader["index_name"]; string desc = (string)reader["index_description"]; string keys = (string)reader["index_keys"]; // 不加给主键增加索引 if (desc.Contains("primary key")) continue; IndexSchema index = BuildIndexSchema(indexName,desc,keys); res.Indexes.Add(index); } }2.1.4创建索引描述对象: /// <summary> /// 创建索引描述对象(Read from SQL Server). /// </summary> /// <param name="indexName">索引名称</param> /// <param name="desc">索引描述</param> /// <param name="keys">索引的键</param> /// <returns>索引的描述对象</returns> private static IndexSchema BuildIndexSchema(string indexName,string desc,string keys) { IndexSchema res = new IndexSchema(); res.IndexName = indexName; // 判断是否是唯一索引 string[] descParts = desc.Split(','); foreach (string p in descParts) { if (p.Trim().Contains("unique")) { res.IsUnique = true; break; } } // foreach // 检查键的排序规则 res.Columns = new List<IndexColumn>(); string[] keysParts = keys.Split(','); foreach (string p in keysParts) { Match m = _keyRx.Match(p.Trim()); if (!m.Success) { throw new ApplicationException("Illegal key name [" + p + "] in index [" +indexName + "]"); } string key = m.Groups[1].Value; IndexColumn ic = new IndexColumn(); ic.ColumnName = key; if (m.Groups[2].Success) ic.IsAscending = false; else ic.IsAscending = true; res.Columns.Add(ic); } return res; }2.1.5创建外键约束描述对象: /// <summary> /// 加一个外键描述对象 从一个特殊的对象中 /// </summary> /// <param name="conn">SQLServer连接字符串</param> /// <param name="ts">需要的数据表描述对象</param> private static void CreateForeignKeySchema(SqlConnection conn,TableSchema ts) { ts.ForeignKeys = new List<ForeignKeySchema>(); SqlCommand cmd = new SqlCommand( @"SELECT " + @" ColumnName = CU.COLUMN_NAME," + @" ForeignTableName = PK.TABLE_NAME," + @" ForeignColumnName = PT.COLUMN_NAME," + @" DeleteRule = C.DELETE_RULE," + @" IsNullable = COL.IS_NULLABLE " + @"FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C " + @"INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME " + @"INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME " + @"INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME " + @"INNER JOIN " + @" ( " + @" SELECT i1.TABLE_NAME,i2.COLUMN_NAME " + @" FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1 " + @" INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2 ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME " + @" WHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY' " + @" ) " + @"PT ON PT.TABLE_NAME = PK.TABLE_NAME " + @"INNER JOIN INFORMATION_SCHEMA.COLUMNS AS COL ON CU.COLUMN_NAME = COL.COLUMN_NAME AND FK.TABLE_NAME = COL.TABLE_NAME " + @"WHERE FK.Table_NAME='" + ts.TableName + "'",conn); using (SqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { ForeignKeySchema fkc = new ForeignKeySchema(); fkc.ColumnName = (string)reader["ColumnName"]; fkc.ForeignTableName = (string)reader["ForeignTableName"]; fkc.ForeignColumnName = (string)reader["ForeignColumnName"]; fkc.CascadeOnDelete = (string)reader["DeleteRule"] == "CASCADE"; fkc.IsNullable = (string)reader["IsNullable"] == "YES"; fkc.TableName = ts.TableName; ts.ForeignKeys.Add(fkc); } } }2.1.6创建SQLServer视图描述对象: SqlCommand cmd = new SqlCommand(@"SELECT TABLE_NAME,VIEW_DEFINITION from INFORMATION_SCHEMA.VIEWS",conn); using (SqlDataReader reader = cmd.ExecuteReader()) { int count = 0; while (reader.Read()) { ViewSchema vs = new ViewSchema(); if (reader["TABLE_NAME"] == DBNull.Value) continue; if (reader["VIEW_DEFINITION"] == DBNull.Value) continue; vs.ViewName = (string)reader["TABLE_NAME"]; vs.ViewSQL = (string)reader["VIEW_DEFINITION"]; // 从所有视图定义中移除所有.dbo字符串 vs.ViewSQL = removedbo.Replace(vs.ViewSQL,string.Empty); views.Add(vs); count++; CheckCancelled(); handler(false,true,50 + (int)(count * 50.0 / views.Count),"Parsed view " + vs.ViewName); _log.Debug("parsed view schema for [" + vs.ViewName + "]"); } }好了步骤一就基本完成了, 步骤一的细节部分大家可以参考 Liron Levi的源代码(不过注释是英文的哦),我也会把翻译过来的源代码优化一下传到CSDN上,感兴趣的同学可以学习一下,改进改进就可以弄个数据库通用转换类库哦,,,共同进步嘛。有空我会把剩下的步骤详细的写在后面的博文中,欢迎菜菜们学习,共同进步~~~ (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |