System.Data.SQLite 的一个优点是它是由 SQLite 团队开发的,该团队已声明长期致力于支持它。支持多种数据类型,比较旧
Microsoft.Data.Sqlite 的一个优点是它是由 Microsoft 开发的,并且可以假设它已通过 Entity Framework、.NET Core 等进行了良好的测试。只支持4中sqlite数据类类型,比较新
SQLite介绍
SQLite是一个类似于Access的单机版数据库管理系统,它将所有数据库的定义(包括定义、表、索引和数据本身)都保存在一个单一的文件中。并且,SQLite是一个用C实现的类库,它在内存消耗、文件体积、简单性方面都有不错的表现,如果数据在10W条以下,查询速度也是相当快的。
SQLite具有以下特征:
实现多数SQL92的标准,包括事务(原子性、一致性、隔离性和持久性)、触发器和大多数的复杂查询。
不对插入或者更新的数据进行类型检查,你可以将字符串插入到整数列中(这个可能让有些用户不太适应)。
支持Windows/Linux/Unix等主流系统,还支持嵌入式系统如Android或Windows Mobile。
System.Data.SQLite的前期准备
1、System.Data.SQLite 库下载,用于C#操作SQLite的dll文件。下载地址:http://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki
2、SQLite可视化工具下载,用于查看SQLite库数据表数据。下载地址:http://www.sqliteexpert.com/download.html
System.Data.SQLite通用类
可以分为以下几种情况:
1、创建数据库文件;
2、返回DataTable;
3、返回DataReader;
4、执行增删改,返回受影响的行数;
5、执行查询,返回第一行第一列(通常用于带有行函数的查询,如SUM/AVG/COUNT等);
6、返回库中所有的表;
因为在System.Data.SQLite中不存在存储过程,所以所有的操作都是基于文本的SQL语句,为了避免SQL注入,所以使用了参数化的SQL语句。
一、创建、删除SQLite数据库文件
为了增加格式的规范性,在项目的App.config文件的配置文本中添加数据库文件路径。
如下connectionStrings标签:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <connectionStrings> <add name="itcastCater" connectionString="data source=C:\Users\**\**\itcastCater.db;version=3;"/> </connectionStrings> </configuration>
运行方法 SQLiteConnection.open 就会创建个空的指定名字的数据库文件。
using System.Data.SQLite; using System.Configuration; //从app.config配置文本中读取连接字符串,获取数据库文件的绝对路径“Data Sounce” private static string connStr = ConfigurationManager.ConnectionStrings["itcastCater"].ConnectionString; static void Create() { SQLiteConnection cn = new SQLiteConnection(connStr); //按照路径创建数据库文件 cn.Open(); cn.Close(); }
由于数据库是文件类型的,直接用System.IO.File.Delete(string path) 方法来删除文件。
//删除数据库 static void Delete() { if (System.IO.File.Exists(connStr)) { System.IO.File.Delete(connStr); } }
二、创建、删除数据库表
创建表格要用到 SQL 命令。
建立一个表的顺序如下步骤(也可以用可视化工具 SQLiteExpert 来创建):
1、建立数据库连接;
2、打开数据库(如果没有数据库,Open 也会新创建一个数据库);
3、声明一个 SQLiteCommand 类,主要用来放置和运行 SQL 命令的;
4、把 SQLiteCommand 的 Connection 和 SQLiteConnection 联系起来;
5、往 SQLiteCommand 的 CommandText 输入 SQL 语句 CREATE TABLE 语句;
6、调用 SQLiteCommand.ExcuteNonQuery() 方法运行。
//创建数据库表 static void CreateTable() { SQLiteConnection cn = new SQLiteConnection(connStr);//建立数据库连接 if (cn.State!= System.Data.ConnectionState.Open) { cn.Open();//打开数据库 SQLiteCommand cmd = new SQLiteCommand(); cmd.Connection = cn;//把 SQLiteCommand的 Connection和SQLiteConnection 联系起来 cmd.CommandText = "CREATE TABLE IF NOT EXISTS t1(id varchar(4),score int)";//输入SQL语句 cmd.ExecuteNonQuery();//调用此方法运行 } cn.Close(); }
上面的SQL语句中存在一句“IF NOT EXISTS”,最好加上此句,如果不加此句且原数据库文件要是存在同名要创建的表,会出现报错。SQL语句大小写并没有规定,一般为了规范用大写。
删除表和建立表的步骤一样,只是把 SQL 语句改了而已:
//删除数据库表 static void DeleteTable() { SQLiteConnection cn = new SQLiteConnection(connStr); if (cn.State != System.Data.ConnectionState.Open) { cn.Open(); SQLiteCommand cmd = new SQLiteCommand(); cmd.Connection = cn; cmd.CommandText = "DROP TABLE IF EXISTS t1"; cmd.ExecuteNonQuery(); } cn.Close(); }
更改表名
用 SQL 语句 ALTER TABLE 把 t1 表名改成 t3
查询表的结构需要用到SQLite特殊的PRAGMA命令
以下为SQliteDataReader读出来的数据顺序列表:
代码如下:
//连接到数据库文件,使用PRAGMA命令准备 SQLiteConnection cn = new SQLiteConnection(connStr); cn.Open(); SQLiteCommand cmd = cn.CreateCommand(); cmd.CommandText= "PRAGMA table_info('t1')"; //方法一:用DataAdapter和DataTable类,调用方法为using System.Data SQLiteDataAdapter adapter = new SQLiteDataAdapter(cmd); DataTable table = new DataTable(); adapter.Fill(table); foreach(DataRow r in table.Rows) { Console.WriteLine($"{r["cid"]},{r["name"]},{r["type"]},{r["notnull"]},{r["dflt_value"]},{r["pk"]} "); } Console.WriteLine(); //方法二:用DataReader,这个效率高些 SQLiteDataReader reader = cmd.ExecuteReader(); while (reader.Read()) { for(int i = 0; i < reader.FieldCount; i++) { Console.Write($"{reader[i]},"); } Console.WriteLine(); } reader.Close();
如果不止一个表,要遍历所有表的结构如下,就要用到 SQLite 中的特殊表 sqlite_master
当 type = table 时,name 和 tbl_name 是一样的,其他比如 type =index 、view 之类时,tbl_name 才是表名
//遍历查询表结构 static void QueryAllTableInfo() { SQLiteConnection cn = new SQLiteConnection(connStr); if (cn.State != System.Data.ConnectionState.Open) { cn.Open(); SQLiteCommand cmd = new SQLiteCommand(); cmd.Connection = cn; cmd.CommandText = "SELECT name FROM sqlite_master WHERE TYPE='table' "; SQLiteDataReader sr = cmd.ExecuteReader(); List<string> tables = new List<string>(); while (sr.Read()) { tables.Add(sr.GetString(0)); } //datareader 必须要先关闭,否则 commandText 不能赋值 sr.Close(); foreach (var a in tables) { cmd.CommandText = $"PRAGMA TABLE_INFO({a})"; sr = cmd.ExecuteReader(); while (sr.Read()) { Console.WriteLine($"{sr[0]} {sr[1]} {sr[2]} {sr[3]}"); } sr.Close(); } } cn.Close(); }
增添列(字段)
用 SQL 命令 ALTER TABLE ,下例中为 t1 表添加一个名为 age,数据类型为 int 的新列:
cmd.CommandText = "ALTER TABLE t1 ADD COLUMN age int"; cmd.ExecuteNonQuery();
读取创建表的 SQL 语句
读取创建表时的 SQL 语句,在 SqliteExpert 中的 DDL 可以查看到。读取这个是为下面增添删除列做准备。
cmd.CommandText = "SELECT sql FROM sqlite_master WHERE TYPE='table'"; SQLiteDataReader sr = cmd.ExecuteReader(); while (sr.Read()) { Console.WriteLine(sr[0].ToString()); } sr.Close();
更改列名
SQLite 中并没有提供直接更改列名与删除列的命令,有两种方式,
第一种是:
1、把目标表改名;
2、创建一个带有新列名的新表;
3、把旧表数据拷贝至新表(记得要 Connection.BeginTransaction())。
第二种是:
更改 sqlite_master 里面的 schema,很容易损坏数据库。
依据是 SQLite 每次连接时,其实都是依据 schema 里面的每个表创建时的 CREATE TABLE 语句来动态建立 column 的信息的,只要 column 的数据类型和位置不变,更改 CREATE TABLE 语句就能更改 column 的信息。
此为以下两种方法:
方式一:
//更改列名1 //把旧表更名,建个带新列名的新表,拷贝数据 //params string[] 中:connStr 数据库名,1 表名,2 旧列名 3 新列名 static void RenameColumn1(params string[] str) { SQLiteConnection cn = new SQLiteConnection(connStr); cn.Open(); SQLiteCommand cmd = new SQLiteCommand(); cmd.Connection = cn; //取得str[1]表名的表的建表SQL语句 cmd.CommandText = "SELECT name,sql FROM sqlite_master WHERE TYPE='table' ORDER BY name"; SQLiteDataReader sr = cmd.ExecuteReader(); string _sql = ""; while (sr.Read()) { if (string.Compare(sr.GetString(connStr), str[1], true) == 0) { _sql = sr.GetString(1); break; } } sr.Close(); //更改旧表名为 带 _old string _old = str[1] + "_old"; cmd.CommandText = $"ALTER TABLE {str[1]} RENAME TO {_old}"; cmd.ExecuteNonQuery(); //建立新表,假设输入的旧列名和表中的列名大小写等完全一致,不写能容错的了 _sql = _sql.Replace(str[2],str[3]); cmd.CommandText = _sql; cmd.ExecuteNonQuery(); //拷贝数据 using (SQLiteTransaction tr = cn.BeginTransaction()) { cmd.CommandText = $"INSERT INTO {str[1]} SELECT * FROM {_old}"; cmd.ExecuteNonQuery(); cmd.CommandText = $"DROP TABLE {_old}"; cmd.ExecuteNonQuery(); tr.Commit(); } cn.Close(); }
方式二:
//更改列名2,改写schema里建表时的sql语句 //原理:sqlite 每次打开的时候,都是依据建表时的sql语句来动态建立column的信息的 static void RenameColumn2(params string[] str) { SQLiteConnection cn = new SQLiteConnection(connStr); cn.Open(); SQLiteCommand cmd = new SQLiteCommand(); cmd.Connection = cn; //取得str[1]表名的表的建表SQL语句 cmd.CommandText = $"SELECT sql FROM sqlite_master WHERE TYPE='table' AND name='{str[1]}'"; SQLiteDataReader sr = cmd.ExecuteReader(); sr.Read(); string _sql = sr.GetString(connStr); sr.Close(); //注意单引号 ' _sql =$"UPDATE sqlite_master SET sql='{_sql.Replace(str[2],str[3])}' WHERE name= '{str[1]}' "; //设置 writable_schema 为 true,准备改写schema cmd.CommandText = "pragma writable_schema=1"; cmd.ExecuteNonQuery(); cmd.CommandText = _sql; cmd.ExecuteNonQuery(); //设置 writable_schema 为 false。 cmd.CommandText = "pragma writable_schema=0"; cmd.ExecuteNonQuery(); cn.Close(); }
删除列
SQLite 也没有提供删除列的命令。和上面一样,也是两种方式。
其一,把目标表改名,建立没有要删除列(字段)的新表,然后把旧表的数据拷贝至新表。
其二,直接修改 schema 中建表的 SQL 语句。
其中最主要的是要把建表的列的所有信息都保存下来,比如索引、缺省值之类的。
示例使用第二种方式:
//删除列2,string[] ,connStr 数据库路径,1 表名,2 要删除的列名 static void DeleteColumn2(params string[] str) { SQLiteConnection cn = new SQLiteConnection(connStr); cn.Open(); SQLiteCommand cmd = new SQLiteCommand(); cmd.Connection = cn; //取得str[1]表名的表的建表SQL语句 cmd.CommandText = $"SELECT sql FROM sqlite_master WHERE TYPE='table' AND name='{str[1]}'"; SQLiteDataReader sr = cmd.ExecuteReader(); sr.Read(); string _sql = sr.GetString(connStr); sr.Close(); //取得列的定义 //C#7.0的新特征,Tuple<>的语法糖,需要 NuGet install-package system.valuetuple List<(string name, string define)> list = GetColumnDefine(_sql); //取得要删除列的序号 int _index = list.IndexOf(list.Where(x => x.name == str[2]).First()); //建立新的sql语句 StringBuilder sb = new StringBuilder(); sb.Append($"CREATE TABLE {str[1]}("); for (int i = 0; i < list.Count; i++) { if (i != _index)//除了要删除的列,其他列复制出来。 { sb.Append($"{list[i].define},"); } } sb.Remove(sb.Length - 1, 1); sb.Append(")"); //改写schema _sql = $"UPDATE sqlite_master SET sql='{sb.ToString()}' WHERE name='{str[1]}'"; //设置 writable_schema 为 true,准备改写schema cmd.CommandText = "pragma writable_schema=1"; cmd.ExecuteNonQuery(); cmd.CommandText = _sql; cmd.ExecuteNonQuery(); //设置 writable_schema 为 false。 cmd.CommandText = "pragma writable_schema=0"; cmd.ExecuteNonQuery(); cn.Close(); }
取得列的定义
//取得列的定义 static List<(string, string)> GetColumnDefine(string SqlStr) { int n = 0; int _start = 0; string _columnStr = ""; for (int i = 0; i < SqlStr.Length; i++) { if (SqlStr[i] == '(') { if (n++ == 0) { _start = i; } } else { if (SqlStr[i] == ')') { if (--n == 0) { _columnStr = SqlStr.Substring(_start + 1, i - _start - 1); break; } } } } string[] ss = _columnStr.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); //C#7.0的新特征,Tuple<>的语法糖,需要 NuGet install-package system.valuetuple List<(string name, string define)> reslut = new List<(string name, string define)>(); foreach (var a in ss) { string s = a.Trim(); n = 0; for (int i = 0; i < s.Length; i++) { if (s[i] == ' ') { reslut.Add((s.Substring(0, i), s)); break; } } } return reslut; }
插入数据
插入数据主要是用 SQL 语句 INSERT INTO
示例1(简单插入):
cmd.CommandText = "INSERT INTO t1 VALUES('99999',11)"; cmd.ExecuteNonQuery();
示例2(变量插入,要引用 System.Data):
using System.Data; string s = "123456"; int n = 10; cmd.CommandText = "INSERT INTO t1(id,age) VALUES(@id,@age)"; cmd.Parameters.Add("id", DbType.String).Value = s; cmd.Parameters.Add("age", DbType.Int32).Value = n; cmd.ExecuteNonQuery();
替换数据
SQL 命令 REPLACE INTO。
下面示例中, t1 表中 id 为主键,相同主键值的就 UPDATE,否则就 INSERT
string s = "123456"; int n = 30; cmd.CommandText = "REPLACE INTO t1(id,age) VALUES(@id,@age)"; cmd.Parameters.Add("id", DbType.String).Value = s; cmd.Parameters.Add("age", DbType.Int32).Value = n; cmd.ExecuteNonQuery();
更新数据
SQL 命令 UPDATE tablename SET column1=value,column2=value… WHERE 条件
string s = "333444"; int n = 30; cmd.CommandText = "UPDATE t1 SET id=@id,age=@age WHERE id='0123456789'"; cmd.Parameters.Add("id", DbType.String).Value = s; cmd.Parameters.Add("age", DbType.Int32).Value = n; cmd.ExecuteNonQuery();
删除数据
SQL 命令:DELETE FROM tablename WHERE 条件
cmd.CommandText = "DELETE FROM t1 WHERE id='99999'"; cmd.ExecuteNonQuery();
查询数据
SQL 命令:SELETE 语句,具体的请参考 SQL 教程。
//查询第1条记录,这个并不保险,rowid 并不是连续的,只是和当时插入有关 cmd.CommandText = "SELECT * FROM t1 WHERE rowid=1"; SQLiteDataReader sr = cmd.ExecuteReader(); while (sr.Read()) { Console.WriteLine($"{sr.GetString(0)} {sr.GetInt32(1).ToString()}"); } sr.Close(); //运行以下的就能知道 rowid 并不能代表 行数 cmd.CommandText = "SELECT rowid FROM t1 "; sr = cmd.ExecuteReader(); while (sr.Read()) { Console.WriteLine($"{sr.GetString(0)} {sr.GetInt32(1).ToString()}"); } sr.Close();
获取查询数据的行数(多少条记录)
从上面示例中我们得知,rowid 并不是正确的行数(记录数),而是 INSERT 的时候的B-Tree 的相关数。
如要知道表中的行数(记录数),要如下:
cmd.CommandText = "SELECT count(*) FROM t1"; sr = cmd.ExecuteReader(); sr.Read(); Console.WriteLine(sr.GetInt32(0).ToString()); sr.Close();
原文链接:https://blog.csdn.net/dfdfd