SQLite 入门指南 – wiki基地


SQLite 入门指南:从零到精通轻量级数据库

数据库是现代软件应用中不可或缺的核心组件,用于存储、管理和检索数据。在众多数据库系统中,SQLite 以其独特的优势占据了一席之地,尤其在嵌入式系统、移动应用、桌面软件以及需要轻量级、零配置数据存储的场景中广受欢迎。

如果你是数据库领域的初学者,或者正在寻找一个简单易用、无需独立服务器的数据库解决方案,那么 SQLite 绝对是一个极好的起点。本指南将带你从零开始,逐步深入理解 SQLite 的概念、使用方法以及如何在实际应用中与其交互。

目录

  1. SQLite 是什么?为何选择它?

    • 什么是 SQLite?核心特性介绍
    • SQLite 的优势:为何如此受欢迎?
    • SQLite 的局限性
    • 常见的应用场景
  2. 快速入门:安装与第一个数据库

    • 安装 SQLite:不同操作系统的获取方式
    • 使用命令行工具 sqlite3
    • 创建或打开一个数据库文件
    • 基本命令行操作 (.help, .quit, .tables, .schema)
  3. 数据库基础概念

    • 数据库文件 (.db)
    • 表 (Table)、列 (Column) 和行 (Row)
    • 数据类型 (Datatypes)
    • 主键 (Primary Key) 与约束 (Constraints)
    • SQL:与数据库交互的语言
  4. SQL 实战:数据操作核心 (CRUD)

    • 创建表 (CREATE TABLE)
    • 插入数据 (INSERT INTO)
    • 查询数据 (SELECT)
      • 基本查询 (SELECT *, SELECT column1, ...)
      • 过滤 (WHERE 子句)
      • 排序 (ORDER BY)
      • 限制结果数量 (LIMIT, OFFSET)
      • 聚合函数 (COUNT, SUM, AVG, MIN, MAX)
      • 分组 (GROUP BY)
    • 更新数据 (UPDATE)
    • 删除数据 (DELETE)
    • 删除表 (DROP TABLE)
  5. 稍微进阶:索引与事务

    • 索引 (Indexes):提升查询性能
      • 什么是索引?
      • 创建索引 (CREATE INDEX)
      • 何时使用索引?
    • 事务 (Transactions):确保数据一致性
      • 什么是事务?ACID 特性
      • 事务命令 (BEGIN, COMMIT, ROLLBACK)
      • 一个简单的事务示例
  6. 与编程语言交互 (以 Python 为例)

    • 为何需要编程接口?
    • Python 的 sqlite3 模块
    • 连接数据库、创建游标
    • 执行 SQL 语句
    • 获取查询结果
    • 提交与回滚事务
    • 关闭连接
    • 参数化查询:防止 SQL 注入 (重要!)
    • 一个完整的 Python 示例
  7. SQLite 工具推荐

    • 命令行工具 sqlite3
    • 图形用户界面 (GUI) 工具 (例如:DB Browser for SQLite)
  8. 使用技巧与注意事项

    • 备份数据库
    • VACUUM 命令
    • 理解 WAL 模式 (Write-Ahead Logging)
    • 性能优化初步
    • SQL 注入防范 (再次强调)
  9. 总结与展望


1. SQLite 是什么?为何选择它?

什么是 SQLite?核心特性介绍

SQLite 是一个轻量级、无服务器、自包含、高可靠性、全功能的 SQL 数据库引擎。与传统的数据库系统(如 MySQL、PostgreSQL、SQL Server)需要一个独立的服务器进程来管理数据不同,SQLite 将整个数据库存储在一个单一的文件中。它是一个 C 语言库,可以直接嵌入到应用程序中,应用程序通过调用函数来访问和操作数据库。

核心特性概览:

  • 无服务器 (Serverless): 没有独立的服务器进程。应用程序直接读写数据库文件。这简化了部署和管理。
  • 自包含 (Self-Contained): 只需要一个 C 库文件(或少量文件),无需外部依赖。
  • 零配置 (Zero-Configuration): 无需安装、配置或启动服务器。数据库文件即数据库。
  • 事务性 (Transactional): 支持完整的 ACID 事务,确保数据的一致性、完整性和持久性。
  • 高可靠性 (Highly Reliable): 经过严格测试,在多种异常情况下(如崩溃、断电)也能保持数据完整。
  • 跨平台 (Cross-Platform): 可以在几乎所有操作系统上运行。
  • 公共领域 (Public Domain): 完全免费使用,无需许可费用,可用于任何目的。
  • 单文件数据库: 整个数据库(包括定义、表、索引和数据)存储在一个普通磁盘文件中。

