如何將數據導入到 SQL Server Compact Edition 數據庫中

一、場景描述
在智能設備(Smart Device)應用程序和智能客戶端(Smart Client)應用程序的部署階段,我們需要對離線數據進行初始化,即將後臺數據庫服務器中的一些數據,導入到離線數據庫中。通常採用兩種方式對離線數據進行初始化,第一種是在程序第一次運行時,通過數據同步的方式,把數據從後臺下載下來;第二種是將預先準備好的離線數據隨應用程序一起部署。

對於 SQL Server Compact Edition (SQL CE 3.1) 數據庫,第一種方式通常可以利用 Remote Data Access (RDA), Merge Replication, Sync Services for ADO.NET (SQL CE 3.5 中新增) 或者自己實現基於 Web Service 的數據同步機制來實現。RDA 和 Merge Replication 最大的缺點是隻能連到 SQL Server 數據庫,如果 SQL CE 需要跟 Oracle 和 DB2 等數據庫進行數據同步,需要 SQL Server 做“中介”。另外,RDA 沒有衝突處理機制,並且每次必須重新下載全部數據;Merge Replication 配置太繁瑣了。Sync Services for ADO.NET 目前還在 beta 階段,beta1 還不支持智能設備應用程序,只支持桌面應用程序。Orcas beta2 剛剛發佈,目前還沒有下載完畢,不知道有沒有性能方面的提升和增加對智能設備應用程序的支持。暫時先對 Sync Services for ADO.NET 保留意見,等我用上 beta2 了再詳細介紹。自己實現基於 Web Service 的數據同步機制需要考慮大數據量如何分批次傳輸和性能問題。總的來說,第一種方式的實現途徑很多,如果初始化數據量比較大,並且客戶端數量比較多的話,那麼將有可能帶來漫長的部署過程和一筆巨大的無線網絡流量的費用。

第二種方式可以利用 SQL CE 3.1 對桌面應用程序的支持,預先將 SQL Server, Oracle, DB2, MySQL 等等各種數據庫的數據導入到 SQL CE 中,然後通過 ActiveSync 批量將 SQL CE 的數據庫文件(*.sdf)拷貝到設備或機器上。以後客戶端再通過 Web Service 的方式下載新增/修改/刪除的數據來更新本地的離線數據。這樣可以節約大量的部署時間和網絡通信成本。

當然並不是第二種方式一定比第一種方式好,這個看具體的實施環境。本文主要介紹的是第二種方式。

二、技術選擇

既然 SQL CE 3.1 支持桌面應用程序,那麼我們可以通過三種方式來準備離線數據:第一,利用 RDA 數據同步;第二,利用 Merge Replication 數據同步;第三,用 ADO.NET 直接從讀寫數據。第一和第二種方式需要額外的安裝和配置,而且只支持 SQL Server 數據庫。如果非要在第一和第二種方式中選擇的話,我會選擇 RDA,因爲它配置工作量更少,性能更好,更加靈活。本文選擇第三種方式,因爲它離兩個數據庫的距離最近,而且支持多種數據庫。

三、實現原理

數據導入程序實現起來很簡單,不過需要考慮性能。從源數據庫讀取數據要考慮速度和內存衝擊,可以採用 DataSet 或者 DataReader,毫無疑問我們選擇 DataReader。將數據寫入 SQL CE,通常大家會想到編寫一個 SqlCeCommand,然後給 SqlCeCommand 的 CommandText 屬性賦上 Insert SQL 語句“insert into Products values(@ProductID, @ProductName)”,接着一邊讀取數據,一邊給參數賦值並寫入 SQL CE 數據庫中……大家冷落了一個叫 SqlCeResultSet 的對象,它是 SQL Mobile 增加的數據訪問對象。SqlCeResultSet 提供了一個功能的組合:DataSet 的可更新性和可滾動性以及與 SqlCeDataReader 類似的性能。SqlCeResultSet 類繼承了 SqlCeDataReader 類,因此它擁有 SqlCeDataReader 類所有的特性。利用 SqlCeResultSet 可以實現高性能的數據讀取和寫入。

四、代碼和分析

/// <summary>
/// 將源數據庫表的數據複製到 SQL Server Compact Edition 數據庫的表中。
/// </summary>
/// <param name="srcConnection">源數據庫連接接對象。</param>
/// <param name="destConnection">目標 SQL Server Compact Edition 數據庫連接對象。</param>
/// <param name="queryString">源數據的查詢語句。</param>
/// <param name="destTableName">目標數據庫表名稱。</param>
/// <remarks>本方法假設目標 SQL Server Compact Edition 數據庫的表已經存在。</remarks>
public static void CopyTable(
    IDbConnection srcConnection, 
    SqlCeConnection destConnection, 
    
string queryString, 
    
string destTableName)
{
    IDbCommand srcCommand 
= srcConnection.CreateCommand();
    srcCommand.CommandText 
= queryString;

    SqlCeCommand destCommand 
= destConnection.CreateCommand();
    destCommand.CommandType 
= CommandType.TableDirect; //基於表的訪問,性能更好
    destCommand.CommandText = destTableName;
    
try
    {
        IDataReader srcReader 
= srcCommand.ExecuteReader();

        SqlCeResultSet resultSet 
= destCommand.ExecuteResultSet(
            ResultSetOptions.Sensitive 
|   //檢測對數據源所做的更改
            ResultSetOptions.Scrollable |  //可以向前或向後滾動
            ResultSetOptions.Updatable); //允許更新數據

        
object[] values;
        SqlCeUpdatableRecord record;
        
while (srcReader.Read())
        {
            
// 從源數據庫表讀取記錄
            values = new object[srcReader.FieldCount];
            srcReader.GetValues(values);

            
// 把記錄寫入到目標數據庫表
            record = resultSet.CreateRecord();
            record.SetValues(values);
            resultSet.Insert(record);
        }

        srcReader.Close();
        resultSet.Close();
    }
    
catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine(ex.ToString());
    }
}

