SQLite数据库管理系统-我所认识的数据库引擎


    SQLite 是一款轻量级的、被设计用于嵌入式系统的关联式数据库管理系统。SQLite 是一个实现自我依赖、纯客户端、零配置且支持事务的数据库引擎。它由D. Richard Hipp首次开发,目前已是世界上最广泛部署的开源数据库引擎。
    本文中,我们将介绍如下内容:
    创建一个SQLite 数据库
    

    复制代码 代码如下:
    SQLiteConnection conn = new SQLiteConnection("Data Source=mytest.s3db");
    conn.Open();
    SQLite 数据插入
    

    复制代码 代码如下:
    /// <summary>
    /// Allows the programmer to easily insert into the DB
    /// </summary>
    /// <param name="tableName">The table into which we insert the data.</param>
    /// <param name="data">A dictionary containing the column names and data for the insert.</param>
    /// <returns>A boolean true or false to signify success or failure.</returns>
    public bool Insert(string tableName, Dictionary<string, string> data)
    {
    Boolean returnCode = true;
    StringBuilder columnBuilder = new StringBuilder();
    StringBuilder valueBuilder = new StringBuilder();
    foreach (KeyValuePair<string, string> val in data)
    {
    columnBuilder.AppendFormat(" {0},", val.Key);
    valueBuilder.AppendFormat(" '{0}',", val.Value);
    }
    columnBuilder.Remove(columnBuilder.Length - 1, 1);
    valueBuilder.Remove(valueBuilder.Length - 1, 1);
    try
    {
    this.ExecuteNonQuery(string.Format("INSERT INTO {0}({1}) VALUES({2});",
    tableName, columnBuilder, valueBuilder));
    }
    catch (Exception ex)
    {
    mLog.Warn(ex.ToString());
    returnCode = false;
    }
    return returnCode;
    }
    复制代码 代码如下:
    DateTime entryTime;
    string name = string.Empty, title = string.Empty;
    GetSampleData(out name, out title, out entryTime);
    int id = random.Next();
    insertParameterDic.Add("Id", id.ToString());
    insertParameterDic.Add("Name", name);
    insertParameterDic.Add("Title", title);
    insertParameterDic.Add("EntryTime",
    entryTime.ToString("yyyy-MM-dd HH:mm:ss"));
    db.Insert("Person", insertParameterDic);
    SQLite 的事务处理方式
    Begin Transaction:
    begin-stmt
    Commit Transaction:
    commit-stmt
    Rollback Transaction:
    rollback-stmt
    复制代码 代码如下:
    try
    {
    db.OpenTransaction();
    Insert4Native();
    db.CommiteTransaction();
    }
    catch (System.Exception ex)
    {
    mLog.Error(ex.ToString());
    db.RollbackTransaction();
    }
    SQLite 的索引
    索引是一种用来优化查询的特性,在数据中分为聚簇索引和非聚簇索引;前者是由数据库中数据组织方式决定的,比如我们在往数据库中一条一条插入数据时,聚簇索引能够保证按顺序插入,插入后数据的位置和结构不变。非聚簇索引是指我们手动、显式创建的索引,可以为数据库中的每个列创建索引,和字典中的索引类似,遵循的原则是对有分散性和组合型的列建立索引,以利于大数据和复杂查询情况下提高查询效率。
    create-index-stmt
    复制代码 代码如下:
    /// <summary>
    /// Create index
    /// </summary>
    /// <param name="tableName">table name</param>
    /// <param name="columnName">column name</param>
    /// <param name="indexName">index name</param>
    public void CreateIndex(string tableName, string columnName, string indexName)
    {
    string createIndexText = string.Format("CREATE INDEX {0} ON {1} ({2});",
    indexName, tableName, columnName);
    ExecuteNonQuery(createIndexText);
    }
    简单查询无关数据库大小情况下对查询效率的测试结果如下(700,000条数据):
    复制代码 代码如下:
    string sql = "SELECT LeafName FROM File WHERE Length > 5000";
    