SQLite 的优势:为何如此受欢迎?

  • 易于使用和部署: 无需安装服务器,直接复制数据库文件即可使用,极大地简化了应用程序的部署和分发。
  • 零管理: 大部分情况下,你不需要数据库管理员。数据库没有用户、权限、配置等复杂概念(与传统服务器数据库相比)。
  • 轻量级: 库文件体积小,资源消耗低,非常适合资源受限的环境。
  • 可靠性高: 设计时就考虑了鲁棒性,能在各种异常情况(如程序崩溃、操作系统崩溃、断电)下保护数据不被破坏。
  • 速度快: 对于许多常见的读操作,SQLite 的速度可以与文件系统读写相媲美。对于中小型数据集或读多写少的场景,性能表现优秀。
  • SQL 支持: 支持大部分标准的 SQL92 语法,学习成本低,易于从其他数据库迁移。
  • 文件格式: 数据库文件是跨平台的,可以直接复制到不同系统上使用。

SQLite 的局限性

虽然 SQLite 优点众多,但它并非万能的,在某些场景下可能不如传统的客户端-服务器数据库:

  • 并发写入性能: 虽然支持并发读,但在同一时间点进行大量并发写入时,性能不如客户端-服务器数据库。通常情况下,整个数据库文件会被锁定进行写入操作。不过,通过 WAL 模式可以改善这一情况。
  • 网络访问: SQLite 数据库不能直接通过网络访问,只能由访问数据库文件的同一台机器上的应用程序使用。如果需要网络访问,你需要在应用程序中构建一个服务层。
  • 用户与权限管理: SQLite 没有内置的用户账户和权限管理系统,数据的访问控制依赖于文件系统的权限。

常见的应用场景

正是因为其独特的优势和局限性,SQLite 在以下场景中大放异彩:

  • 嵌入式设备: 机顶盒、消费电子、物联网设备等。
  • 移动应用: iOS (Core Data)、Android (SQLiteDatabase) 都内置了 SQLite。
  • 桌面应用程序: 作为本地数据存储,例如浏览器 (Chrome, Firefox)、邮件客户端 (Thunderbird)、本地音乐播放器等。
  • 文件格式: 可以用作复杂文档的文件格式(例如:Firefox Places 历史记录,Adobe Lightroom)。
  • 网站数据存储: 对于流量不大、读操作远多于写操作的网站,SQLite 可以作为一种简单高效的后端数据库。
  • 开发和测试: 作为开发过程中的临时数据库或测试数据库,部署和重置都非常方便。
  • 数据分析: 用于处理小型到中型的数据集。

2. 快速入门:安装与第一个数据库

SQLite 的安装通常非常简单,甚至在很多系统上已经预装。

