黎波

Windows Mobile Development for Line of Business
posts - 179, comments - 1140, trackbacks - 36, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

系列文章导航:
如何将数据导入到 SQL Server Compact Edition 数据库中(一)
如何将数据导入到 SQL Server Compact Edition 数据库中(二)
如何将数据导入到 SQL Server Compact Edition 数据库中(三)

摘要:在本系列文章的第一篇和第二篇为了提高数据写入的性能,我使用了 SqlCeResultSet 基于表的数据写入方式,而不是使用常规的 Insert 语句。使用 SqlCeResultSet 写入数据确实方便又快速,但是必须保证从源数据库查询的结果集(通过 Select 查询语句)跟目标数据库(SQL Server Compact Edition)表的字段先后顺序一致。如果不一致,可能会导致数据导入出错;即便是导入成功,数据跟原来的字段位置也对不上。所以,我觉得有必要给大家介绍常规的 Insert 语句数据插入方式,解决 SqlCeResultSet 存在的问题。

在第三篇文章中,我们学习了 IDataReader.GetSchemaTable 方法,它可以返回一个描述了 DataReader 查询结果中各列的元数据的 DataTable。在前面的文章介绍的数据导入方法中,都是使用 DataReader 从源数据库读取数据。那么从这个 DataReader 获取的 SchemaTable 信息,就可以用于生成插入数据的 Insert 语句,前提是源数据库和目标数据库的表字段名称一致,字段的先后顺序可以不一样。以下是根据 SchemaTable 生成 Insert 语句的代码:

// 通过 DataReader 获取 SchemaTable 信息
srcReader = srcCommand.ExecuteReader(CommandBehavior.KeyInfo);
DataTable scheamTable 
= srcReader.GetSchemaTable();