Capture

    复杂查询情况下对查询效率的测试结果如下(~40,000条数据):
    复制代码 代码如下:
    string sql = "SELECT folder.Location AS FilePath"
    + "FROM Folder folder LEFT JOIN File file ON file.ParentGuid=folder.Guid"
    +"WHERE file.Length > 5000000 GROUP BY File.LeafName";
    Capture2
    SQLite 的触发器(Trigger)
    触发器是指当一个特定的数据库事件(DELETE, INSERT, or UPDATE)发生以后自动执行的数据库操作,  我们可以把触发器理解为高级语言中的事件(Event)。
    假设我有两个表
    Folder(Guid VCHAR(255) NOT NULL, Deleted BOOLEAN DEFAULT 0)
    File(ParentGuid VCHAR(255) NOT NULL, Deleted BOOLEAN DEFAULT 0)
    在Folder 表中创建一个触发器Update_Folder_Deleted:
    复制代码 代码如下:
    CREATE TRIGGER Update_Folder_Deleted UPDATE Deleted ON Folder
    Begin
    UPDATE File SET Deleted=new.Deleted WHERE ParentGuid=old.Guid;
    END;
    创建完触发器以后在执行以下语句:
    复制代码 代码如下:
    UPDATE Folder SET Deleted=1 WHERE Guid='13051a74-a09c-4b71-ae6d-42d4b1a4a7ae'
    以上语句将会导致下面的语句自动执行:
    复制代码 代码如下:
    UPDATE File SET Deleted=1 WHERE ParentGuid='13051a74-a09c-4b71-ae6d-42d4b1a4a7ae'
    SQLite 的视图(View)
    视图可以是一个虚拟表,里面可以存储按照一定条件过滤出来的数据集合,这样我们再下次想得到这些特定数据集合的时候就不用通过复杂查询来获得,简单的查询指定视图就可以得到想要的数据。
    在下个例子中,我们创建一个简单的视图:
    Capture
    基于上面的查询结果我们创建一个视图:
    Capture2
    SQLite 命令行工具
    SQLite 库中包含了一个SQLite3.exe 的命令行工具,它可以实现SQLite 各项基本操作。这里只介绍一下如何使用它来分析我们的查询结果:
    1. CMD->sqlite3.exe MySQLiteDbWithoutIndex.s3db
    Capture
    2. 开启EXPLAIN 功能并分析指定查询结果
    Capture
    3. 重新使用命令行打开一个有索引的数据库并执行前两步
    Capture
    4. 通过比较两个不同查询语句的分析结果,我们可以发现如果查询过程中使用了索引,SQLite 会在detail 列中提示我们。
    5. 要注意的是每条语句后面都要加分号“;”
    SQLite一些常见的使用限制
    1. SQLite 不支持Unicode 字符的大小写比较,请看以下测试结果:
    Capture
    2. 如何处理SQLite 转义字符:
    复制代码 代码如下:
    INSERT INTO xyz VALUES('5 O''clock');
    3. 一条复合SELECT语句的条数限制:
    一条复合查询语句是指多条SELECT语句由 UNION, UNION ALL, EXCEPT, or INTERSECT 连接起来. SQLite进程的代码生成器使用递归算法来组合SELECT语句。为了降低堆栈的大小,SQLite 的设计者们限制了一条复合SELECT语句的条目数量。 SQLITE_MAX_COMPOUND_SELECT的默认值是500. 这个值没有严格限制,在实践中,几乎很难看到一条复合查询语句的条目数大于500的。
    这里提到复合查询的原因是我们可以使用它来帮助我们快速插入大量数据:
    复制代码 代码如下:
    public void Insert4SelectUnion()
    {
    bool newQuery = true;
    StringBuilder query = new StringBuilder(4 * ROWS4ACTION);
    for (int i = 0; i < ROWS4ACTION; i++)
    {
    if (newQuery)
    {
    query.Append("INSERT INTO Person");
    newQuery = false;
    }
    else
    {
    query.Append(" UNION ALL");
    }
    DateTime entryTime;
    string name = string.Empty, title = string.Empty;
    GetSampleData(out name, out title, out entryTime);
    int id = random.Next();
    query.AppendFormat(" SELECT '{0}','{1}','{2}','{3}'", id, name, title, entryTime.ToString("yyyy-MM-dd HH:mm:ss"));
    if (i % 499 == 0)
    {
    db.ExecuteNonQuery(query.ToString());
    query.Remove(0, query.Length);
    newQuery = true;
    }
    }
    //executing remaining lines
    if (!newQuery)
    {
    db.ExecuteNonQuery(query.ToString());
    query.Remove(0, query.Length);
    }
    }