安装 SQLite:不同操作系统的获取方式

  • Windows: 前往 SQLite 官方网站 (https://www.sqlite.org/download.html) 下载预编译的二进制文件 (sqlite-tools-win64-x64-*.zipsqlite-tools-win32-x86-*.zip)。解压后,将包含 sqlite3.exe 的目录添加到系统的 PATH 环境变量中,或者直接进入该目录使用。
  • macOS: macOS 系统通常内置了 SQLite。打开终端即可直接使用 sqlite3 命令。
  • Linux: 大多数 Linux 发行版也预装了 SQLite 或者可以通过包管理器轻松安装。例如,在 Debian/Ubuntu 上:sudo apt-get install sqlite3;在 Fedora/CentOS/RHEL 上:sudo yum install sqlitesudo dnf install sqlite

使用命令行工具 sqlite3

sqlite3 是 SQLite 官方提供的命令行工具,是学习和管理 SQLite 数据库最直接的方式。

打开终端或命令提示符,输入 sqlite3 并按回车。

bash
$ sqlite3
SQLite version 3.36.0 2021-06-18 14:11:39
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite>

这表示你已经进入了 SQLite 命令行界面。此时你连接的是一个临时的内存数据库。

创建或打开一个数据库文件

要创建一个持久化的数据库文件,或者打开一个已有的数据库文件,可以在启动 sqlite3 时指定文件名:

bash
$ sqlite3 my_first_database.db
SQLite version 3.36.0 2021-06-18 14:11:39
Enter ".help" for usage hints.
sqlite>

如果在当前目录下不存在 my_first_database.db 文件,SQLite 会自动创建一个。如果文件已经存在,SQLite 会打开它。现在,你在这个界面中执行的所有数据库操作(如创建表、插入数据)都将保存在这个文件中。

你也可以在进入 sqlite3 界面后再打开一个文件,使用 .open 命令:

sql
sqlite> .open my_another_database.db

基本命令行操作

sqlite3 命令行工具支持一些点开头的特殊命令,用于控制工具的行为,而不是执行 SQL 语句。

  • .help:显示所有可用的点命令列表及其用途。
  • .quit.exit:退出 SQLite 命令行界面。
  • .databases:显示当前连接的数据库及其文件路径。
  • .tables:显示当前数据库中的所有表名。
  • .schema [table_name]:显示指定表的创建语句(即表结构),如果不指定表名,则显示所有表的结构。
  • .mode MODE:设置输出模式,例如 .mode column 使输出对齐,.mode list (默认) 用逗号分隔,.mode html 输出 HTML 表格等。
  • .headers on/.headers off:开启/关闭在结果中显示列头。
  • .show:显示当前的设置(模式、头信息等)。

示例:

sql
sqlite> .help
... (一大串帮助信息) ...
sqlite> .databases
main: /path/to/my_first_database.db
sqlite> .tables
-- 此时应该没有表
sqlite> .quit

3. 数据库基础概念

在开始编写 SQL 语句之前,理解一些基本的数据库概念是必要的。

  • 数据库文件 (.db): 在 SQLite 中,整个数据库的所有内容都存储在一个独立的磁盘文件中,通常以 .db.sqlite 作为扩展名。
  • 表 (Table): 数据库中的基本组织单位,用来存储具有相同结构的数据集合。想象它像电子表格一样,由行和列组成。
  • 列 (Column): 定义了表中存储的数据的类型和名称。每一列代表数据的一个属性。例如,在一个 users 表中,可能有 id, name, age 等列。
  • 行 (Row): 表中的一条记录,代表一个独立的数据项。每一行包含了表中所有列的数据。
  • 数据类型 (Datatypes): 定义了列中可以存储的数据种类。SQLite 的数据类型系统比较灵活,称为“动态类型系统”或“类型亲和性”。尽管你可以声明列为特定的类型(如 INT, TEXT, VARCHAR),但 SQLite 实际上使用以下五种基本存储类来存储数据:
    • NULL: 值是 NULL。
    • INTEGER: 带符号的整数,根据数值大小存储为 1、2、3、4、6 或 8 字节。
    • REAL: 浮点数值,存储为 8 字节 IEEE 浮点数。
    • TEXT: 文本字符串,使用数据库编码 (UTF-8, UTF-16LE 或 UTF-16BE) 存储。
    • BLOB: 二进制大对象 (Binary Large Object),数据完全按照输入存储(例如,图片、音频文件)。
    • 当你声明列类型时,SQLite 会根据声明的类型字符串,为其分配一个“类型亲和性”,指导它如何存储该列的数据。例如 INT, INTEGER, TINYINT 等会具有 INTEGER 亲和性;CHAR, VARCHAR, TEXT 等具有 TEXT 亲和性;REAL, DOUBLE, FLOAT 具有 REAL 亲和性;BLOB 没有亲和性;其他类型如 DATETIME, BOOLEAN 等会根据其名称被赋予 NUMERICTEXT 亲和性。
  • 主键 (Primary Key): 一列或一组列,其值能唯一标识表中的每一行。主键列的值不能重复,且通常是非空的 (NOT NULL)。在 SQLite 中,如果一个表的 INTEGER PRIMARY KEY 列没有提供值,或者提供 NULL,SQLite 会自动生成一个唯一的、递增的整数作为该行的 ID。这通常称为 ROWID。
  • 约束 (Constraints): 定义了表中数据必须满足的规则,用于维护数据的一致性和完整性。常见的约束有:
    • NOT NULL: 该列的值不能为空。
    • UNIQUE: 该列的所有值都必须是唯一的。
    • DEFAULT value: 如果插入新行时该列没有提供值,则使用指定的默认值。
    • CHECK expression: 确保该列的值满足指定的布尔表达式。
    • FOREIGN KEY: 定义了与其他表之间关系的约束(虽然本入门指南不深入探讨,但知道它的存在很重要)。
  • SQL (Structured Query Language): 结构化查询语言,是与关系型数据库交互的标准语言,用于定义、操作和控制数据。我们将主要使用 SQL 语句来创建表、插入、查询、更新和删除数据。

4. SQL 实战:数据操作核心 (CRUD)

CRUD 是指数据库操作中最核心的四种功能:Create (创建)、Read (读取)、Update (更新) 和 Delete (删除)。我们将学习如何使用 SQL 语句在 SQLite 中执行这些操作。

创建表 (CREATE TABLE)

在存储数据之前,首先需要定义数据的结构,即创建表。

语法:

sql
CREATE TABLE table_name (
column1_name datatype [constraints],
column2_name datatype [constraints],
...
[PRIMARY KEY (column_name) | PRIMARY KEY (column1, column2, ...)]
[FOREIGN KEY (column_name) REFERENCES other_table(other_column)]
);

示例:创建一个 users 表来存储用户信息

“`sql
— 连接到数据库文件(如果还没连接)
— $ sqlite3 my_first_database.db

— 在 sqlite> 提示符下输入以下语句:
CREATE TABLE users (
id INTEGER PRIMARY KEY, — INTEGER PRIMARY KEY 通常自动递增且唯一
name TEXT NOT NULL UNIQUE, — 姓名是文本类型,不能为空,且必须唯一
age INTEGER DEFAULT 0, — 年龄是整数类型,默认为0
email TEXT — 电子邮件是文本类型,可以为空
);

— 验证表是否创建成功
.tables
— 应该看到: users

— 查看表结构
.schema users
— 应该看到创建表的 SQL 语句
“`

插入数据 (INSERT INTO)

向表中添加新的数据行。

语法 1:指定列名和对应的值

sql
INSERT INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...);

