一.基本介绍
SQLite数据库相较于我们常用的Mysql,Oracle而言,实在是轻量得不行(最低只占几百K的内存)。平时开发或生产环境中使用各种类型的数据库,可能都需要先安装数据库服务(server),然后才能通过代码、命令行或者客户端工具来操作数据库,但是SQLite却有点别具一格,它是一个文件型的关系数据库,完全不用你安装,也不需要任何的配置或依赖,去官网下载编译好的二进制文件解压就可以使用——第一次使用时,同事直接拷给我一个文件,说这就是SQLite了,我擦嘞还惊了一下,竟然直接对着文件就能执行CRUD命令,这完全刷新了我对数据库的认知。而它的官网/When to use SQLite也说得特别明确:SQLite is not directly comparable to client/server SQL database engines such as MySQL, Oracle, or SQL Server since SQLite is trying to solve a different problem。是的,它的出现并不是为了和 Mysql,Oracle等数据库竞争,而是为了解决不同的问题;那么哪些场景适合使用SQLite,哪些场景又适合client/server型数据库呢?博主本来也想问度娘的,但是发现官网中就有最最合适的解释:Client/server SQL database engines strive to implement a shared repository of enterprise data; SQLite strives to provide local data storage for individual applications and devices。意译过来就是说,client/server型的数据库适合于共享数据的存储—— 一个server端存储数据,N个客户端都可以对数据进行CRUD的操作;而SQLite就完全是一种本地化的文件存储,加之其非常的轻量,特别适合个人应用和设备,所以,你会发现SQLite在嵌入式设备开发比如移动开发中应用得非常广泛。博主虽然不是做嵌入式开发的,但是用了一下SQLite发现特别小巧好用,建议大家如果应用程序是并发量不是特别高本地应用,完全可以尝试使用SQLite这种轻量的数据库来代替,也省去了安装繁重的数据库服务对系统资源的占用。
二.SQLite 的存储类型
学习一个新的数据库,大体从几个方面来了解它——存储结构、操作语言以及支持存储的数据类型。既然SQLite是一种关系型数据库,那么就支持通用的SQL语言,所以你大可将你深厚的SQL功力运用到SQLite身上愉快的进行你的CRUD操作(当然,一些内置函数和SQL语法还是有所不同)。SQLite比较特殊的其实在于它粗放式的数据存储类型,而且并不强制的进行类型约束,这点和其他关系型数据库有很大的不同。如此的结果就是,虽然创建表的时候你指定了某一列应该用什么数据类型,但实际上你是可以胡来的,比如向整型列中插入文本数据,向字符型中插入日期等等(有个特殊情况就是建表时主键设置若为INTEGER PRIMARY KEY【原文:except an INTEGER PRIMARY KEY column】就只能插入整数,插入其他数据类型会报错)。至于原因,SQLite官网数据类型页面中Datatypes In SQLite段讲得很清楚,博主转述过来就是说:传统的关系型数据库采用的是静态数据类型系统,一个字段值的数据类型由存储其值的列容器决定,而SQLite采用更通用的动态数据类型系统,一个字段的数据类型只与其值本身有关,而与存储它的容器无关。
按照官网描述,SQLite支持的数据类型分为以下 5 个存储类型:
- NULL 空值
- INTEGER 带符号整型,根据其大小存储在1, 2, 3, 4, 6,或 8 字节中
- REAL 浮点型,存储为一个 8 字节的IEEE 浮点数
- TEXT 文本类型 (UTF-8, UTF-16BE 或 UTF-16LE编码格式)
- BLOB 全型(布衣博主自己给的称谓),存什么就是什么
别看只有基本的 5 种存储类型支持,但由于SQLite采用的是动态数据类型系统,而且存储类较之普通的数据类型是更加笼统的包含关系,因此能完全兼容其它静态数据类型系统的关系型数据库。但是这种太自由化的存储还是有些问题的,比如一个数据列我同时存了一个 400 整型值和 '500' 的文本类型值,这两个值数据类型不一样,我这么比较?所以,根据SQLite官网的说法,为了最大限度与其他关系型数据库兼容,SQLite对数据类型进行了很精巧的设计,就是让数据列具有类型亲和性的特性(其实博主更喜欢某些人翻译的 类型近似 这种叫法)。当我们创建表做字段类型声明的时候,实际上只是表明了该列具有的近似类型,在正式插入数据的时候,SQLite引擎才会基于该列的近似类型优先推荐使用 5 种存储类型中的哪一种来存储你的数据——注意是推荐,并不强制,也就是说只要你想要乱搞,SQLite也并不会限制你,SQLite是列自由的。所以,建表时字段类型声明的限制在SQLite中是被弱化了的。在最新的SQLite版本中,数据库的每一列都被定义为以下 5 种近似类型的一种,其定义和规则官网/Determination Of Column Affinity段中有做出解释:
- INTEGER 整型,如果声明的字段类型包含字符串“INT”(注意,SQLite大多数情况都不区分大小写,下同),那么该字段类型被分配为INTEGER 近似类型;
- TEXT 文本,如果为字段声明的类型中包含了'CHAR'、'CLOB'或'TEXT',该字段被分配为TEXT亲和性。比如'VARCHAR'包含了'CHAR',所以被分配为TEXT 近似类型;
- BLOB 无类型,如果为字段声明的类型中包含了'BLOB',或者没有为该字段声明类型,该字段被分配为BLOB 近似类型;
- REAL 浮点型,如果为字段声明的类型中包含了'REAL'、'FLOA'或'DOUB',则该字段被分配为REAL 近似类型;
- NUMERIC 数值型,除以上情况外的类型,则被分配为NUMERIC 近似类型。
官网上/Affinity Name Examples段落中有一份表格罗列了传统数据类型作为子集与上述 5 种近似类型如何对应的关系。基于上述近似类型的列叙,可以看出SQLite是从声明类型的字符串中去匹配列的近似类型来决定数据的存储类的,因此SQLite很有意思的是创建表时类型声明可以很随意,比如我可以声明一个不存在类型的字符串”chenbenbuyi”,但SQLite会根据规则自动识别为NUMERIC的近似类型,并据此近似类型存储我的数据
可能有些人对于上面的数据存储类还是有些疑惑——根据列的近似类型是如何推断数据存入后的最终类型呢?按博主的理解,SQLite在基于类型近似做数据存储转换的时候是有个推荐优先级的。比如上面的声明中,并不存在name声明的数据类型,所以数据库会自动匹配为NUMERIC的近似类型。当文本数据('23')被插入到该列时,如果转换操作不会导致数据信息丢失以及完全可逆,那么SQLite就会将该文本数据优先转换为INTEGER或REAL类型的数据(NULL或BLOB类型数据不做转换),转换不成功才会按照文本数据存储——这里能转换INTEGER成功;如果存储列的近似类型为TEXT,那么自然数据存储的优先存储类是TEXT,所以哪怕你存储的是整型数字,结果存的依然是TEXT。这就是类型近似在数据存储中的应用。
三.可视化工具
SQLiteSpy 下载地址:https://www.yunqa.de/delphi/products/sqlitespy/index
四.代码操作SQLite
using System; using System.Data.SQLite; namespace ConsoleAppTest.SQLite { /// <summary> /// SQLite 操作类 /// </summary> class SqLiteHelper { /// <summary> /// 数据库连接定义 /// </summary> private SQLiteConnection dbConnection; /// <summary> /// SQL命令定义 /// </summary> private SQLiteCommand dbCommand; /// <summary> /// 数据读取定义 /// </summary> private SQLiteDataReader dataReader; /// <summary> /// 构造函数 /// </summary> /// <param name="connectionString">连接SQLite库字符串</param> public SqLiteHelper(string connectionString) { try { dbConnection = new SQLiteConnection(connectionString); dbConnection.Open(); } catch (Exception e) { Log(e.ToString()); } } /// <summary> /// 执行SQL命令 /// </summary> /// <returns>The query.</returns> /// <param name="queryString">SQL命令字符串</param> public SQLiteDataReader ExecuteQuery(string queryString) { try { dbCommand = dbConnection.CreateCommand(); dbCommand.CommandText = queryString; dataReader = dbCommand.ExecuteReader(); } catch (Exception e) { Log(e.Message); } return dataReader; } /// <summary> /// 关闭数据库连接 /// </summary> public void CloseConnection() { //销毁Commend if (dbCommand != null) { dbCommand.Cancel(); } dbCommand = null; //销毁Reader if (dataReader != null) { dataReader.Close(); } dataReader = null; //销毁Connection if (dbConnection != null) { dbConnection.Close(); } dbConnection = null; } /// <summary> /// 读取整张数据表 /// </summary> /// <returns>The full table.</returns> /// <param name="tableName">数据表名称</param> public SQLiteDataReader ReadFullTable(string tableName) { string queryString = "SELECT * FROM " + tableName; return ExecuteQuery(queryString); } /// <summary> /// 向指定数据表中插入数据 /// </summary> /// <returns>The values.</returns> /// <param name="tableName">数据表名称</param> /// <param name="values">插入的数值</param> public SQLiteDataReader InsertValues(string tableName, string[] values) { //获取数据表中字段数目 int fieldCount = ReadFullTable(tableName).FieldCount; //当插入的数据长度不等于字段数目时引发异常 if (values.Length != fieldCount) { throw new SQLiteException("values.Length!=fieldCount"); } string queryString = "INSERT INTO " + tableName + " VALUES (" + "'" + values[0] + "'"; for (int i = 1; i < values.Length; i++) { queryString += ", " + "'" + values[i] + "'"; } queryString += " )"; return ExecuteQuery(queryString); } /// <summary> /// 更新指定数据表内的数据 /// </summary> /// <returns>The values.</returns> /// <param name="tableName">数据表名称</param> /// <param name="colNames">字段名</param> /// <param name="colValues">字段名对应的数据</param> /// <param name="key">关键字</param> /// <param name="value">关键字对应的值</param> /// <param name="operation">运算符:=,<,>,...,默认“=”</param> public SQLiteDataReader UpdateValues(string tableName, string[] colNames, string[] colValues, string key, string value, string operation = "=") { //当字段名称和字段数值不对应时引发异常 if (colNames.Length != colValues.Length) { throw new SQLiteException("colNames.Length!=colValues.Length"); } string queryString = "UPDATE " + tableName + " SET " + colNames[0] + "=" + "'" + colValues[0] + "'"; for (int i = 1; i < colValues.Length; i++) { queryString += ", " + colNames[i] + "=" + "'" + colValues[i] + "'"; } queryString += " WHERE " + key + operation + "'" + value + "'"; return ExecuteQuery(queryString); } /// <summary> /// 删除指定数据表内的数据 /// </summary> /// <returns>The values.</returns> /// <param name="tableName">数据表名称</param> /// <param name="colNames">字段名</param> /// <param name="colValues">字段名对应的数据</param> public SQLiteDataReader DeleteValuesOR(string tableName, string[] colNames, string[] colValues, string[] operations) { //当字段名称和字段数值不对应时引发异常 if (colNames.Length != colValues.Length || operations.Length != colNames.Length || operations.Length != colValues.Length) { throw new SQLiteException("colNames.Length!=colValues.Length || operations.Length!=colNames.Length || operations.Length!=colValues.Length"); } string queryString = "DELETE FROM " + tableName + " WHERE " + colNames[0] + operations[0] + "'" + colValues[0] + "'"; for (int i = 1; i < colValues.Length; i++) { queryString += "OR " + colNames[i] + operations[0] + "'" + colValues[i] + "'"; } return ExecuteQuery(queryString); } /// <summary> /// 删除指定数据表内的数据 /// </summary> /// <returns>The values.</returns> /// <param name="tableName">数据表名称</param> /// <param name="colNames">字段名</param> /// <param name="colValues">字段名对应的数据</param> public SQLiteDataReader DeleteValuesAND(string tableName, string[] colNames, string[] colValues, string[] operations) { //当字段名称和字段数值不对应时引发异常 if (colNames.Length != colValues.Length || operations.Length != colNames.Length || operations.Length != colValues.Length) { throw new SQLiteException("colNames.Length!=colValues.Length || operations.Length!=colNames.Length || operations.Length!=colValues.Length"); } string queryString = "DELETE FROM " + tableName + " WHERE " + colNames[0] + operations[0] + "'" + colValues[0] + "'"; for (int i = 1; i < colValues.Length; i++) { queryString += " AND " + colNames[i] + operations[i] + "'" + colValues[i] + "'"; } return ExecuteQuery(queryString); } /// <summary> /// 创建数据表 /// </summary> + /// <returns>The table.</returns> /// <param name="tableName">数据表名</param> /// <param name="colNames">字段名</param> /// <param name="colTypes">字段名类型</param> public SQLiteDataReader CreateTable(string tableName, string[] colNames, string[] colTypes) { string queryString = "CREATE TABLE IF NOT EXISTS " + tableName + "( " + colNames[0] + " " + colTypes[0]; for (int i = 1; i < colNames.Length; i++) { queryString += ", " + colNames[i] + " " + colTypes[i]; } queryString += " ) "; return ExecuteQuery(queryString); } /// <summary> /// Reads the table. /// </summary> /// <returns>The table.</returns> /// <param name="tableName">Table name.</param> /// <param name="items">Items.</param> /// <param name="colNames">Col names.</param> /// <param name="operations">Operations.</param> /// <param name="colValues">Col values.</param> public SQLiteDataReader ReadTable(string tableName, string[] items, string[] colNames, string[] operations, string[] colValues) { string queryString = "SELECT " + items[0]; for (int i = 1; i < items.Length; i++) { queryString += ", " + items[i]; } queryString += " FROM " + tableName + " WHERE " + colNames[0] + " " + operations[0] + " " + colValues[0]; for (int i = 0; i < colNames.Length; i++) { queryString += " AND " + colNames[i] + " " + operations[i] + " " + colValues[0] + " "; } return ExecuteQuery(queryString); } /// <summary> /// 本类log /// </summary> /// <param name="s"></param> static void Log(string s) { Console.WriteLine("class SqLiteHelper:::" + s); } } }
using ConsoleAppTest.SQLite; using System; using System.Data.SQLite; namespace ConsoleAppTest { class Program { class Test { private static SqLiteHelper sql; static void Main(string[] args) { SQLiteConnectionStringBuilder sqlitestring = new SQLiteConnectionStringBuilder(); sql = new SqLiteHelper(@"Data Source=C:\Users\DigiLinkPC3\Desktop\data.sqlite"); SQLiteDataReader reader = sql.ReadFullTable("province"); while (reader.Read()) { Console.WriteLine(reader.GetString(reader.GetOrdinal("code"))); Console.WriteLine(reader.GetString(reader.GetOrdinal("name"))); } Console.ReadLine(); } } } }