C# SQLite高级功能示例
目录
1 主要功能
2 程序结构和流程
3 详细实现说明
3.1 基础设置
3.2 事务演示
3.3 索引演示
3.4 视图演示
3.5 触发器演示
3.6 全文搜索演示
3.7 窗口函数演示
3.8 外键约束演示
4 高级功能示例
5 单个方法详细介绍
5.1 SetupExampleData()方法
5.2 UseTransactions() 方法
5.3 UseIndexes() 方法
5.4 UseViews() 方法
5.5 UseTriggers() 方法
5.6 UseFTS() 方法
5.7 UseWindowFunctions() 方法
5.8 UseForeignKeys() 方法
5.9 辅助方法
6 使用注意事项
7 数据类型映射
本文给出的C#示例程序,展示了SQLite数据库的各种高级功能使用方法。通过命名空间SQLiteAdvancedDemo
下的Program
类实现。
1 主要功能
这个程序演示了以下SQLite高级特性:
- 事务管理:展示如何开始、提交和回滚事务
- 索引使用:创建和使用索引来优化查询性能
- 视图:创建和使用SQL视图简化复杂查询
- 触发器:实现数据变更时自动执行的操作
- 全文搜索(FTS5):演示SQLite的全文搜索功能
- 窗口函数:使用高级SQL分析功能
- 外键约束:展示如何正确处理表之间的关系
2 程序结构和流程
-
初始化和环境设置:
- 定义数据库文件名和连接字符串
- 检查数据库文件是否存在,不存在则创建
- 调用
SetupExampleData()
方法创建示例数据
-
依次演示各个高级功能:
- 调用各个演示方法:
UseTransactions()
,UseIndexes()
,UseViews()
,UseTriggers()
,UseFTS()
,UseWindowFunctions()
,UseForeignKeys()
- 调用各个演示方法:
-
每个演示方法的通用结构:
- 打印功能标题
- 创建数据库连接
- 执行相关SQL命令
- 显示执行结果
- 捕获和处理可能的异常
-
辅助方法:
ExecuteNonQuery
: 执行非查询SQL语句DisplayQueryResults
: 格式化显示查询结果
3 详细实现说明
3.1 基础设置
- 在
Main
方法中创建数据库文件(如不存在)并处理可能的异常SetupExampleData
方法中创建了两个表(部门和员工),并插入了示例数据- 启用外键约束(
PRAGMA foreign_keys = ON
)
3.2 事务演示
- 展示如何使用事务包装多个SQL操作
- 演示提交和回滚机制,确保数据一致性
3.3 索引演示
- 创建索引优化查询
- 使用
EXPLAIN QUERY PLAN
展示查询计划- 展示带索引的查询执行
3.4 视图演示
- 创建表连接的视图简化复杂查询
- 查询视图以获取员工详细信息
3.5 触发器演示
- 创建审计日志表
- 实现插入和更新触发器
- 通过操作员工表触发这些触发器
- 显示审计日志记录
3.6 全文搜索演示
- 创建FTS5虚拟表用于文本搜索
- 插入示例文档数据
- 演示基本搜索和带排名的搜索
3.7 窗口函数演示
- 使用窗口函数进行分析计算
- 展示分区排名、平均值计算等高级功能
3.8 外键约束演示
- 展示违反外键约束时的错误处理
- 演示正确的删除顺序
4 高级功能示例
using System;
using System.Data.SQLite;
using System.IO;
using System.Collections.Generic;namespace SQLiteAdvancedDemo
{class Program{private static string dbFile = "advanced_demo.db";private static string connectionString = $"Data Source={dbFile};Version=3;";static void Main(string[] args){try{// 确保数据库文件存在if (!File.Exists(dbFile)){SQLiteConnection.CreateFile(dbFile);}// 创建示例数据SetupExampleData();// 演示高级功能UseTransactions();UseIndexes();UseViews();UseTriggers();UseFTS();UseWindowFunctions();UseForeignKeys();Console.WriteLine("所有高级功能演示已完成!");}catch (Exception ex){Console.WriteLine($"发生错误: {ex.Message}");}Console.ReadKey();}// 设置示例数据static void SetupExampleData(){using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 启用外键约束ExecuteNonQuery(connection, "PRAGMA foreign_keys = ON;");// 创建示例表ExecuteNonQuery(connection, @"CREATE TABLE IF NOT EXISTS Departments (DeptId INTEGER PRIMARY KEY,Name TEXT NOT NULL)");ExecuteNonQuery(connection, @"CREATE TABLE IF NOT EXISTS Employees (EmpId INTEGER PRIMARY KEY,Name TEXT NOT NULL,DeptId INTEGER,Salary REAL,HireDate TEXT,FOREIGN KEY (DeptId) REFERENCES Departments(DeptId))");// 清空表,以便重复运行ExecuteNonQuery(connection, "DELETE FROM Employees");ExecuteNonQuery(connection, "DELETE FROM Departments");// 添加部门数据ExecuteNonQuery(connection, @"INSERT INTO Departments (DeptId, Name) VALUES(1, '研发部'),(2, '市场部'),(3, '财务部')");// 添加员工数据ExecuteNonQuery(connection, @"INSERT INTO Employees (Name, DeptId, Salary, HireDate) VALUES('张三', 1, 15000, '2020-01-15'),('李四', 1, 12000, '2020-03-20'),('王五', 2, 10000, '2021-05-10'),('赵六', 2, 9500, '2021-06-01'),('钱七', 3, 11000, '2022-02-15')");}}// 使用事务static void UseTransactions(){Console.WriteLine("\n=== 事务演示 ===");using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 开始事务using (var transaction = connection.BeginTransaction()){try{// 执行多个操作ExecuteNonQuery(connection, "INSERT INTO Departments (DeptId, Name) VALUES (4, '人力资源部')", transaction);ExecuteNonQuery(connection, "UPDATE Employees SET Salary = Salary * 1.1 WHERE DeptId = 1", transaction);// 假设这里有条件判断是否提交bool shouldCommit = true;if (shouldCommit){transaction.Commit();Console.WriteLine("事务已提交。");}else{transaction.Rollback();Console.WriteLine("事务已回滚。");}}catch (Exception ex){transaction.Rollback();Console.WriteLine($"发生错误,事务已回滚: {ex.Message}");}}// 显示结果Console.WriteLine("部门列表:");DisplayQueryResults(connection, "SELECT * FROM Departments");}}// 使用索引static void UseIndexes(){Console.WriteLine("\n=== 索引演示 ===");using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 创建索引ExecuteNonQuery(connection, "CREATE INDEX IF NOT EXISTS idx_employees_dept ON Employees(DeptId)");ExecuteNonQuery(connection, "CREATE INDEX IF NOT EXISTS idx_employees_salary ON Employees(Salary DESC)");// 使用EXPLAIN QUERY PLAN查看查询计划Console.WriteLine("查询计划分析:");DisplayQueryResults(connection, "EXPLAIN QUERY PLAN SELECT * FROM Employees WHERE DeptId = 1 AND Salary > 10000");// 使用索引的查询Console.WriteLine("\n使用索引的查询结果:");DisplayQueryResults(connection, "SELECT * FROM Employees WHERE DeptId = 1 AND Salary > 10000");}}// 使用视图static void UseViews(){Console.WriteLine("\n=== 视图演示 ===");using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 创建视图ExecuteNonQuery(connection, @"CREATE VIEW IF NOT EXISTS EmployeeDetails ASSELECT e.EmpId, e.Name, e.Salary, d.Name AS Department, e.HireDateFROM Employees eJOIN Departments d ON e.DeptId = d.DeptId");// 使用视图Console.WriteLine("视图查询结果:");DisplayQueryResults(connection, "SELECT * FROM EmployeeDetails ORDER BY Salary DESC");}}// 使用触发器static void UseTriggers(){Console.WriteLine("\n=== 触发器演示 ===");using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 创建审计日志表ExecuteNonQuery(connection, @"CREATE TABLE IF NOT EXISTS AuditLog (LogId INTEGER PRIMARY KEY,TableName TEXT,Operation TEXT,Timestamp TEXT DEFAULT CURRENT_TIMESTAMP)");// 创建触发器ExecuteNonQuery(connection, @"CREATE TRIGGER IF NOT EXISTS after_employee_insertAFTER INSERT ON EmployeesBEGININSERT INTO AuditLog (TableName, Operation)VALUES ('Employees', 'INSERT');END");ExecuteNonQuery(connection, @"CREATE TRIGGER IF NOT EXISTS after_employee_updateAFTER UPDATE ON EmployeesBEGININSERT INTO AuditLog (TableName, Operation)VALUES ('Employees', 'UPDATE');END");// 触发触发器ExecuteNonQuery(connection, "INSERT INTO Employees (Name, DeptId, Salary) VALUES ('新员工', 2, 8000)");ExecuteNonQuery(connection, "UPDATE Employees SET Salary = 8500 WHERE Name = '新员工'");// 显示审计日志Console.WriteLine("审计日志:");DisplayQueryResults(connection, "SELECT * FROM AuditLog");}}// 使用全文搜索static void UseFTS(){Console.WriteLine("\n=== 全文搜索(FTS5)演示 ===");using (var connection = new SQLiteConnection(connectionString)){connection.Open();try{// 创建FTS5虚拟表ExecuteNonQuery(connection, @"CREATE VIRTUAL TABLE IF NOT EXISTS DocumentsFTS USING fts5(Title, Content, Author,tokenize = 'porter unicode61')");// 插入示例文档ExecuteNonQuery(connection, @"INSERT INTO DocumentsFTS (Title, Content, Author) VALUES('SQLite教程', '这是一份关于SQLite数据库的完整教程,包括基本操作和高级功能。', '张三'),('C#编程入门', 'C#是微软开发的面向对象编程语言,用于.NET平台开发。', '李四'),('数据库设计', '良好的数据库设计遵循范式和性能优化原则。SQLite是轻量级数据库的首选。', '王五')");// 执行全文搜索Console.WriteLine("搜索'SQLite'的结果:");DisplayQueryResults(connection, "SELECT * FROM DocumentsFTS WHERE DocumentsFTS MATCH 'sqlite'");// 带排名的搜索Console.WriteLine("\n带排名的搜索结果:");DisplayQueryResults(connection, @"SELECT Title, Author, rankFROM DocumentsFTSWHERE DocumentsFTS MATCH 'sqlite OR 数据库'ORDER BY rank");}catch (SQLiteException ex){Console.WriteLine($"FTS5可能未启用: {ex.Message}");Console.WriteLine("注意: 使用FTS5需要SQLite编译时启用该扩展。");}}}// 使用窗口函数static void UseWindowFunctions(){Console.WriteLine("\n=== 窗口函数演示 ===");using (var connection = new SQLiteConnection(connectionString)){connection.Open();try{// 使用窗口函数string windowQuery = @"SELECT e.Name,d.Name AS Department,e.Salary,RANK() OVER (PARTITION BY e.DeptId ORDER BY e.Salary DESC) AS DeptRank,AVG(e.Salary) OVER (PARTITION BY e.DeptId) AS AvgDeptSalary,e.Salary - AVG(e.Salary) OVER (PARTITION BY e.DeptId) AS DiffFromAvgFROM Employees eJOIN Departments d ON e.DeptId = d.DeptIdORDER BY d.Name, DeptRank";Console.WriteLine("窗口函数查询结果:");DisplayQueryResults(connection, windowQuery);}catch (SQLiteException ex){Console.WriteLine($"窗口函数可能未支持: {ex.Message}");Console.WriteLine("注意: 窗口函数需要SQLite 3.25+版本。");}}}// 使用外键约束static void UseForeignKeys(){Console.WriteLine("\n=== 外键约束演示 ===");using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 确保外键约束已开启ExecuteNonQuery(connection, "PRAGMA foreign_keys = ON;");// 尝试删除有关联记录的部门try{ExecuteNonQuery(connection, "DELETE FROM Departments WHERE DeptId = 1");Console.WriteLine("删除成功"); // 不应该执行到此处}catch (SQLiteException ex){Console.WriteLine($"预期的外键约束错误: {ex.Message}");}// 正确的删除顺序 - 先删除关联记录Console.WriteLine("\n按正确顺序删除:");ExecuteNonQuery(connection, "BEGIN TRANSACTION");ExecuteNonQuery(connection, "DELETE FROM Employees WHERE DeptId = 3");ExecuteNonQuery(connection, "DELETE FROM Departments WHERE DeptId = 3");ExecuteNonQuery(connection, "COMMIT");Console.WriteLine("部门列表:");DisplayQueryResults(connection, "SELECT * FROM Departments");}}// 执行非查询语句的辅助方法static void ExecuteNonQuery(SQLiteConnection connection, string sql, SQLiteTransaction transaction = null){using (var command = new SQLiteCommand(sql, connection, transaction)){command.ExecuteNonQuery();}}// 显示查询结果的辅助方法static void DisplayQueryResults(SQLiteConnection connection, string sql){using (var command = new SQLiteCommand(sql, connection)){using (var reader = command.ExecuteReader()){// 获取列名List<string> columns = new List<string>();for (int i = 0; i < reader.FieldCount; i++){columns.Add(reader.GetName(i));}// 输出列名Console.WriteLine(string.Join(" | ", columns));Console.WriteLine(new string('-', columns.Count * 15));// 输出行while (reader.Read()){List<string> values = new List<string>();for (int i = 0; i < reader.FieldCount; i++){values.Add(reader[i]?.ToString() ?? "NULL");}Console.WriteLine(string.Join(" | ", values));}}}}}
}
5 单个方法详细介绍
5.1 SetupExampleData()方法
static void SetupExampleData()
{using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 启用外键约束ExecuteNonQuery(connection, "PRAGMA foreign_keys = ON;");// 创建示例表ExecuteNonQuery(connection, @"CREATE TABLE IF NOT EXISTS Departments (DeptId INTEGER PRIMARY KEY,Name TEXT NOT NULL)");ExecuteNonQuery(connection, @"CREATE TABLE IF NOT EXISTS Employees (EmpId INTEGER PRIMARY KEY,Name TEXT NOT NULL,DeptId INTEGER,Salary REAL,HireDate TEXT,FOREIGN KEY (DeptId) REFERENCES Departments(DeptId))");// 清空表,以便重复运行ExecuteNonQuery(connection, "DELETE FROM Employees");ExecuteNonQuery(connection, "DELETE FROM Departments");// 添加部门数据ExecuteNonQuery(connection, @"INSERT INTO Departments (DeptId, Name) VALUES(1, '研发部'),(2, '市场部'),(3, '财务部')");// 添加员工数据ExecuteNonQuery(connection, @"INSERT INTO Employees (Name, DeptId, Salary, HireDate) VALUES('张三', 1, 15000, '2020-01-15'),('李四', 1, 12000, '2020-03-20'),('王五', 2, 10000, '2021-05-10'),('赵六', 2, 9500, '2021-06-01'),('钱七', 3, 11000, '2022-02-15')");}
}
解析:
连接管理:使用
using
语句确保连接被正确关闭和资源释放,这是C#中处理IDisposable资源的最佳实践。启用外键约束:
PRAGMA foreign_keys = ON;
- SQLite默认不启用外键约束,这行代码显式启用它,允许表之间建立引用完整性关系。创建表结构:
Departments
表:有DeptId
(主键)和Name
字段
Employees
表:有EmpId
(主键)、Name
、DeptId
(外键)、Salary
和HireDate
字段
FOREIGN KEY
约束:确保Employees
表中的DeptId
引用Departments
表中存在的DeptId
清空表数据:
DELETE FROM
语句确保每次运行程序时表是空的,避免重复数据。从有外键约束的Employees
表先删除是必要的,以避免违反引用完整性。插入示例数据:向两个表添加示例数据。注意:先插入
Departments
数据,再插入Employees
数据,这是因为Employees
表的外键引用了Departments
表。
5.2 UseTransactions() 方法
static void UseTransactions()
{Console.WriteLine("\n=== 事务演示 ===");using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 开始事务using (var transaction = connection.BeginTransaction()){try{// 执行多个操作ExecuteNonQuery(connection, "INSERT INTO Departments (DeptId, Name) VALUES (4, '人力资源部')", transaction);ExecuteNonQuery(connection, "UPDATE Employees SET Salary = Salary * 1.1 WHERE DeptId = 1", transaction);// 假设这里有条件判断是否提交bool shouldCommit = true;if (shouldCommit){transaction.Commit();Console.WriteLine("事务已提交。");}else{transaction.Rollback();Console.WriteLine("事务已回滚。");}}catch (Exception ex){transaction.Rollback();Console.WriteLine($"发生错误,事务已回滚: {ex.Message}");}}// 显示结果Console.WriteLine("部门列表:");DisplayQueryResults(connection, "SELECT * FROM Departments");}
}
解析:
事务管理:
connection.BeginTransaction()
创建一个SQLite事务,也用using
语句确保适当关闭。错误处理:
try...catch
块捕获任何可能发生的异常,如果有错误发生,会回滚事务。执行事务操作:在事务中执行两个操作:
插入新部门"人力资源部"
给所有研发部(DeptId=1)的员工加薪10%
提交或回滚:根据条件决定提交或回滚事务。在这个示例中,
shouldCommit
设为true,所以事务会被提交。在实际应用中,这可能基于业务逻辑或验证结果。事务的好处:
原子性:多个操作要么全部执行,要么全部不执行
一致性:数据库从一个一致状态转换到另一个一致状态
隔离性:事务操作相互隔离
持久性:一旦提交,更改永久保存
5.3 UseIndexes() 方法
static void UseIndexes()
{Console.WriteLine("\n=== 索引演示 ===");using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 创建索引ExecuteNonQuery(connection, "CREATE INDEX IF NOT EXISTS idx_employees_dept ON Employees(DeptId)");ExecuteNonQuery(connection, "CREATE INDEX IF NOT EXISTS idx_employees_salary ON Employees(Salary DESC)");// 使用EXPLAIN QUERY PLAN查看查询计划Console.WriteLine("查询计划分析:");DisplayQueryResults(connection, "EXPLAIN QUERY PLAN SELECT * FROM Employees WHERE DeptId = 1 AND Salary > 10000");// 使用索引的查询Console.WriteLine("\n使用索引的查询结果:");DisplayQueryResults(connection, "SELECT * FROM Employees WHERE DeptId = 1 AND Salary > 10000");}
}
解析:
创建索引:
idx_employees_dept
:在Employees
表的DeptId
列上创建索引,优化按部门查询
idx_employees_salary
:在Employees
表的Salary
列上创建降序索引,优化薪资排序和范围查询查询计划分析:
EXPLAIN QUERY PLAN
显示SQLite将如何执行查询,包括是否使用索引、使用哪些索引、按什么顺序扫描表等信息。这对于优化查询性能非常有用。执行索引优化查询:执行一个同时使用
DeptId
和Salary
条件的查询,应该能从创建的索引中受益。索引优化的好处:
加速数据检索操作
减少磁盘I/O
帮助执行ORDER BY操作
加速JOIN操作
注意事项:实际应用中需要平衡读写性能,因为索引会减慢写入速度。
5.4 UseViews() 方法
static void UseViews()
{Console.WriteLine("\n=== 视图演示 ===");using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 创建视图ExecuteNonQuery(connection, @"CREATE VIEW IF NOT EXISTS EmployeeDetails ASSELECT e.EmpId, e.Name, e.Salary, d.Name AS Department, e.HireDateFROM Employees eJOIN Departments d ON e.DeptId = d.DeptId");// 使用视图Console.WriteLine("视图查询结果:");DisplayQueryResults(connection, "SELECT * FROM EmployeeDetails ORDER BY Salary DESC");}
}
解析:
创建视图:
CREATE VIEW
语句创建了一个名为EmployeeDetails
的视图,这个视图连接Employees
和Departments
表,并选择特定的列。视图提供了一个预定义的查询,简化了复杂查询的使用。视图功能:视图将
Employees
和Departments
表联接在一起,显示员工ID、姓名、薪资、所属部门名称和入职日期。查询视图:通过简单的
SELECT * FROM EmployeeDetails
查询视图,就像查询普通表一样,但实际上视图封装了复杂的JOIN查询。视图的好处:
简化复杂查询
提供抽象和封装
限制对某些列的访问,增强安全性
确保数据查询的一致性
注意事项:SQLite视图是只读的,不支持可更新视图。
5.5 UseTriggers() 方法
static void UseTriggers()
{Console.WriteLine("\n=== 触发器演示 ===");using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 创建审计日志表ExecuteNonQuery(connection, @"CREATE TABLE IF NOT EXISTS AuditLog (LogId INTEGER PRIMARY KEY,TableName TEXT,Operation TEXT,Timestamp TEXT DEFAULT CURRENT_TIMESTAMP)");// 创建触发器ExecuteNonQuery(connection, @"CREATE TRIGGER IF NOT EXISTS after_employee_insertAFTER INSERT ON EmployeesBEGININSERT INTO AuditLog (TableName, Operation)VALUES ('Employees', 'INSERT');END");ExecuteNonQuery(connection, @"CREATE TRIGGER IF NOT EXISTS after_employee_updateAFTER UPDATE ON EmployeesBEGININSERT INTO AuditLog (TableName, Operation)VALUES ('Employees', 'UPDATE');END");// 触发触发器ExecuteNonQuery(connection, "INSERT INTO Employees (Name, DeptId, Salary) VALUES ('新员工', 2, 8000)");ExecuteNonQuery(connection, "UPDATE Employees SET Salary = 8500 WHERE Name = '新员工'");// 显示审计日志Console.WriteLine("审计日志:");DisplayQueryResults(connection, "SELECT * FROM AuditLog");}
}
解析:
创建审计日志表:
AuditLog
表用于记录数据变更操作,包含字段:LogId、TableName(被修改的表)、Operation(执行的操作)和Timestamp(时间戳)。创建触发器:
after_employee_insert
:在Employees
表插入操作后触发,记录到审计日志
after_employee_update
:在Employees
表更新操作后触发,记录到审计日志触发操作:
向
Employees
表插入新员工,触发after_employee_insert
触发器更新新员工的薪资,触发
after_employee_update
触发器查看结果:查询
AuditLog
表,显示记录的操作日志。触发器的应用场景:
审计跟踪
业务规则验证
自动维护派生数据
级联更新或删除
强制数据一致性
注意事项:触发器会增加数据库复杂性,可能影响性能,特别是在大量数据操作时。
5.6 UseFTS() 方法
static void UseFTS()
{Console.WriteLine("\n=== 全文搜索(FTS5)演示 ===");using (var connection = new SQLiteConnection(connectionString)){connection.Open();try{// 创建FTS5虚拟表ExecuteNonQuery(connection, @"CREATE VIRTUAL TABLE IF NOT EXISTS DocumentsFTS USING fts5(Title, Content, Author,tokenize = 'porter unicode61')");// 插入示例文档ExecuteNonQuery(connection, @"INSERT INTO DocumentsFTS (Title, Content, Author) VALUES('SQLite教程', '这是一份关于SQLite数据库的完整教程,包括基本操作和高级功能。', '张三'),('C#编程入门', 'C#是微软开发的面向对象编程语言,用于.NET平台开发。', '李四'),('数据库设计', '良好的数据库设计遵循范式和性能优化原则。SQLite是轻量级数据库的首选。', '王五')");// 执行全文搜索Console.WriteLine("搜索'SQLite'的结果:");DisplayQueryResults(connection, "SELECT * FROM DocumentsFTS WHERE DocumentsFTS MATCH 'sqlite'");// 带排名的搜索Console.WriteLine("\n带排名的搜索结果:");DisplayQueryResults(connection, @"SELECT Title, Author, rankFROM DocumentsFTSWHERE DocumentsFTS MATCH 'sqlite OR 数据库'ORDER BY rank");}catch (SQLiteException ex){Console.WriteLine($"FTS5可能未启用: {ex.Message}");Console.WriteLine("注意: 使用FTS5需要SQLite编译时启用该扩展。");}}
}
解析:
创建FTS5虚拟表:
CREATE VIRTUAL TABLE ... USING fts5
创建全文搜索表,FTS5是SQLite中最新的全文搜索引擎。
tokenize = 'porter unicode61'
:指定使用Porter词干提取算法和unicode61标记器,以支持中文等非ASCII字符。插入示例文档:向
DocumentsFTS
表中插入三条文档记录,每条记录有标题、内容和作者。执行基本全文搜索:使用
MATCH
操作符搜索含有"sqlite"的文档,FTS支持不区分大小写的搜索。带排名的搜索:使用FTS的
rank
函数对搜索结果进行排序,包含更多搜索词或出现频率更高的文档排名更高。错误处理:捕获可能的异常,因为FTS5是一个扩展,不是所有SQLite编译版本都默认启用。
FTS的优势:
快速文本搜索
支持复杂检索语法(短语、AND/OR、接近度等)
支持结果排名
适用于全文内容检索应用
应用场景:文档搜索、应用内搜索、日志分析等。
5.7 UseWindowFunctions() 方法
static void UseWindowFunctions()
{Console.WriteLine("\n=== 窗口函数演示 ===");using (var connection = new SQLiteConnection(connectionString)){connection.Open();try{// 使用窗口函数string windowQuery = @"SELECT e.Name,d.Name AS Department,e.Salary,RANK() OVER (PARTITION BY e.DeptId ORDER BY e.Salary DESC) AS DeptRank,AVG(e.Salary) OVER (PARTITION BY e.DeptId) AS AvgDeptSalary,e.Salary - AVG(e.Salary) OVER (PARTITION BY e.DeptId) AS DiffFromAvgFROM Employees eJOIN Departments d ON e.DeptId = d.DeptIdORDER BY d.Name, DeptRank";Console.WriteLine("窗口函数查询结果:");DisplayQueryResults(connection, windowQuery);}catch (SQLiteException ex){Console.WriteLine($"窗口函数可能未支持: {ex.Message}");Console.WriteLine("注意: 窗口函数需要SQLite 3.25+版本。");}}
}
解析:
窗口函数查询:这个查询使用多种窗口函数来计算员工数据的聚合和排名:
RANK() OVER (PARTITION BY e.DeptId ORDER BY e.Salary DESC) AS DeptRank
:计算每个员工在其部门内按薪资排名
AVG(e.Salary) OVER (PARTITION BY e.DeptId) AS AvgDeptSalary
:计算每个部门的平均薪资
e.Salary - AVG(e.Salary) OVER (PARTITION BY e.DeptId) AS DiffFromAvg
:计算每个员工薪资与部门平均值的差异PARTITION BY:按部门分组计算,每个部门单独计算聚合值。
ORDER BY:在窗口函数中指定排序方式,这里按薪资降序排列。
错误处理:捕获可能的异常,因为窗口函数需要SQLite 3.25+版本支持。
窗口函数的优势:
在保持原始行的情况下执行聚合计算
避免复杂的自连接或子查询
实现排名、移动平均、累计和等复杂分析功能
提高查询可读性和性能
应用场景:
排名计算
移动平均和累计计算
同比环比分析
分组内排序和比较
5.8 UseForeignKeys() 方法
static void UseForeignKeys()
{Console.WriteLine("\n=== 外键约束演示 ===");using (var connection = new SQLiteConnection(connectionString)){connection.Open();// 确保外键约束已开启ExecuteNonQuery(connection, "PRAGMA foreign_keys = ON;");// 尝试删除有关联记录的部门try{ExecuteNonQuery(connection, "DELETE FROM Departments WHERE DeptId = 1");Console.WriteLine("删除成功"); // 不应该执行到此处}catch (SQLiteException ex){Console.WriteLine($"预期的外键约束错误: {ex.Message}");}// 正确的删除顺序 - 先删除关联记录Console.WriteLine("\n按正确顺序删除:");ExecuteNonQuery(connection, "BEGIN TRANSACTION");ExecuteNonQuery(connection, "DELETE FROM Employees WHERE DeptId = 3");ExecuteNonQuery(connection, "DELETE FROM Departments WHERE DeptId = 3");ExecuteNonQuery(connection, "COMMIT");Console.WriteLine("部门列表:");DisplayQueryResults(connection, "SELECT * FROM Departments");}
}
解析:
启用外键约束:SQLite默认不强制执行外键约束,需要显式启用
PRAGMA foreign_keys = ON;
。错误测试:尝试删除有员工关联的部门(DeptId=1),这应该会触发外键约束错误,因为有员工引用这个部门。
异常处理:捕获外键约束违反异常,这是预期的行为,确保数据完整性。
正确的删除顺序:
开始事务
BEGIN TRANSACTION
先删除引用记录:
DELETE FROM Employees WHERE DeptId = 3
再删除被引用记录:
DELETE FROM Departments WHERE DeptId = 3
提交事务
COMMIT
查看结果:显示部门表数据,确认删除成功。
外键约束的好处:
确保数据一致性和引用完整性
防止孤立记录
可配置级联动作(CASCADE、SET NULL等)
提供数据库级别的数据验证
注意事项:
SQLite外键约束需要显式启用
多表操作需要考虑正确的顺序
事务可以确保多步操作的原子性
5.9 辅助方法
// 执行非查询语句的辅助方法
static void ExecuteNonQuery(SQLiteConnection connection, string sql, SQLiteTransaction transaction = null)
{using (var command = new SQLiteCommand(sql, connection, transaction)){command.ExecuteNonQuery();}
}// 显示查询结果的辅助方法
static void DisplayQueryResults(SQLiteConnection connection, string sql)
{using (var command = new SQLiteCommand(sql, connection)){using (var reader = command.ExecuteReader()){// 获取列名List<string> columns = new List<string>();for (int i = 0; i < reader.FieldCount; i++){columns.Add(reader.GetName(i));}// 输出列名Console.WriteLine(string.Join(" | ", columns));Console.WriteLine(new string('-', columns.Count * 15));// 输出行while (reader.Read()){List<string> values = new List<string>();for (int i = 0; i < reader.FieldCount; i++){values.Add(reader[i]?.ToString() ?? "NULL");}Console.WriteLine(string.Join(" | ", values));}}}
}
解析:
ExecuteNonQuery方法:
用于执行不返回结果集的SQL命令(INSERT、UPDATE、DELETE、CREATE等)
参数:数据库连接、SQL命令文本、可选的事务对象
使用
using
语句确保命令对象被正确释放DisplayQueryResults方法:
用于执行查询并漂亮地显示结果
步骤:
创建SQL命令并执行查询获取读取器
获取所有列名并显示表头
循环读取每一行数据
将每行格式化为表格形式并输出
数据读取技巧:
使用
reader.FieldCount
获取结果集列数使用
reader.GetName(i)
获取列名使用空合并运算符
??
处理NULL值使用连接将结果格式化为表格
方法复用:这些辅助方法体现了代码复用的好实践,避免在多个地方重复相似代码。
6 使用注意事项
-
连接管理:
-
总是使用
using
语句确保连接正确关闭 -
避免长时间保持连接打开
-
对于多线程应用,考虑使用连接池
-
-
参数化查询:
-
始终使用参数化查询防止SQL注入
-
不要通过字符串拼接构建SQL语句
-
-
事务使用:
-
批量操作使用事务提高性能
-
保持事务尽可能短小
-
正确处理事务的异常情况
-
-
性能考虑:
-
为频繁查询的列创建索引
-
使用
EXPLAIN QUERY PLAN
分析查询性能 -
大量数据操作时考虑批处理
-
-
版本兼容性:
-
确认目标SQLite版本是否支持需要的功能
-
窗口函数需要SQLite 3.25+
-
FTS5需要特殊编译支持
-
7 数据类型映射
C#类型和SQLite类型之间的映射:
C#类型 | SQLite类型 |
---|---|
int, long | INTEGER |
double, float | REAL |
string | TEXT |
byte[] | BLOB |
DateTime | TEXT/INTEGER |
bool | INTEGER |
在读取时需要注意类型转换,SQLite的动态类型系统可能需要额外的类型转换处理。
以上就是SQLite高级功能的学习资源,在里面已经详细介绍了SQLite高级功能的使用以及注意事项等问题!!
相关文章:
C# SQLite高级功能示例
目录 1 主要功能 2 程序结构和流程 3 详细实现说明 3.1 基础设置 3.2 事务演示 3.3 索引演示 3.4 视图演示 3.5 触发器演示 3.6 全文搜索演示 3.7 窗口函数演示 3.8 外键约束演示 4 高级功能示例 5 单个方法详细介绍 5.1 SetupExampleData()方法 5.2 UseTransact…...

【周输入】510周阅读推荐-1
本号一年了,有一定的成长,也有很多读者和点赞。自觉更新仍然远远不够,需要继续努力。 但是还是要坚持2点: 在当前这个时代,信息大爆炸,层次不齐,不追加多, 信息输入可以很多&#x…...

基于动态规划的强化学习方法
目录 # 动态规划 # 基于动态规划的强化学习方法 # 求解过程: ## 策略评估 ## 策略提升 # 价值迭代算法 # 参考 # 动态规划 动态规划的基本思想是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到目标问题的解。…...
启动 spyder ModuleNotFoundError: No module named ‘PyQt5.QtWebKitWidgets‘
一、根本原因 Spyder 版本兼容性:Spyder 4.x 依赖 QtWebKitWidgets,但该模块在 PyQt5 5.15 中已被移除。 PyQt5 版本冲突:如果你安装了较新的 PyQt5(如 5.15),则会缺少 QtWebKitWidgets。 二、解决方案 方法…...
ChemBlender:科研绘图创新解决方案
一、研究背景与冲突 (一)研究背景 在科学研究领域,可视化表达对于成果的呈现与交流至关重要。科研绘图作为科学可视化的关键手段,涵盖了从微观分子结构到宏观实验现象等广泛的内容。随着科研的深入发展,研究对象的复杂…...

Uniapp Android/IOS 获取手机通讯录
介绍 最近忙着开发支付宝小程序和app,下面给大家介绍一下 app 获取通讯录的全部过程吧,也是这也是我app开发中的一项需求吧。 效果图如下 勾选配置文件 使用uniapp开发的童鞋都知道有一个配置文件 manifest.json 简单的说一下,就是安卓/ios/…...
设计一个分布式系统:要求全局消息顺序,如何使用Kafka实现?
一、高吞吐低延迟 Kafka 集群设计要点 1. 分区策略优化 // 计算合理分区数公式(动态调整) int numPartitions max(Tp, Tc) / min(Tp, Tc) // Tp生产者吞吐量 Tc消费者吞吐量建议初始按业务键(如订单ID)哈希分区单分区吞吐建议…...

2025年RIS SCI2区,改进白鲸优化算法+复杂非线性方程组求解,深度解析+性能实测
目录 1.摘要2.白鲸优化算法BWO原理3.改进策略4.结果展示5.参考文献6.代码获取7.读者交流 1.摘要 本文提出了一种改进白鲸优化算法(ABWOA)用来解决非线性方程组(SNLEs)求解问题。ABWOA引入了平衡因子和非线性自适应参数࿰…...

Java后端开发day48--反射动态代理
(以下内容全部来自上述课程) 反射 反射允许对成员变量,成员方法和构造方法的信息进行编程访问。 就是获取里面的成员变量、构造方法和成员方法,idea中打代码跳出来的提示就是反射。 1. 获取class对象的三种方式 Class.for…...
十四、继承与组合(Inheritance Composition)
十四、继承与组合(Inheritance & Composition) 引言 C最引人注目的特性之一是代码复用。组合:在新类中创建已有类的对象。继承:将新类作为已有类的一个类型来创建。 14.1 组合的语法 Useful.h //C14:Useful.h #ifndef US…...

ValueError: Caught ValueError in DataLoader worker process 0.
参考链接: https://stackoverflow.com/questions/1841565/valueerror-invalid-literal-for-int-with-base-10 它提示我有个地方值错误空字符 果然因为格式处理没有传进去东西,找下原因,让它正常处理 原来是相对路径的.影响了程序运行 将v…...

【数据结构】——链表OJ(下)
前面我们已经刷了几道单链表的题目,下面我们继续看几道题目。 一、相交链表 这道题题目的要求是很好理解的,就是现在我们有两个链表,然后我们就相办法进行判断,这两个链表是否是相交的,那么链表的相交其实就是有没有共…...

Adobe Acrobat pro在一份PDF中插入空白页
在Adobe Acrobat pro中先打开我们的PDF文件; 用鼠标点击需要插入空白页处的上一页; 然后如下图操作: 默认会在光标处的下一页插入一张空白页,你也可以修改插入页的页码或者向前一页插入...

java-----异常
对于Error:表示系统级错误或者资源耗尽的状况,像OutOfMemoryError、StackOverflowError等。这类错误是程序无法处理的,通常也不应该尝试去处理。 对于Exception:表示程序可以处理的异常。它又能细分为: 受检查异常&a…...

[工具]B站缓存工具箱 (By 郭逍遥)
📌 项目简介 B站缓存工具箱是一个多功能的B站缓存工具,包含视频下载、缓存重载、文件合并及系统设置四大核心功能。基于yutto开发,采用图形化界面操作,极大简化B站资源获取与管理流程。 工具可以直接将原本缓存的视频读取&#…...
《内网渗透测试:绕过最新防火墙策略》
内网渗透测试是检验企业网络安全防御体系有效性的核心手段,而现代防火墙策略的持续演进(如零信任架构、AI流量分析、深度包检测)对攻击者提出了更高挑战。本文系统解析2024年新型防火墙的防护机制,聚焦协议隐蔽隧道、上下文感知绕…...
python_竞态条件
好的,我们通过一个具体的例子来说明在多线程环境中,可变对象和不可变对象的行为差异,以及不可变对象如何避免竞态条件(race condition)。 1. 竞态条件(Race Condition) 竞态条件是指在多线程环…...
聊聊JetCache的CachePenetrationProtect
序 本文主要研究一下JetCache的CachePenetrationProtect CachePenetrationProtect com/alicp/jetcache/anno/CachePenetrationProtect.java Documented Retention(RetentionPolicy.RUNTIME) Target({ElementType.METHOD, ElementType.FIELD}) public interface CachePenetr…...
【实战】基于 ABP vNext 构建高可用 S7 协议采集平台(西门子 PLC 通信全流程)
🚀🔧【实战】基于 ABP vNext 构建高可用 S7 协议采集平台(西门子 PLC 通信全流程)📊 📑 目录 🚀🔧【实战】基于 ABP vNext 构建高可用 S7 协议采集平台(西门子 PLC 通信全…...
数据结构:树(Tree)
目录 为什么需要树? 🌱 基本的树结构定义 什么是树? 树的术语 🌿 常见基本树的变体 🌳 二叉搜索树(BST) 🌲 自平衡二叉搜索树 1. AVL树(Adelson-Velsky and La…...

自动化测试与功能测试详解
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 什么是自动化测试? 自动化测试是指利用软件测试工具自动实现全部或部分测试,它是软件测试的一个重要组成 部分,能完成许多手工测试无…...
java中的Optional
在 Java 8 中,Optional 是一个用于处理可能为 null 的值的容器类,旨在减少空指针异常(NullPointerException)并提升代码的可读性。以下是 Optional 的核心用法和最佳实践: 1. 创建 Optional 对象 1.1 常规创建方式 Op…...
Qt事件循环机制
受事件循环机制影响,按钮的样式表改变了可能不会立即刷新。 需要使用 update() 或 repaint() 或者调用 QApplication::processEvents() 强制处理所有待处理的事件,从而确保界面更新。 在 Qt 中,事件循环(Event Loop)是…...
深入理解 OAuth 2.0:技术核心与实战场景
在互联网应用日益复杂的今天,如何安全、高效地实现第三方应用授权访问资源,成为开发者面临的重要问题。OAuth 2.0 凭借其灵活、安全的授权机制,成为解决这一问题的主流方案。本文将深入剖析 OAuth 2.0 的技术重点,并结合具体使用场…...
Rust 环境变量管理秘籍:从菜鸟到老鸟都爱的 dotenv 教程
前言 写代码的你,是否遭遇过这些灵魂拷问: “我现在在哪个环境?开发?测试?还是直接在生产线上裸奔?”“少写一个 .env,测试脚本在数据库里上演清空大法,客户当场破防。”“每次手动设置 RUST_ENV,命令敲到一半就开始怀疑人生,还怕输错一个字符引发灭世级事故。”别慌…...

CSS经典布局之圣杯布局和双飞翼布局
目标: 中间自适应,两边定宽,并且三栏布局在一行展示。 圣杯布局 实现方法: 通过float搭建布局margin使三列布局到一行上relative相对定位调整位置; 给外部容器添加padding,通过相对定位调整左右两列的…...

OpenCV 的 CUDA 模块中用于将多个单通道的 GpuMat 图像合并成一个多通道的图像 函数cv::cuda::merge
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 OpenCV 的 CUDA 模块中,cv::cuda::merge 函数用于将多个单通道的 GpuMat 图像合并成一个多通道的图像。该函数是 cv::merge 的 GP…...

计网实验笔记(一)CS144 Lab
Lab0 ByteStream : 实现一个在内存中的 有序可靠字节流Lab1 StreamReassembler:实现一个流重组器,一个将字节流的字串或者小段按照正确顺序来拼接回连续字节流的模块Lab2 TCPReceiver:实现入站字节流的TCP部分。Lab3 TCPSender:实…...
Blog Contents
目录 Python Financing Medical Logistics Tool(IT & AI) 持续更新~ Python # Name URL 1 Python | Dashboard制作 Python | Dashboard制作-CSDN博客 2 Python | AKShare获取A股数据 Python | AKShare获取A股数据-CSDN博客 3 Python | A股指标对比 Python | A股…...

什么是ERP?ERP有哪些功能?小微企业ERP系统源码,SpringBoot+Vue+ElementUI+UniAPP
什么是ERP? ERP翻译过来叫企业资源计划,通俗的讲,应该叫企业的全面预算控制,其通常包括三个部分:工程预算、投资预算和经营预算(即产销存预算)。之所以做预算控制,是因为企业运作的…...