语法 2:为所有列提供值 (值的顺序必须与创建表时列的顺序一致)

sql
INSERT INTO table_name
VALUES (value1, value2, ...);

示例:向 users 表插入数据

“`sql
— 插入 Alice 的信息,指定列
INSERT INTO users (name, age, email) VALUES (‘Alice’, 30, ‘[email protected]’);

— 插入 Bob 的信息,为所有列提供值 (id 会自动生成)
INSERT INTO users VALUES (NULL, ‘Bob’, 25, ‘[email protected]’); — 使用 NULL 让 id 自动生成

— 插入 Charlie 的信息,只提供必须的列
INSERT INTO users (name) VALUES (‘Charlie’); — age 将使用默认值 0,email 为 NULL

— 插入多行数据 (部分数据库支持,SQLite 支持)
INSERT INTO users (name, age) VALUES
(‘David’, 22),
(‘Eve’, 35);
``
注意:对于
INTEGER PRIMARY KEY,插入NULL` 或省略该列都会触发自动生成行为。

查询数据 (SELECT)

从表中检索数据。这是最常用也是功能最强大的 SQL 命令。

语法:

sql
SELECT column1, column2, ... -- 或 * 表示所有列
FROM table_name
[WHERE condition] -- 过滤条件
[GROUP BY column1, ...] -- 分组
[HAVING condition] -- 分组后的过滤条件
[ORDER BY column1 [ASC|DESC], ...] -- 排序
[LIMIT count [OFFSET offset]]; -- 限制结果数量

方括号 [] 表示可选部分。

示例:查询 users 表中的数据