由於 CopyTable 函數的源數據庫連接參數採用的是 IDbConnection 接口,所以該方法可以支持多種源數據庫。代碼中還利用 IDataReader.GetValues(object[] values) 和 SqlCeUpdatableRecord.SetValues(object[] values) 更方便的讀取和寫入數據。

在使用 CopyTable 函數之前必須預先創建好 SQL CE 數據庫的表結構,並且 SQL CE 數據庫的表結構必須跟 queryString 參數(select SQL 語句)的查詢結果的表結構對應。

通過下面的代碼使用 CopyTable 函數:

// 創建源 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);

VerifyDatabaseExists(destConnString); //創建數據庫結構

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

// 複製數據
CopyTable(srcConnection, destConnection, "SELECT * FROM Products""Products");
CopyTable(srcConnection, destConnection, 
"SELECT * FROM Employees""Employees"
);

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

五、創建數據庫結構

上面說到在使用 CopyTable 函數之前 SQL CE 數據庫必須存在並且表結構都創建好。我們現在就來編寫創建數據庫結構的代碼。首先創建一個名爲 DbSchema.sql 的文件,並編寫 Northwind 數據庫中的 Products 和 Employees 表的創建腳本:

CREATE TABLE Products(
    ProductID 
int NOT NULL CONSTRAINT PK_Products PRIMARY KEY,
    ProductName 
nvarchar(40NOT NULL,
    SupplierID 
int NULL,
    CategoryID 
int NULL,
    QuantityPerUnit 
nvarchar(20NULL,
    UnitPrice 
money NULL,
    UnitsInStock 
smallint NULL,
    UnitsOnOrder 
smallint NULL,
    ReorderLevel 
smallint NULL,
    Discontinued 
bit NOT NULL
)
GO
CREATE TABLE Employees(
    EmployeeID 
int NOT NULL CONSTRAINT PK_Employees PRIMARY KEY,
    LastName 
nvarchar(20NOT NULL,
    FirstName 
nvarchar(10NOT NULL,
    Title 
nvarchar(30NULL,
    TitleOfCourtesy 
nvarchar(25NULL,
    BirthDate 
datetime NULL,
    HireDate 
datetime NULL,
    Address 
nvarchar(60NULL,
    City 
nvarchar(15NULL,
    Region 
nvarchar(15NULL,
    PostalCode 
nvarchar(10NULL,
    Country 
nvarchar(15NULL,
    HomePhone 
nvarchar(24NULL,
    Extension 
nvarchar(4NULL,
    Photo 
image NULL,
    Notes 
ntext NULL,
    ReportsTo 
int NULL,
    PhotoPath 
nvarchar(255NULL
)
GO

這段 SQL 語句不能直接在 SQL CE 上執行的,我們需要進行一些字符串的處理。現在將該文件添加到 Visual Studio 2005 的項目資源中。





並添加執行這段 SQL 創建數據庫表結構的方法:

public static void VerifyDatabaseExists(string connectionString)
{
    
using (SqlCeConnection connection = new SqlCeConnection(connectionString))
    {
        
if (! File.Exists(connection.Database))
        {
            
using (SqlCeEngine engine = new SqlCeEngine(connection.ConnectionString))
            {
                engine.CreateDatabase();

                
string[] commands = Properties.Resources.DbSchema.Split(
                    
new string[] { "GO" }, StringSplitOptions.RemoveEmptyEntries);

                SqlCeCommand command 
= new SqlCeCommand();
                command.Connection 
= connection;
                connection.Open();
                
for (int i = 0; i < commands.Length; i++)
                {
                    command.CommandText 
= commands[i];
                    command.ExecuteNonQuery();
                }
            }
        }
    }
}

六、總結

性能測試的結果會因爲環境的不同而有一些出入,我想留給大家去做會更有意義。不過我相信這是當前性能比較好的向 SQL CE 導入數據的方法之一。目前需要預先創建好 SQL CE 的表結構是美中不足的地方,我會在後續文章中實現一個自動根據查詢結果生成創建 SQL CE 表結構的 SQL 語句的代碼,其實並不難。

參考:
ADO.NET Generic Copy Table Data Function
Creating your SQL Server Compact Edition database and schema in code

示例代碼下載:sqlce_data_import.rar

作者:黎波
博客:http://upto.cnblogs.com/
日期:2007年7月29日 

發佈了96 篇原創文章 · 獲贊 1 · 訪問量 43萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章