// 生成 SQL Server Compact Edition 数据插入 SQL 语句
StringBuilder sbFields = new StringBuilder();
StringBuilder sbParams 
= new StringBuilder();
string field, param;
DataRow schemaRow;
for (int i = 0; i < scheamTable.Rows.Count; i++)
{
    
if (i != 0)
    {
        sbFields.Append(
"");
        sbParams.Append(
"");
    } 

    schemaRow 
= scheamTable.Rows[i];
    field 
= string.Format("[{0}]", schemaRow["ColumnName"]); //字段名称
    param = "@" + ((string)schemaRow["ColumnName"]).Replace(" ""_"); //参数名称
    sbFields.Append(field);
    sbParams.Append(param);
    destCommand.Parameters.Add(param, 
null);


string insertSql = string.Format("INSERT INTO [{0}]({1}) VALUES({2})", destTableName, sbFields, sbParams);
destCommand.CommandText 
= insertSql; 

生成 Insert 语句的代码很简单,这里就不再详细说明了。准备好了 Insert 语句,就可以开始从源数据库取数据并写入目标数据库了。这里我使用了 DataReader.GetValues 方法一次性读取一整行数据,再给 Insert 命令的参数赋值。代码如下所示:

// 执行数据导入
object[] values;
while (srcReader.Read())
{
    values 
= new object[srcReader.FieldCount];
    srcReader.GetValues(values);
    
for (int i = 0; i < values.Length; i++)
    {
        destCommand.Parameters[i].Value 
= values[i];
    }
    destCommand.ExecuteNonQuery();
}

本文的主要内容到这里已经介绍完了,我们可以结合第三篇文章介绍的根据 SchemaTable 自动生成创建表结构的 SQL 语句的代码,在导入数据前先自动创建数据表结构。相关的代码如下:

// 通过 DataReader 获取 SchemaTable 信息
srcReader = srcCommand.ExecuteReader(CommandBehavior.KeyInfo);
DataTable scheamTable = srcReader.GetSchemaTable();


// 创建 SQL Server Compact Edition 表结构
SqlCeCommand command = destConnection.CreateCommand();
command.CommandText 
= GenerateTableSchemaSql(scheamTable);
command.ExecuteNonQuery();

// 生成 SQL Server Compact Edition 数据插入 SQL 语句
StringBuilder sbFields = new StringBuilder();
StringBuilder sbParams = new StringBuilder();
......

通过遍历 SQL Server 2000 的 Northwind 数据库的每个用户表,并将每个表的数据导入到一个 SQL Server Compact Edition 数据文件 Northwind.sdf 中。代码如下所示:

// 创建源 SQL Server 数据库连接对象
string srcConnString = "Data Source=(local);Initial Catalog=Northwind;Integrated Security=True";
SqlConnection srcConnection 
= new SqlConnection(srcConnString);

// 创建目标 SQL Server Compact Edition 数据库连接对象
string destConnString = @"Data Source=C:\Northwind.sdf";
SqlCeConnection destConnection 
= new SqlCeConnection(destConnString);

// 创建 SQL Server Compact Edition 数据文件
VerifyDatabaseExists(destConnString);

srcConnection.Open();
destConnection.Open();

// 复制数据
string[] tableNames = GetTableNames(srcConnection);
string query;
for (int i = 0; i < tableNames.Length; i++)
{
    query 
= string.Format("SELECT * FROM [{0}]", tableNames[i]);
    CopyTable(srcConnection, destConnection, query, tableNames[i]);
}

srcConnection.Close();
destConnection.Close();

同第二篇文章相比,本文中的 VerifyDatabaseExists 方法只创建 SQL Server Compact Edition 数据文件,不批量创建表结构,因为我们用上了“自动”的方法,不需要预先准备好创建表结构的 SQL 脚本。GetTableNames 和 GenerateTableSchemaSql 方法跟第三篇文章的一样,这里不再解释。

总结:本文介绍了一种比 SqlCeResultSet 更安全的数据写入方式,并结合了第三篇文章中介绍的自动生成创建数据库表结构的 SQL 语句的方法,向大家展示了一种比较完善的 SQL Server Compact Edition 数据导入方法。在后续的文章中我会继续深入下去,提供本方案在实际应用中面临的问题的解决方法。

示例代码下载:sqlce_data_import4.rar

作者:黎波
博客:http://upto.cnblogs.com/
日期:2008年2月9日

Feedback

#1楼    回复  引用  查看    

2008-02-09 23:47 by fox23      
一上来就抢到沙发...支持一个
写完我一起收藏

#2楼    回复  引用    

2008-02-10 00:14 by 台州拉 [未注册用户]
--引用--------------------------------------------------
fox23: 一上来就抢到沙发...支持一个
写完我一起收藏
--------------------------------------------------------
```
我刚下了正在调试

#3楼    回复  引用    

2008-02-13 19:27 by tdskee [未注册用户]
非常感谢
太好了
学习

#4楼    回复  引用    

2008-02-14 16:05 by 51ytw [未注册用户]
非常不错,实用

#5楼    回复  引用  查看    

2008-02-16 13:15 by 毁于随      
文章写的很好.不愧是MVP.但是我有一个需求,就是让PC端的程序直接操作设备中的sdf数据库.

在2.0框架中,我发现已经可以引用System.Data.SqlServerCE这个类库了,然后在程中使用与CF一样的代码时会因为没有提供Native的Dll而报错,我在VS的安装文件夹中找到了Sqlce*.dll的一些文件Copy到程序的运行路径下,就可以读写"真实存储器"中的sdf库了.这里我用了"真实存储器"是因为在ActiveSync的作用下,在我的电脑中会有"Mobile Device"文件夹,如果在同步的时候就可以像普通的存储器一样看到设备中的sdf库,而在VS中,我通过"添加数据源"功能生成的类去操作时,会提示路径错误,原因是DataSource="Mobile Devlce\DB.sdf"这样的,而程序实际上是不可以读到Mobile Device这个文件夹的.
但是,在VS的数据源页中,可以通过"预览数据"直接读取到了设备中的数据库表中的内容.看来,VS是有单独的处理的.

这种数据同步的方式也有一定的用处.比如在数据冲突或者向服务器同步数据时违反了主键约束,则会要求人员的参与,这个功能如果在设备上操作很不方便,不利于排除错误.另外,对于我的工作中使用的WinCE设备又缺少触屏功能,所以就更不方便.试想一下,将设备插到通信座中,然后在PC的程序上点击"上传数据"岂不是更方便?

我的MSN+邮箱:fx__wdl at hotmail.com 希望共同研究一下这个问题.

#6楼 [楼主]   回复  引用  查看    

2008-02-16 17:22 by 黎波      
@毁于随
你提的需求很有意思,我也注意到Visual Studio和SQL Server Management Studio可以直接访问设备上的sdf数据库,不过一直没有深究其原理。

我想VS能够访问到设备上的sdf数据库,必定不是轻而易举的事情。SQLCE数据库引擎是运行在桌面电脑上,还是设备上。
如果是桌面电脑上,那就是说桌面电脑上的数据库引擎首先要能够读取到设备上的sdf文件。这里可能用了RAPI把设备上的sdf文件copy到桌面电脑上,再进行操作,可是这样实现的效果肯定不好,文件大点怎么办,我自己使用过一次,访问的速度确实很慢。如果是直接访问,那就不知道什么原理了,可能要去反编译VS的DLL才知道。
如果是运行在设备上,那VS是通过发送命令给设备的某个程序去执行,想想也挺复杂的,这样程序之间同步的问题肯定不好处理。

我暂时还是没有时间去研究这个问题,看有没有其他朋友能够指点迷津啊!

#7楼    回复  引用  查看    

2008-02-16 18:36 by 马宁      
我觉得有两种可能性:第一种就是通过RAPI来拷贝到本地,就是你说的

另一种可能是使用VS调试时推送到设备上的客户端来实现

这只是猜测,可以HardRest一下模拟器,然后拷贝sdf文件过去,再通过ActiveSync访问,看看是否可以成功。

#8楼    回复  引用  查看    

2008-02-18 11:31 by 毁于随      
通过RAPI的方式有一些问题,因为我不可能要求在上传数据的时候要求客户端将程序退出,或者"关闭数据库"连接,即使勉强去做也会像你说的,实现的效果肯定不好,但也可以做为一种方案,呵呵.

VS2005应该是使用VC++编写的,反编难度太大了吧.

直接访问的方式你们分析的很有道理,应该是依赖于设备中的组件,因为在我的设备中没有安装SQLCE的Engin的时候,连VS也是连接不了的.

上面我已经说过了,直接用程序去读存放在PC上的sdf文件是完全可行的,不知道两位老大是否注意到了,呵呵.现在的问题只是:
桌面程序-->ActiveSync-->设备中的sdf,也不知道从哪里可以下手,呵呵.....如果解决这个问题,那以后可真是爽呆了.....

#9楼 [楼主]   回复  引用  查看    

2008-02-18 12:50 by 黎波      
@毁于随
--引用--------------------------------------------------
毁于随: VS2005应该是使用VC++编写的,反编难度太大了吧.
--------------------------------------------------------
事实上VS2005很多UI和功能组件,都是用.NET编写,SQL Server 2005就更不用说了。你可以打开他们的程序目录看看。

#10楼    回复  引用  查看    

2008-02-18 12:55 by 毁于随      
我确实也找了,而且看那个预览对话框的样子也像是.Net的.

#11楼 [楼主]   回复  引用  查看    

2008-02-18 13:06 by 黎波      
@毁于随
我前天反编译过那些相关的DLL,简单看了一下,还没有看明白。不过看到数据库连接对象有区分执行的数据库引擎是本地的(PC)还是远程的(Mobile),等有时间再继续看。

#12楼    回复  引用  查看    

2008-02-18 13:12 by 毁于随      
你找到哪些相关的Dll了?

是.Net的Dll,还是标准的?还是COM的Dll?

#13楼 [楼主]   回复  引用  查看    

2008-02-18 19:34 by 黎波      
@毁于随
SQL Server 2005 的DLL在“X:\Program Files\Microsoft SQL Server\90\Tools\Binn\VSShell\Common7\IDE\”目录下:
Microsoft.SqlServerCe.Client.dll
Microsoft.SqlServerCe.Enumerator.dll
Microsoft.SqlServerCe.ManagementUI.dll
Microsoft.SqlServerCE.ReplWiz.dll

Visual Studio 2005 的DLL在“X:\Program Files\Microsoft Visual Studio 8\Common7\IDE”目录下:
Microsoft.SqlServerCe.Client.dll

其实这两个目录下的很多文件都是.NET写的,长得很像:D

#14楼    回复  引用    

2008-02-19 08:29 by guthing [未注册用户]
一直在找这方面的资料 现在终于找到了 受益中...

#15楼    回复  引用  查看    

2008-02-19 09:45 by 毁于随      
这些Dll我看过了.没有找到相关的内容.
我手里的SQL2005是Express版的,Management也是Express版的,不知道你的企业版的Management是否有直接通过ActiveSync浏览数据库的功能,我的是没有.

顺便发句牢骚,感觉MS就是在骗人,什么JIT编译不会慢,看看Management Studio和SQL2000的时候真是没的比呀.

#16楼    回复  引用    

2008-02-21 14:41 by haiy [未注册用户]
能在Mobile中实现类似的功能吗?怎么实现呢?我使用该demo做了一些修改后调试出现下面的错误:
“System.Text.StringBuilder”并不包含“AppendLine”的定义
“System.Text.StringBuilder.AppendFormat(System.IFormatProvider, string, params object[])”最匹配的重载方法具有一些无效参数

#17楼 [楼主]   回复  引用  查看    

2008-02-21 22:26 by 黎波      
@haiy
StringBuilder.AppendLine("Mobile") 可以换成 StringBuilder.Append("Mobile\r\n")

#18楼    回复  引用    

2008-02-22 11:48 by haiy [未注册用户]
谢谢!已经更正了一个错误,但还是不行。

根据回复信息{StringBuilder.AppendLine("Mobile") 可以换成 StringBuilder.Append("Mobile\r\n")},已经更正错误{“System.Text.StringBuilder”并不包含“AppendLine”的定义}

编译错误的语句: sbPKFields.AppendFormat("[{0}],", columnName);
编译错误的信息:
1、“System.Text.StringBuilder.AppendForma(System.IFormatProvider, string, params object[])”最匹配的重载方法具有一些无效参数
2、参数“1”: 无法从“string”转换为“System.IFormatProvider”

#19楼    回复  引用  查看    

2008-02-22 14:40 by 毁于随      
sbPKFields.AppendFormat(null,"[{0}]", columnName);

#20楼    回复  引用    

2008-02-22 21:28 by haiy [未注册用户]
太好了,编译通过,并且数据导入成功.
SQL Server Compact Edition不支持双主键,所以缺少三个数据表,分别是CustomerCustomerDemo、EmployeeTerritories和Order Details.

#21楼    回复  引用    

2008-02-22 21:41 by haiy [未注册用户]
我是初学者,搞糊涂了,呵呵!

可是"如何将数据导入到 SQL Server Compact Edition 数据库中(四)"例子导出的数据库文件有数据表CustomerCustomerDemo、EmployeeTerritories和Order Details.

#22楼 [楼主]   回复  引用  查看    

2008-02-22 23:34 by 黎波      
@haiy
SQL Server Compact Edition 是支持多主键字段的。

#23楼    回复  引用    

2008-02-26 09:39 by tds [未注册用户]
在sql server中将数据写到sqlce成功..
但是换成oracle的时候,提示一个错误 ...
// 从 SQL Server 信息架构视图获取 Northwind 数据库所有表的名称
command.CommandText = @"SELECT * FROM INFORMATION_SCHEMA_TABLES
WHERE TABLE_TYPE='BASE TABLE' AND TABLE_CATALOG="Northwind";

这部分不知道要怎么获得oracle中的所有表及其所指定的数据库呢?

忘指点...谢谢了...

#24楼    回复  引用  查看    

2008-02-26 14:23 by 大双      
table_catalog指定的是Northwind数据库,查询出来的是他下面的所有TableNames,可是我现在数据库DataBase下面有63个表,然而我只要3个....
是不是没必要这么指定呢??或者还其他的方式???
麻烦指点下....

#25楼 [楼主]   回复  引用  查看    

2008-02-28 15:49 by 黎波      
@大双
我只是为了演示,生成所有表的SQL,你可以自己修改示例代码,指定需要生成SQL的表名即可。

#26楼    回复  引用    

2008-04-01 10:35 by 阿奎 [未注册用户]
有个软件Sqlce Desktop Manager就实现了PC上操作设备上的SQLCE数据库的功能。
不过不知道怎么实现的。

#27楼    回复  引用    

2008-07-25 15:00 by cakalee [未注册用户]
在运行destConnection.Open();时提示"未指定的错误 [ sqlcese30.sys.dll ]"我的环境是vs.net 2005 +sql server 2000 sp3+sql ce 2.0 sp3+Microsoft SQL Server Compact Edition\v3.5

#28楼 [楼主]   回复  引用  查看    

2008-07-25 18:04 by 黎波      
@阿奎
看看这里:http://www.cnblogs.com/upto/archive/2008/07/25/1251532.html

#29楼 [楼主]   回复  引用  查看    

2008-07-25 22:51 by 黎波      
@cakalee
本文是基于Visual Studio 2005, SQL Server 2000 SP4/2005 SP2, SQL Server Compact 3.1编写的。

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-02-09 23:46 编辑过
 
另存  打印