“`sql
— 查询所有列和所有行
SELECT * FROM users;

— 查询特定列 (姓名和年龄)
SELECT name, age FROM users;

— 查询年龄大于 25 岁的用户 (使用 WHERE 过滤)
SELECT * FROM users WHERE age > 25;

— 查询姓名是 ‘Alice’ 的用户
SELECT * FROM users WHERE name = ‘Alice’;

— 查询年龄在 20 到 30 岁之间的用户 (使用 AND 和 BETWEEN)
SELECT * FROM users WHERE age >= 20 AND age <= 30;
SELECT * FROM users WHERE age BETWEEN 20 AND 30;

— 查询姓名以 ‘A’ 开头的用户 (使用 LIKE 和通配符 %)
SELECT * FROM users WHERE name LIKE ‘A%’; — % 匹配任意长度字符

— 查询姓名倒数第二个字母是 ‘i’ 的用户 (使用 LIKE 和通配符 )
SELECT * FROM users WHERE name LIKE ‘%i
‘; — _ 匹配单个字符

— 查询没有电子邮件的用户 (使用 IS NULL)
SELECT * FROM users WHERE email IS NULL;

— 查询有电子邮件的用户 (使用 IS NOT NULL)
SELECT * FROM users WHERE email IS NOT NULL;

— 查询年龄大于 25 岁,并按年龄从低到高排序
SELECT * FROM users WHERE age > 25 ORDER BY age ASC; — ASC 可省略,默认升序

— 查询所有用户,按年龄从高到低排序
SELECT * FROM users ORDER BY age DESC;

— 查询所有用户,先按年龄升序,年龄相同时按姓名降序
SELECT * FROM users ORDER BY age ASC, name DESC;

— 查询年龄最大的 3 个用户
SELECT * FROM users ORDER BY age DESC LIMIT 3;

— 查询年龄最大的用户 (另一种方式,结合 ORDER BY 和 LIMIT)
SELECT * FROM users ORDER BY age DESC LIMIT 1;

— 查询总共有多少用户 (使用聚合函数 COUNT)
SELECT COUNT(*) FROM users;

— 查询所有用户的平均年龄 (使用聚合函数 AVG)
SELECT AVG(age) FROM users;

— 查询年龄最小和最大的用户的年龄 (使用聚合函数 MIN 和 MAX)
SELECT MIN(age), MAX(age) FROM users;

— 查询不同年龄组的人数 (使用 GROUP BY)
SELECT age, COUNT(*) FROM users GROUP BY age;

— 查询人数多于 1 人的年龄组 (使用 GROUP BY 和 HAVING)
SELECT age, COUNT() FROM users GROUP BY age HAVING COUNT() > 1;
“`

更新数据 (UPDATE)

修改表中已有数据行的值。

语法:

sql
UPDATE table_name
SET column1 = value1, column2 = value2, ...
[WHERE condition]; -- 指定要更新哪些行

示例:更新 users 表中的数据

“`sql
— 将 Alice 的年龄改为 31
UPDATE users SET age = 31 WHERE name = ‘Alice’;

— 将所有年龄小于 10 岁的用户的年龄设置为 10
UPDATE users SET age = 10 WHERE age < 10;

— 将所有用户的年龄都增加 1
UPDATE users SET age = age + 1;

— 注意:如果没有 WHERE 子句,UPDATE 将会影响表中的所有行!
“`

删除数据 (DELETE)

从表中删除数据行。

语法:

sql
DELETE FROM table_name
[WHERE condition]; -- 指定要删除哪些行

示例:删除 users 表中的数据

“`sql
— 删除年龄小于 20 岁的用户
DELETE FROM users WHERE age < 20;

— 删除姓名为 ‘Bob’ 的用户
DELETE FROM users WHERE name = ‘Bob’;

— 注意:如果没有 WHERE 子句,DELETE 将会删除表中的所有行!
DELETE FROM users; — 删除 users 表中的所有数据,但表结构还在
“`

删除表 (DROP TABLE)

彻底删除数据库中的一个表及其所有数据。

语法:

sql
DROP TABLE table_name;

示例:删除 users

“`sql
— 删除 users 表
DROP TABLE users;

— 再次查看表列表,users 应该不见了
.tables
“`

重要提示: DROP TABLE 是一个不可逆的操作,会永久删除表及其数据,请谨慎使用。

5. 稍微进阶:索引与事务

了解索引和事务对于构建更高效、更可靠的数据库应用至关重要。

索引 (Indexes):提升查询性能

想象一本书的目录或索引。当你需要查找特定主题时,你会先查阅索引,找到对应的页码,然后直接翻到那一页,而不是从头到尾翻遍全书。数据库索引的作用类似。

什么是索引?

数据库索引是一种数据结构(如 B-树),它可以加快数据库表中数据的检索速度。索引是基于表中的一列或多列创建的。

创建索引 (CREATE INDEX)

语法:

sql
CREATE INDEX index_name ON table_name (column1 [ASC|DESC], column2 [ASC|DESC], ...);

示例:

“`sql
— 为 users 表的 age 列创建索引,以便快速查询特定年龄范围的用户
CREATE INDEX idx_users_age ON users (age);

— 为 users 表的 name 列创建唯一索引,确保 name 列的值唯一 (虽然我们在 CREATE TABLE 时已经用了 UNIQUE,这里只是示例)
CREATE UNIQUE INDEX idx_users_name ON users (name); — UNIQUE 关键字确保索引值唯一
“`

何时使用索引?

  • 经常用于 WHERE 子句中进行过滤的列。
  • 经常用于 JOIN 操作的列。
  • 经常用于 ORDER BY 子句中进行排序的列。
  • 主键列会自动创建索引。

何时不使用索引?

  • 表很小: 对于只有几十或几百行的表,全表扫描可能比使用索引更快或一样快。
  • 经常进行大量写入 (INSERT, UPDATE, DELETE) 的表: 每次数据修改时,索引也需要更新,这会带来额外的开销。索引越多,写入越慢。
  • 包含大量重复值的列: 比如一个只有 “男” 和 “女” 两个值的性别列,索引的区分度太低,效果不佳。
  • 不会用于查询条件的列。

索引是以空间换时间:它们会占用额外的磁盘空间,但能显著提高查询速度。在 SQLite 中,索引存储在同一个 .db 文件中。

事务 (Transactions):确保数据一致性

事务是一组数据库操作,被视为单个逻辑工作单元。事务中的所有操作要么全部成功提交,要么全部失败回滚,回到事务开始前的状态。

什么是事务?ACID 特性

事务具有 ACID 特性,这是数据库系统可靠性的基石:

  • 原子性 (Atomicity): 事务是一个不可分割的最小工作单元,其中的操作要么全做,要么全不做。
  • 一致性 (Consistency): 事务应使数据库从一个一致性状态转换到另一个一致性状态。例如,转账事务执行前后,总金额不变。
  • 隔离性 (Isolation): 并发执行的事务之间互不干扰,每个事务感觉自己是系统中唯一在运行的事务。
  • 持久性 (Durability): 事务一旦提交,其所做的修改就是永久的,即使系统发生故障也不会丢失。

事务命令 (BEGIN, COMMIT, ROLLBACK)

在 SQLite 中,默认情况下,每条 SQL 语句都是一个独立的事务并立即提交(这被称为自动提交模式)。对于需要多个步骤协作完成的操作,你需要显式地开始和结束事务。

  • BEGIN TRANSACTION;BEGIN;: 开始一个新事务。
  • COMMIT;: 提交当前事务,使所有修改永久生效。
  • ROLLBACK;: 回滚当前事务,撤销所有未提交的修改。

一个简单的事务示例 (模拟转账)

假设有一个 accounts 表,包含 name (TEXT), balance (REAL) 两列。我们要从 Alice 的账户转 100 元给 Bob。这个操作包括两个步骤:Alice 余额减少,Bob 余额增加。这两个步骤必须同时成功或同时失败。

“`sql
— 模拟数据
CREATE TABLE accounts (name TEXT PRIMARY KEY, balance REAL);
INSERT INTO accounts VALUES (‘Alice’, 1000.0);
INSERT INTO accounts VALUES (‘Bob’, 500.0);

— 查看初始余额
SELECT * FROM accounts;
— 输出:
— Alice|1000.0
— Bob|500.0

— 开始一个事务
BEGIN TRANSACTION;

— 尝试从 Alice 账户扣款
UPDATE accounts SET balance = balance – 100 WHERE name = ‘Alice’;

— 模拟一个可能发生的错误,例如 Bob 账户不存在,或者其他操作失败
— 如果这里发生错误,后面的 COMMIT 就不会执行,我们可以手动 ROLLBACK

— 尝试给 Bob 账户加款
UPDATE accounts SET balance = balance + 100 WHERE name = ‘Bob’;

— 如果一切顺利,提交事务
COMMIT;

— 如果在中间步骤发生错误(例如,假设 Bob 的 UPDATE 语句写错了),
— 我们会执行 ROLLBACK 代替 COMMIT:
— ROLLBACK;

— 查看最终余额 (假设事务成功提交)
SELECT * FROM accounts;
— 输出:
— Alice|900.0
— Bob|600.0

— 如果我们 ROLLBACK 了,余额会回到初始状态
— Alice|1000.0
— Bob|500.0
“`

使用事务可以确保即使在操作过程中发生中断(如程序崩溃),数据库也能保持一致状态。

6. 与编程语言交互 (以 Python 为例)

在实际应用中,我们很少直接在命令行中操作数据库,而是通过编程语言来控制。几乎所有主流编程语言都有 SQLite 的驱动或库。这里以 Python 为例,因为它内置了 sqlite3 模块,无需额外安装。

为何需要编程接口?

  • 动态构建 SQL: 根据程序逻辑生成不同的查询。
  • 处理数据: 将数据库查询结果集成到程序的数据结构中进行进一步处理。
  • 用户交互: 根据用户输入执行数据库操作并将结果展示给用户。
  • 自动化任务: 定时或事件触发的数据库维护和数据处理。

Python 的 sqlite3 模块

Python 标准库提供了 sqlite3 模块,用于与 SQLite 数据库交互。

基本流程:

  1. 导入 sqlite3 模块。
  2. 使用 sqlite3.connect() 函数连接到数据库文件。如果文件不存在,会创建一个新的。
  3. 获取一个游标对象 (connection.cursor())。游标用于执行 SQL 命令和获取结果。
  4. 使用游标对象的 execute() 方法执行 SQL 语句。
  5. 如果执行的是 SELECT 查询,使用游标对象的 fetchone(), fetchall(), 或 fetchmany() 方法获取结果。
  6. 如果执行的是修改操作 (INSERT, UPDATE, DELETE),需要调用连接对象的 commit() 方法来保存修改。如果不需要保存,可以调用 rollback() 回滚。
  7. 使用 try...except...finallywith 语句处理异常并确保连接和游标被正确关闭。
  8. 最后,调用连接对象的 close() 方法关闭连接。

一个完整的 Python 示例

“`python
import sqlite3
import os

定义数据库文件路径

db_file = ‘my_app_database.db’

如果数据库文件已存在,先删除以便重新开始示例

if os.path.exists(db_file):
os.remove(db_file)
print(f”Deleted existing database file: {db_file}”)

conn = None # 初始化连接对象
try:
# 1. 连接数据库 (如果文件不存在则创建)
# 使用 with 语句可以确保连接和游标自动关闭,推荐使用
with sqlite3.connect(db_file) as conn:
# 2. 获取游标对象
cursor = conn.cursor()

    # 3. 创建表 (如果不存在)
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS products (
            id INTEGER PRIMARY KEY,
            name TEXT NOT NULL UNIQUE,
            price REAL DEFAULT 0.0
        )
    ''')
    print("Table 'products' created or already exists.")

    # 4. 插入数据
    # 插入单条数据
    cursor.execute("INSERT INTO products (name, price) VALUES (?, ?)", ('Laptop', 1200.00)) # 使用占位符 ? 防止 SQL 注入

    # 插入多条数据
    products_list = [
        ('Keyboard', 75.00),
        ('Mouse', 25.00),
        ('Monitor', 300.00)
    ]
    cursor.executemany("INSERT INTO products (name, price) VALUES (?, ?)", products_list) # executemany 用于插入多行
    print("Data inserted into 'products' table.")

    # 5. 查询数据
    print("\n--- All products ---")
    cursor.execute("SELECT * FROM products")
    rows = cursor.fetchall() # 获取所有结果行
    for row in rows:
        print(row)

    print("\n--- Products cheaper than $100 ---")
    cursor.execute("SELECT name, price FROM products WHERE price < ?", (100.00,)) # WHERE 条件带参数
    rows_cheap = cursor.fetchall()
    for row in rows_cheap:
        print(f"Name: {row[0]}, Price: {row[1]}")

    print("\n--- Count of products ---")
    cursor.execute("SELECT COUNT(*) FROM products")
    count = cursor.fetchone()[0] # fetchone() 返回一个元组,取第一个元素
    print(f"Total products: {count}")

    # 6. 更新数据
    cursor.execute("UPDATE products SET price = ? WHERE name = ?", (1300.00, 'Laptop'))
    print("\nUpdated Laptop price.")

    # 7. 删除数据
    cursor.execute("DELETE FROM products WHERE name = ?", ('Mouse',))
    print("Deleted Mouse.")

    # 8. 再次查询确认修改
    print("\n--- Products after update and delete ---")
    cursor.execute("SELECT * FROM products")
    rows_final = cursor.fetchall()
    for row in rows_final:
        print(row)

    # with 语句块结束时,如果一切正常,会自动 COMMIT;如果发生异常,会自动 ROLLBACK。
    # 如果不使用 with 语句,你需要手动 conn.commit() 或 conn.rollback()

except sqlite3.Error as e:
print(f”Database error: {e}”)
if conn:
conn.rollback() # 发生错误时回滚

except Exception as e:
print(f”An error occurred: {e}”)

finally:
# with 语句会确保连接关闭,但如果不用 with,这里需要手动关闭
# if conn:
# conn.close()
print(“\nDatabase connection managed.”)

“`

参数化查询 (防止 SQL 注入):

在上面的 Python 示例中,注意我们在 INSERTSELECT 语句中使用了 ? 占位符,并将实际的值作为 execute() 方法的第二个参数(一个元组或列表)传递。这是防止 SQL 注入攻击的关键方法! 永远不要直接将用户输入或其他变量通过字符串格式化(如 f-string)拼接到 SQL 语句中。使用参数化查询,数据库驱动会负责正确地处理特殊字符和数据类型。

7. SQLite 工具推荐

除了命令行工具 sqlite3,还有许多图形用户界面 (GUI) 工具可以帮助你更方便地管理 SQLite 数据库。

  • sqlite3 (命令行): 轻量级,安装方便,适合快速查看和执行简单命令,或者在自动化脚本中使用。
  • DB Browser for SQLite: 免费、开源、跨平台,功能强大且易于使用。提供了直观的界面来创建和修改表、索引、执行 SQL 查询、浏览和编辑数据、导入导出数据等。强烈推荐初学者使用。
  • SQLiteStudio: 另一个免费、开源、跨平台的 SQLite GUI 工具,功能也很全面。
  • 各种 IDE/编辑器插件: 许多集成开发环境 (IDE) 和代码编辑器(如 VS Code, PyCharm)都有支持 SQLite 的插件,可以直接在开发环境中管理数据库。

选择一个适合你的工具可以大大提高开发效率。

8. 使用技巧与注意事项

  • 备份数据库: 备份 SQLite 数据库非常简单,只需要复制 .db 文件即可。在程序运行时进行备份可能需要注意文件锁定问题,可以考虑在程序关闭时备份,或者使用 .backup 命令(通过编程接口或命令行)。
  • VACUUM 命令: 当你删除数据后,数据库文件的大小可能不会立即减小,被删除的空间会被标记为可用,但仍然占用磁盘。执行 VACUUM; 命令可以重组数据库文件,回收未使用的空间,减小文件体积。这可能会比较慢,不应频繁执行。
  • 理解 WAL 模式: 默认情况下,SQLite 使用回滚日志模式。写入时,整个数据库可能会被锁定。启用 Write-Ahead Logging (WAL) 模式 (PRAGMA journal_mode=WAL;) 可以允许多个读取者与一个写入者并发工作,提高并发性能。但 WAL 模式会产生额外的 -wal-shm 文件。
  • 性能优化初步: 对于慢查询,首先考虑是否能添加合适的索引。分析查询语句是否复杂,考虑简化。
  • SQL 注入防范: 再次强调,通过编程语言与 SQLite 交互时,务必使用参数化查询,不要直接拼接字符串。
  • 错误处理: 在编程中,总是要捕获数据库操作可能抛出的异常,并进行适当的处理(例如回滚事务,记录错误信息)。

9. 总结与展望

通过本指南的学习,你应该对 SQLite 有了一个全面的初步认识。我们了解了它的核心特性、优势、局限性,学习了如何安装和使用命令行工具,掌握了数据库的基本概念以及如何使用 SQL 进行核心的 CRUD 操作。此外,我们还初步接触了索引和事务的概念,并通过 Python 示例了解了如何在代码中与 SQLite 交互,以及重要的安全实践——参数化查询。

SQLite 是一个强大而灵活的工具,尤其适合作为应用的本地数据存储。它的简单性使得数据库不再是一个高门槛的技术栈,即便是初学者也能快速上手。

这只是一个开始。你可以继续深入学习:

  • 更复杂的 SQL 查询,例如 JOIN (多表连接)、子查询、窗口函数等。
  • 更高级的事务管理和锁定机制。
  • 如何根据具体应用场景进行性能调优。
  • 与其他编程语言(如 Java, C#, Node.js 等)的 SQLite 接口。
  • SQLite 的内部原理,例如 WAL 模式的细节、索引的实现方式等。

实践是最好的老师。现在就开始尝试使用 SQLite 来存储和管理你的数据吧!无论是开发一个小工具,制作一个桌面应用,还是为你的移动应用添加本地存储,SQLite 都是一个值得信赖的伙伴。

希望这篇详细的指南能帮助你顺利迈出 SQLite 学习的第一步!


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部