SQLite 数据库操作完整指南

发布于:2025-06-22 ⋅ 阅读:(19) ⋅ 点赞:(0)

SQLite 数据库操作完整指南

全面的 SQLite 数据库操作手册,涵盖从基础操作到高级优化的所有内容

目录

**

SQLite

简介与特点
创建和连接数据库
创建表
数据类型和约束
插入数据
查询数据
更新数据
删除数据
多表查询
视图
索引优化
触发器
事务处理
全文搜索
JSON 支持
窗口函数
备份和恢复
性能优化
常用函数
实用技巧和最佳实践
常见问题和解决方案
版本兼容性说明

**


1. SQLite 简介与特点

SQLite 特性

SQLite 是一个轻量级的嵌入式关系型数据库,具有以下特点:

  • 无服务器架构:不需要独立的服务器进程
  • 零配置:无需安装和管理
  • 跨平台:支持所有主流操作系统
  • 单一文件:整个数据库存储在一个文件中
  • 事务性:支持 ACID 事务
  • 体积小:完整的 SQLite 库小于 600KB

使用场景

-- SQLite 适用场景:
-- 1. 移动应用程序
-- 2. 桌面应用程序
-- 3. 嵌入式系统
-- 4. 网站的轻量级数据存储
-- 5. 应用程序配置管理
-- 6. 测试和原型开发

2. 创建和连接数据库

创建数据库

-- 在命令行中创建数据库
sqlite3 company.db

-- 在 SQLite shell 中创建数据库
.open company.db

-- 使用 Python 创建数据库
-- import sqlite3
-- conn = sqlite3.connect('company.db')
-- conn.close()

基本命令

-- 查看 SQLite 版本
SELECT sqlite_version();

-- 查看所有表
.tables

-- 查看表结构
.schema employees

-- 查看数据库信息
.databases

-- 开启列标题显示
.headers on

-- 设置输出模式
.mode column  -- 列模式
.mode list    -- 列表模式
.mode csv     -- CSV模式
.mode table   -- 表格模式

-- 设置列宽
.width 10 20 15

-- 退出 SQLite
.quit

附加数据库

-- 附加另一个数据库
ATTACH DATABASE 'backup.db' AS backup;

-- 查看附加的数据库
.databases

-- 在附加的数据库中创建表
CREATE TABLE backup.employees_backup AS SELECT * FROM main.employees;

-- 分离数据库
DETACH DATABASE backup;

3. 创建表

基本表创建

-- 创建员工表
CREATE TABLE employees (
    employee_id INTEGER PRIMARY KEY AUTOINCREMENT,
    first_name TEXT NOT NULL,
    last_name TEXT NOT NULL,
    email TEXT UNIQUE,
    phone TEXT,
    hire_date DATE DEFAULT CURRENT_DATE,
    salary REAL CHECK (salary > 0),
    department_id INTEGER,
    is_active INTEGER DEFAULT 1,
    created_date DATETIME DEFAULT CURRENT_TIMESTAMP,
    modified_date DATETIME
);

-- 创建部门表
CREATE TABLE departments (
    department_id INTEGER PRIMARY KEY AUTOINCREMENT,
    department_name TEXT NOT NULL UNIQUE,
    location TEXT,
    budget REAL,
    manager_id INTEGER,
    created_date DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (manager_id) REFERENCES employees(employee_id)
);

-- 创建项目表
CREATE TABLE projects (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_name TEXT NOT NULL,
    description TEXT,
    start_date DATE,
    end_date DATE,
    budget REAL,
    status TEXT DEFAULT 'Planning',
    department_id INTEGER,
    FOREIGN KEY (department_id) REFERENCES departments(department_id),
    CHECK (end_date >= start_date)
);

-- 创建多对多关系表
CREATE TABLE employee_projects (
    employee_project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    employee_id INTEGER NOT NULL,
    project_id INTEGER NOT NULL,
    role TEXT,
    assigned_date DATE DEFAULT CURRENT_DATE,
    hours_worked REAL DEFAULT 0,
    FOREIGN KEY (employee_id) REFERENCES employees(employee_id),
    FOREIGN KEY (project_id) REFERENCES projects(project_id),
    UNIQUE(employee_id, project_id)
);

临时表和虚拟表

-- 创建临时表
CREATE TEMP TABLE temp_calculations (
    id INTEGER PRIMARY KEY,
    result REAL,
    calculated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 创建虚拟表(用于全文搜索)
CREATE VIRTUAL TABLE documents USING fts5(
    title,
    content,
    author,
    tags
);

-- WITHOUT ROWID 表(优化存储)
CREATE TABLE config (
    key TEXT PRIMARY KEY,
    value TEXT,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
) WITHOUT ROWID;

从查询结果创建表

-- 创建表并复制数据
CREATE TABLE high_salary_employees AS
SELECT * FROM employees WHERE salary > 50000;

-- 只创建表结构
CREATE TABLE employees_template AS
SELECT * FROM employees WHERE 1=0;

-- 使用 WITH 子句创建表
CREATE TABLE department_summary AS
WITH dept_stats AS (
    SELECT 
        department_id,
        COUNT(*) as employee_count,
        AVG(salary) as avg_salary
    FROM employees
    GROUP BY department_id
)
SELECT 
    d.department_name,
    ds.employee_count,
    ds.avg_salary
FROM departments d
JOIN dept_stats ds ON d.department_id = ds.department_id;

4. 数据类型和约束

SQLite 数据类型

-- SQLite 使用动态类型系统,支持以下存储类:
-- NULL - 空值
-- INTEGER - 整数
-- REAL - 浮点数
-- TEXT - 文本字符串
-- BLOB - 二进制数据

-- 类型亲和性示例
CREATE TABLE type_examples (
    -- INTEGER 亲和性
    int_col INTEGER,
    id_col INT,
    bigint_col BIGINT,
    
    -- TEXT 亲和性
    text_col TEXT,
    varchar_col VARCHAR(100),
    char_col CHAR(10),
    
    -- REAL 亲和性
    real_col REAL,
    float_col FLOAT,
    double_col DOUBLE,
    decimal_col DECIMAL(10,2),
    
    -- NUMERIC 亲和性
    numeric_col NUMERIC,
    boolean_col BOOLEAN,
    date_col DATE,
    datetime_col DATETIME
);

-- 类型转换示例
SELECT 
    CAST('123' AS INTEGER) AS int_value,
    CAST(123 AS TEXT) AS text_value,
    CAST('123.45' AS REAL) AS real_value,
    typeof(123) AS type_of_int,
    typeof('123') AS type_of_text,
    typeof(123.45) AS type_of_real;

约束详解

-- PRIMARY KEY 约束
CREATE TABLE users (
    user_id INTEGER PRIMARY KEY,  -- 自动创建 ROWID 别名
    username TEXT NOT NULL
);

-- 复合主键
CREATE TABLE order_items (
    order_id INTEGER,
    product_id INTEGER,
    quantity INTEGER DEFAULT 1,
    PRIMARY KEY (order_id, product_id)
) WITHOUT ROWID;

-- UNIQUE 约束
CREATE TABLE products (
    product_id INTEGER PRIMARY KEY,
    product_code TEXT UNIQUE,
    product_name TEXT,
    UNIQUE(product_name, product_code)
);

-- CHECK 约束
CREATE TABLE orders (
    order_id INTEGER PRIMARY KEY,
    order_date DATE DEFAULT CURRENT_DATE,
    total_amount REAL CHECK (total_amount >= 0),
    status TEXT CHECK (status IN ('pending', 'processing', 'completed', 'cancelled')),
    CHECK (order_date <= CURRENT_DATE)
);

-- DEFAULT 约束
CREATE TABLE audit_log (
    log_id INTEGER PRIMARY KEY,
    action TEXT NOT NULL,
    user_id INTEGER,
    timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
    ip_address TEXT DEFAULT '0.0.0.0',
    success INTEGER DEFAULT 1
);

-- 外键约束(需要启用)
PRAGMA foreign_keys = ON;

CREATE TABLE customers (
    customer_id INTEGER PRIMARY KEY,
    customer_name TEXT NOT NULL
);

CREATE TABLE customer_orders (
    order_id INTEGER PRIMARY KEY,
    customer_id INTEGER,
    order_date DATE DEFAULT CURRENT_DATE,
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);

修改表结构

-- 添加列
ALTER TABLE employees ADD COLUMN middle_name TEXT;
ALTER TABLE employees ADD COLUMN birth_date DATE;

-- 重命名表
ALTER TABLE employees RENAME TO staff;
ALTER TABLE staff RENAME TO employees;

-- 重命名列(SQLite 3.25.0+)
ALTER TABLE employees RENAME COLUMN phone TO phone_number;

-- 删除列(SQLite 3.35.0+)
ALTER TABLE employees DROP COLUMN middle_name;

-- 对于旧版本 SQLite,需要重建表来删除列
-- 1. 创建新表
CREATE TABLE employees_new AS 
SELECT employee_id, first_name, last_name, email, hire_date, salary, department_id 
FROM employees;

-- 2. 删除旧表
DROP TABLE employees;

-- 3. 重命名新表
ALTER TABLE employees_new RENAME TO employees;

5. 插入数据

基本插入操作

-- 插入单行数据
INSERT INTO departments (department_name, location, budget) 
VALUES ('人力资源部', '北京', 500000.00);

-- 插入多行数据
INSERT INTO departments (department_name, location, budget) VALUES
    ('技术部', '上海', 2000000.00),
    ('销售部', '广州', 1500000.00),
    ('财务部', '北京', 800000.00),
    ('市场部', '深圳', 1200000.00);

-- 插入员工数据
INSERT INTO employees (first_name, last_name, email, phone, hire_date, salary, department_id) VALUES
    ('张', '三', 'zhang.san@company.com', '13800138001', '2023-01-15', 8000.00, 2),
    ('李', '四', 'li.si@company.com', '13800138002', '2023-02-20', 12000.00, 2),
    ('王', '五', 'wang.wu@company.com', '13800138003', '2023-03-10', 7000.00, 3),
    ('赵', '六', 'zhao.liu@company.com', '13800138004', '2023-04-05', 9000.00, 1),
    ('陈', '七', 'chen.qi@company.com', '13800138005', '2023-05-12', 11000.00, 4);

-- 使用 DEFAULT 值
INSERT INTO employees (first_name, last_name, email, salary, department_id)
VALUES ('新', '员工', 'new.employee@company.com', 8500.00, 1);

高级插入操作

-- INSERT OR REPLACE(存在则更新,不存在则插入)
INSERT OR REPLACE INTO products (product_id, product_name, price)
VALUES (1, 'iPhone 15', 999.99);

-- INSERT OR IGNORE(存在则忽略)
INSERT OR IGNORE INTO departments (department_name, location)
VALUES ('技术部', '上海');

-- INSERT OR ABORT(默认行为,违反约束时中止)
INSERT OR ABORT INTO employees (employee_id, first_name, last_name)
VALUES (1, 'Test', 'User');

-- 从其他表插入数据
INSERT INTO employee_backup 
SELECT * FROM employees WHERE department_id = 2;

-- 使用 WITH 子句插入
WITH new_projects AS (
    SELECT 
        'Project-' || department_id AS project_name,
        'Auto-generated project' AS description,
        date('now') AS start_date,
        date('now', '+6 months') AS end_date,
        budget * 0.1 AS project_budget,
        department_id
    FROM departments
    WHERE budget > 500000
)
INSERT INTO projects (project_name, description, start_date, end_date, budget, department_id)
SELECT * FROM new_projects;

-- 条件插入
INSERT INTO employees (first_name, last_name, email, salary, department_id)
SELECT '测试', '用户', 'test.user@company.com', 7500.00, 1
WHERE NOT EXISTS (
    SELECT 1 FROM employees WHERE email = 'test.user@company.com'
);

批量插入优化

-- 使用事务批量插入
BEGIN TRANSACTION;

INSERT INTO large_table (col1, col2, col3) VALUES (1, 'A', 100);
INSERT INTO large_table (col1, col2, col3) VALUES (2, 'B', 200);
-- ... 更多插入语句
INSERT INTO large_table (col1, col2, col3) VALUES (1000, 'Z', 10000);

COMMIT;

-- 使用预处理语句(在应用程序中)
-- PREPARE stmt FROM 'INSERT INTO employees (first_name, last_name, salary) VALUES (?, ?, ?)';
-- EXECUTE stmt USING @fname, @lname, @sal;

-- 关闭同步以提高性能(谨慎使用)
PRAGMA synchronous = OFF;
-- 执行批量插入
PRAGMA synchronous = NORMAL;  -- 恢复默认设置

-- 使用 UPSERT(SQLite 3.24.0+)
INSERT INTO employees (employee_id, first_name, last_name, salary)
VALUES (1, '张', '三', 9000)
ON CONFLICT(employee_id) 
DO UPDATE SET 
    salary = excluded.salary,
    modified_date = CURRENT_TIMESTAMP
WHERE excluded.salary > employees.salary;

6. 查询数据

基本查询

-- 查询所有数据
SELECT * FROM employees;

-- 查询特定列
SELECT employee_id, first_name, last_name, salary 
FROM employees;

-- 使用别名
SELECT 
    e.first_name || ' ' || e.last_name AS full_name,
    e.email AS email_address,
    e.salary AS monthly_salary,
    e.salary * 12 AS annual_salary
FROM employees e;

-- 条件查询
SELECT * FROM employees WHERE salary > 10000;
SELECT * FROM employees WHERE department_id = 2 AND is_active = 1;
SELECT * FROM employees WHERE hire_date BETWEEN '2023-01-01' AND '2023-06-30';

-- IN 和 NOT IN
SELECT * FROM employees WHERE department_id IN (1, 2, 3);
SELECT * FROM employees WHERE department_id NOT IN (4, 5);

-- LIKE 模式匹配
SELECT * FROM employees WHERE first_name LIKE '张%';
SELECT * FROM employees WHERE email LIKE '%@company.com';
SELECT * FROM employees WHERE last_name LIKE '_四';  -- 第二个字是"四"

-- GLOB 模式匹配(大小写敏感)
SELECT * FROM employees WHERE email GLOB '*@company.com';
SELECT * FROM employees WHERE first_name GLOB '[张李王]*';

-- NULL 值处理
SELECT * FROM employees WHERE phone IS NULL;
SELECT * FROM employees WHERE phone IS NOT NULL;

-- 排序
SELECT * FROM employees ORDER BY salary DESC;
SELECT * FROM employees ORDER BY department_id, salary DESC;

-- 限制结果数量
SELECT * FROM employees ORDER BY salary DESC LIMIT 5;
SELECT * FROM employees ORDER BY salary DESC LIMIT 5 OFFSET 10;

聚合函数

-- 基本聚合函数
SELECT 
    COUNT(*) AS total_employees,
    COUNT(DISTINCT department_id) AS department_count,
    AVG(salary) AS average_salary,
    MIN(salary) AS min_salary,
    MAX(salary) AS max_salary,
    SUM(salary) AS total_salary_expense,
    GROUP_CONCAT(first_name || ' ' || last_name, ', ') AS all_names
FROM employees;

-- 分组统计
SELECT 
    department_id,
    COUNT(*) AS employee_count,
    AVG(salary) AS avg_salary,
    MIN(salary) AS min_salary,
    MAX(salary) AS max_salary
FROM employees
GROUP BY department_id
HAVING COUNT(*) > 1;

-- 使用 CASE 语句
SELECT 
    first_name || ' ' || last_name AS full_name,
    salary,
    CASE 
        WHEN salary < 8000 THEN '初级'
        WHEN salary BETWEEN 8000 AND 12000 THEN '中级'
        ELSE '高级'
    END AS level
FROM employees;

-- 分组统计薪资级别
SELECT 
    CASE 
        WHEN salary < 8000 THEN '初级'
        WHEN salary BETWEEN 8000 AND 12000 THEN '中级'
        ELSE '高级'
    END AS salary_level,
    COUNT(*) AS employee_count,
    AVG(salary) AS avg_salary
FROM employees
GROUP BY salary_level;

子查询

-- 标量子查询
SELECT 
    first_name || ' ' || last_name AS full_name,
    salary,
    (SELECT AVG(salary) FROM employees) AS company_avg_salary,
    salary - (SELECT AVG(salary) FROM employees) AS salary_difference
FROM employees;

-- 相关子查询
SELECT 
    e.first_name || ' ' || e.last_name AS full_name,
    e.salary,
    d.department_name,
    (SELECT COUNT(*) FROM employees e2 
     WHERE e2.department_id = e.department_id 
     AND e2.salary > e.salary) AS higher_salary_count
FROM employees e
JOIN departments d ON e.department_id = d.department_id;

-- EXISTS 子查询
SELECT department_name, budget
FROM departments d
WHERE EXISTS (
    SELECT 1 
    FROM employees e 
    WHERE e.department_id = d.department_id 
    AND e.salary > 10000
);

-- NOT EXISTS 子查询
SELECT first_name || ' ' || last_name AS full_name
FROM employees e
WHERE NOT EXISTS (
    SELECT 1 
    FROM employee_projects ep 
    WHERE ep.employee_id = e.employee_id
);

-- IN 子查询
SELECT * FROM employees
WHERE department_id IN (
    SELECT department_id 
    FROM departments 
    WHERE location = '上海'
);

WITH 子句(CTE)

-- 基本 CTE
WITH department_stats AS (
    SELECT 
        department_id,
        COUNT(*) AS employee_count,
        AVG(salary) AS avg_salary,
        SUM(salary) AS total_salary
    FROM employees
    GROUP BY department_id
)
SELECT 
    d.department_name,
    ds.employee_count,
    printf('%.2f', ds.avg_salary) AS avg_salary,
    ds.total_salary,
    d.budget,
    d.budget - ds.total_salary AS remaining_budget
FROM department_stats ds
JOIN departments d ON ds.department_id = d.department_id
WHERE ds.employee_count > 1;

-- 多个 CTE
WITH 
dept_employees AS (
    SELECT department_id, COUNT(*) AS emp_count
    FROM employees
    GROUP BY department_id
),
dept_projects AS (
    SELECT department_id, COUNT(*) AS proj_count
    FROM projects
    GROUP BY department_id
)
SELECT 
    d.department_name,
    COALESCE(de.emp_count, 0) AS employee_count,
    COALESCE(dp.proj_count, 0) AS project_count
FROM departments d
LEFT JOIN dept_employees de ON d.department_id = de.department_id
LEFT JOIN dept_projects dp ON d.department_id = dp.department_id;

-- 递归 CTE
WITH RECURSIVE employee_hierarchy(employee_id, name, level, path) AS (
    -- 初始查询:找到所有经理
    SELECT 
        e.employee_id,
        e.first_name || ' ' || e.last_name AS name,
        0 AS level,
        e.first_name || ' ' || e.last_name AS path
    FROM employees e
    WHERE e.employee_id IN (SELECT manager_id FROM departments WHERE manager_id IS NOT NULL)
    
    UNION ALL
    
    -- 递归查询:找到下属
    SELECT 
        e.employee_id,
        e.first_name || ' ' || e.last_name,
        eh.level + 1,
        eh.path || ' -> ' || e.first_name || ' ' || e.last_name
    FROM employees e
    JOIN employee_hierarchy eh ON e.department_id IN (
        SELECT department_id FROM departments WHERE manager_id = eh.employee_id
    )
    WHERE eh.level < 3  -- 限制递归深度
)
SELECT * FROM employee_hierarchy ORDER BY level, name;

7. 更新数据

基本更新操作

-- 更新单个字段
UPDATE employees 
SET salary = 9000.00 
WHERE employee_id = 1;

-- 更新多个字段
UPDATE employees 
SET 
    phone = '13900139001',
    email = 'zhang.san.new@company.com',
    modified_date = CURRENT_TIMESTAMP
WHERE employee_id = 1;

-- 条件更新
UPDATE employees 
SET salary = salary * 1.1 
WHERE department_id = 2 AND salary < 10000;

-- 使用 CASE 语句更新
UPDATE employees
SET salary = CASE 
    WHEN department_id = 1 THEN salary * 1.05
    WHEN department_id = 2 THEN salary * 1.10
    WHEN department_id = 3 THEN salary * 1.08
    ELSE salary * 1.03
END,
modified_date = CURRENT_TIMESTAMP;

高级更新操作

-- 使用子查询更新
UPDATE employees 
SET salary = (
    SELECT AVG(salary) * 1.1 
    FROM employees e2 
    WHERE e2.department_id = employees.department_id
)
WHERE salary < (
    SELECT AVG(salary) 
    FROM employees e3 
    WHERE e3.department_id = employees.department_id
);

-- 从其他表更新数据
UPDATE employees
SET salary = s.new_salary
FROM (
    SELECT employee_id, salary * 1.1 AS new_salary
    FROM employees
    WHERE department_id = 2
) s
WHERE employees.employee_id = s.employee_id;

-- 使用 CTE 更新
WITH salary_updates AS (
    SELECT 
        employee_id,
        salary * 1.15 AS new_salary
    FROM employees
    WHERE employee_id IN (
        SELECT employee_id
        FROM employees
        ORDER BY salary DESC
        LIMIT 5
    )
)
UPDATE employees
SET salary = su.new_salary,
    modified_date = CURRENT_TIMESTAMP
FROM salary_updates su
WHERE employees.employee_id = su.employee_id;

-- UPDATE OR REPLACE
UPDATE OR REPLACE employees
SET email = 'new.email@company.com'
WHERE employee_id = 1;

批量更新优化

-- 使用事务批量更新
BEGIN TRANSACTION;

UPDATE employees SET salary = salary * 1.05 WHERE department_id = 1;
UPDATE employees SET salary = salary * 1.10 WHERE department_id = 2;
UPDATE employees SET salary = salary * 1.08 WHERE department_id = 3;

COMMIT;

-- 使用临时表批量更新
-- 1. 创建临时表
CREATE TEMP TABLE salary_updates (
    employee_id INTEGER PRIMARY KEY,
    new_salary REAL
);

-- 2. 插入更新数据
INSERT INTO salary_updates (employee_id, new_salary) VALUES
    (1, 9500.00),
    (2, 13000.00),
    (3, 7700.00);

-- 3. 执行批量更新
UPDATE employees
SET salary = su.new_salary,
    modified_date = CURRENT_TIMESTAMP
FROM salary_updates su
WHERE employees.employee_id = su.employee_id;

-- 4. 清理临时表
DROP TABLE salary_updates;

8. 删除数据

基本删除操作

-- 删除单条记录
DELETE FROM employees WHERE employee_id = 100;

-- 条件删除
DELETE FROM employees 
WHERE salary < 5000 AND is_active = 0;

-- 删除所有记录(保留表结构)
DELETE FROM temp_table;

-- 使用子查询删除
DELETE FROM employee_projects
WHERE project_id IN (
    SELECT project_id 
    FROM projects 
    WHERE status = 'cancelled'
);

-- 删除重复数据
DELETE FROM employees
WHERE rowid NOT IN (
    SELECT MIN(rowid)
    FROM employees
    GROUP BY email
);

高级删除操作

-- 使用 CTE 删除
WITH inactive_employees AS (
    SELECT employee_id
    FROM employees
    WHERE is_active = 0
    AND date(modified_date) < date('now', '-2 years')
)
DELETE FROM employee_projects
WHERE employee_id IN (SELECT employee_id FROM inactive_employees);

-- 限制删除数量
DELETE FROM log_table
WHERE log_id IN (
    SELECT log_id
    FROM log_table
    WHERE timestamp < date('now', '-30 days')
    ORDER BY timestamp
    LIMIT 1000
);

-- 级联删除(需要开启外键约束)
PRAGMA foreign_keys = ON;

-- 删除部门(会级联删除相关员工)
DELETE FROM departments WHERE department_id = 5;

清空和删除表

-- 删除表中所有数据(快速,重置自增ID)
DELETE FROM employees;
VACUUM;  -- 回收空间

-- 删除表
DROP TABLE IF EXISTS temp_table;

-- 删除多个表
DROP TABLE IF EXISTS table1, table2, table3;

-- 删除视图
DROP VIEW IF EXISTS employee_view;

-- 删除索引
DROP INDEX IF EXISTS idx_employees_email;

-- 删除触发器
DROP TRIGGER IF EXISTS update_modified_date;

9. 多表查询

内连接(INNER JOIN)

-- 基本内连接
SELECT 
    e.first_name || ' ' || e.last_name AS employee_name,
    e.email,
    e.salary,
    d.department_name,
    d.location
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id;

-- 使用 USING 子句
SELECT 
    e.first_name || ' ' || e.last_name AS employee_name,
    d.department_name
FROM employees e
INNER JOIN departments d USING (department_id);

-- 多表连接
SELECT 
    e.first_name || ' ' || e.last_name AS employee_name,
    d.department_name,
    p.project_name,
    ep.role,
    ep.hours_worked
FROM employees e
INNER JOIN employee_projects ep ON e.employee_id = ep.employee_id
INNER JOIN projects p ON ep.project_id = p.project_id
INNER JOIN departments d ON e.department_id = d.department_id
WHERE ep.hours_worked > 100;

左连接(LEFT JOIN)

-- 查询所有员工及其部门(包括未分配部门的员工)
SELECT 
    e.first_name || ' ' || e.last_name AS employee_name,
    e.email,
    e.salary,
    COALESCE(d.department_name, '未分配部门') AS department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id;

-- 查询所有部门及其员工数量
SELECT 
    d.department_name,
    d.location,
    d.budget,
    COUNT(e.employee_id) AS employee_count,
    COALESCE(AVG(e.salary), 0) AS avg_salary
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id
GROUP BY d.department_id, d.department_name, d.location, d.budget;

-- 查找没有员工的部门
SELECT d.*
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id
WHERE e.employee_id IS NULL;

交叉连接(CROSS JOIN)

-- 生成所有可能的员工-项目组合
SELECT 
    e.first_name || ' ' || e.last_name AS employee_name,
    p.project_name
FROM employees e
CROSS JOIN projects p
WHERE e.department_id = p.department_id
AND NOT EXISTS (
    SELECT 1 FROM employee_projects ep 
    WHERE ep.employee_id = e.employee_id 
    AND ep.project_id = p.project_id
);

-- 生成日期序列
WITH RECURSIVE dates(date) AS (
    SELECT date('now', '-30 days')
    UNION ALL
    SELECT date(date, '+1 day')
    FROM dates
    WHERE date < date('now')
)
SELECT * FROM dates;

自连接(Self JOIN)

-- 查找同部门的员工配对
SELECT 
    e1.first_name || ' ' || e1.last_name AS employee1,
    e2.first_name || ' ' || e2.last_name AS employee2,
    d.department_name
FROM employees e1
INNER JOIN employees e2 ON e1.department_id = e2.department_id 
    AND e1.employee_id < e2.employee_id
INNER JOIN departments d ON e1.department_id = d.department_id;

-- 查找员工的上级
SELECT 
    e.first_name || ' ' || e.last_name AS employee_name,
    m.first_name || ' ' || m.last_name AS manager_name,
    d.department_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id
LEFT JOIN employees m ON d.manager_id = m.employee_id;

UNION 操作

-- UNION(去重)
SELECT first_name || ' ' || last_name AS name, 'Employee' AS type, salary AS amount
FROM employees
UNION
SELECT department_name AS name, 'Department' AS type, budget AS amount
FROM departments
ORDER BY amount DESC;

-- UNION ALL(保留重复)
SELECT department_id, 'Employee' AS source FROM employees
UNION ALL
SELECT department_id, 'Project' AS source FROM projects
ORDER BY department_id, source;

-- EXCEPT(差集)
SELECT employee_id FROM employees
EXCEPT
SELECT DISTINCT employee_id FROM employee_projects;

-- INTERSECT(交集)
SELECT department_id FROM employees
INTERSECT
SELECT department_id FROM projects;

10. 视图

创建基本视图

-- 创建简单视图
CREATE VIEW employee_basic_info AS
SELECT 
    employee_id,
    first_name || ' ' || last_name AS full_name,
    email,
    phone,
    hire_date
FROM employees
WHERE is_active = 1;

-- 创建带 JOIN 的视图
CREATE VIEW employee_department_info AS
SELECT 
    e.employee_id,
    e.first_name || ' ' || e.last_name AS full_name,
    e.email,
    e.salary,
    d.department_name,
    d.location,
    m.first_name || ' ' || m.last_name AS manager_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id
LEFT JOIN employees m ON d.manager_id = m.employee_id
WHERE e.is_active = 1;

-- 使用视图
SELECT * FROM employee_department_info WHERE salary > 10000;

创建高级视图

-- 带聚合的视图
CREATE VIEW department_statistics AS
SELECT 
    d.department_id,
    d.department_name,
    d.location,
    d.budget,
    COUNT(e.employee_id) AS employee_count,
    COALESCE(AVG(e.salary), 0) AS average_salary,
    COALESCE(SUM(e.salary), 0) AS total_salary_expense,
    d.budget - COALESCE(SUM(e.salary), 0) AS remaining_budget
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id AND e.is_active = 1
GROUP BY d.department_id, d.department_name, d.location, d.budget;

-- 带 CTE 的视图
CREATE VIEW employee_ranking AS
WITH salary_ranking AS (
    SELECT 
        employee_id,
        first_name || ' ' || last_name AS full_name,
        salary,
        department_id,
        ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dept_salary_rank,
        RANK() OVER (ORDER BY salary DESC) AS company_salary_rank
    FROM employees
    WHERE is_active = 1
)
SELECT * FROM salary_ranking;

-- 带 UNION 的视图
CREATE VIEW all_contacts AS
SELECT 
    'Employee' AS contact_type,
    employee_id AS contact_id,
    first_name || ' ' || last_name AS name,
    email,
    phone
FROM employees
WHERE is_active = 1
UNION ALL
SELECT 
    'Manager' AS contact_type,
    m.employee_id AS contact_id,
    m.first_name || ' ' || m.last_name AS name,
    m.email,
    m.phone
FROM departments d
INNER JOIN employees m ON d.manager_id = m.employee_id;

临时视图

-- 创建临时视图
CREATE TEMP VIEW temp_high_salary AS
SELECT * FROM employees WHERE salary > 10000;

-- 使用临时视图
SELECT * FROM temp_high_salary;

-- 临时视图在会话结束时自动删除

视图管理

-- 查看所有视图
SELECT name, sql 
FROM sqlite_master 
WHERE type = 'view';

-- 查看特定视图的定义
SELECT sql 
FROM sqlite_master 
WHERE type = 'view' AND name = 'employee_department_info';

-- 替换视图
DROP VIEW IF EXISTS employee_basic_info;
CREATE VIEW employee_basic_info AS
SELECT 
    employee_id,
    first_name || ' ' || last_name AS full_name,
    email,
    phone,
    hire_date,
    department_id  -- 新增字段
FROM employees
WHERE is_active = 1;

-- 删除视图
DROP VIEW IF EXISTS employee_basic_info;

11. 索引优化

创建索引

-- 创建简单索引
CREATE INDEX idx_employees_last_name ON employees(last_name);

-- 创建唯一索引
CREATE UNIQUE INDEX idx_employees_email ON employees(email);

-- 创建复合索引
CREATE INDEX idx_employees_dept_salary ON employees(department_id, salary DESC);

-- 创建条件索引(部分索引)
CREATE INDEX idx_active_employees_salary ON employees(salary) 
WHERE is_active = 1;

-- 创建表达式索引
CREATE INDEX idx_employees_full_name ON employees(first_name || ' ' || last_name);

-- 创建覆盖索引
CREATE INDEX idx_employees_email_covering ON employees(email) 
WHERE email IS NOT NULL;

索引分析

-- 查看所有索引
SELECT 
    m.name AS table_name,
    il.name AS index_name,
    il.unique,
    il.origin,
    il.partial
FROM sqlite_master m
JOIN pragma_index_list(m.name) il
WHERE m.type = 'table'
ORDER BY m.name, il.name;

-- 查看索引详细信息
SELECT 
    m.name AS table_name,
    ii.name AS index_name,
    ii.seqno AS column_position,
    ii.cid AS column_id,
    ii.name AS column_name,
    ii.desc,
    ii.coll,
    ii.key
FROM sqlite_master m,
     pragma_index_list(m.name) il,
     pragma_index_info(il.name) ii
WHERE m.type = 'table'
AND m.name = 'employees';

-- 分析表和索引
ANALYZE;
ANALYZE employees;

-- 查看统计信息
SELECT * FROM sqlite_stat1;
SELECT * FROM sqlite_stat4;

查询计划分析

-- 使用 EXPLAIN QUERY PLAN
EXPLAIN QUERY PLAN
SELECT * FROM employees WHERE department_id = 2;

-- 分析复杂查询
EXPLAIN QUERY PLAN
SELECT 
    e.first_name || ' ' || e.last_name AS employee_name,
    d.department_name,
    e.salary
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id
WHERE e.salary > 10000
ORDER BY e.salary DESC;

-- 使用 EXPLAIN(详细执行计划)
EXPLAIN
SELECT * FROM employees WHERE email = 'zhang.san@company.com';

索引优化策略

-- 删除未使用的索引
DROP INDEX IF EXISTS idx_unused_index;

-- 重建索引
REINDEX;  -- 重建所有索引
REINDEX employees;  -- 重建特定表的索引
REINDEX idx_employees_email;  -- 重建特定索引

-- 优化数据库
VACUUM;  -- 清理并重组数据库
VACUUM INTO 'backup.db';  -- 创建优化后的副本

-- 自动索引
-- SQLite 会为以下情况自动创建索引:
-- 1. PRIMARY KEY 列
-- 2. UNIQUE 约束列

-- 禁用自动索引(用于测试)
PRAGMA automatic_index = OFF;

-- 查看查询优化器的选择
.eqp on  -- 在 SQLite shell 中启用
SELECT * FROM employees WHERE salary > 10000;
.eqp off

12. 触发器

创建基本触发器

-- BEFORE INSERT 触发器
CREATE TRIGGER validate_employee_salary
BEFORE INSERT ON employees
FOR EACH ROW
WHEN NEW.salary <= 0
BEGIN
    SELECT RAISE(ABORT, '薪资必须大于0');
END;

-- AFTER INSERT 触发器
CREATE TRIGGER log_new_employee
AFTER INSERT ON employees
FOR EACH ROW
BEGIN
    INSERT INTO audit_log (action, table_name, record_id, user, timestamp)
    VALUES ('INSERT', 'employees', NEW.employee_id, 'system', CURRENT_TIMESTAMP);
END;

-- BEFORE UPDATE 触发器
CREATE TRIGGER update_modified_date
BEFORE UPDATE ON employees
FOR EACH ROW
BEGIN
    UPDATE employees 
    SET modified_date = CURRENT_TIMESTAMP 
    WHERE employee_id = NEW.employee_id;
END;

-- AFTER UPDATE 触发器
CREATE TRIGGER log_salary_changes
AFTER UPDATE OF salary ON employees
FOR EACH ROW
WHEN OLD.salary != NEW.salary
BEGIN
    INSERT INTO salary_history (employee_id, old_salary, new_salary, change_date)
    VALUES (NEW.employee_id, OLD.salary, NEW.salary, CURRENT_TIMESTAMP);
END;

-- BEFORE DELETE 触发器
CREATE TRIGGER prevent_manager_delete
BEFORE DELETE ON employees
FOR EACH ROW
WHEN EXISTS (SELECT 1 FROM departments WHERE manager_id = OLD.employee_id)
BEGIN
    SELECT RAISE(ABORT, '不能删除部门经理');
END;

-- AFTER DELETE 触发器
CREATE TRIGGER archive_deleted_employee
AFTER DELETE ON employees
FOR EACH ROW
BEGIN
    INSERT INTO employees_archive 
    SELECT *, CURRENT_TIMESTAMP AS deleted_date 
    FROM employees 
    WHERE employee_id = OLD.employee_id;
END;

高级触发器

-- 多条件触发器
CREATE TRIGGER complex_validation
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
    -- 验证邮箱格式
    SELECT CASE
        WHEN NEW.email NOT LIKE '%_@_%.__%' THEN
            RAISE(ABORT, '邮箱格式无效')
    END;
    
    -- 验证部门存在
    SELECT CASE
        WHEN NOT EXISTS (SELECT 1 FROM departments WHERE department_id = NEW.department_id) THEN
            RAISE(ABORT, '部门不存在')
    END;
    
    -- 验证薪资范围
    SELECT CASE
        WHEN NEW.salary < 3000 OR NEW.salary > 100000 THEN
            RAISE(ABORT, '薪资超出有效范围')
    END;
END;

-- 级联更新触发器
CREATE TRIGGER cascade_department_update
AFTER UPDATE OF department_name ON departments
FOR EACH ROW
BEGIN
    -- 更新相关日志
    INSERT INTO audit_log (action, details, timestamp)
    VALUES ('部门更名', '从 ' || OLD.department_name || ' 改为 ' || NEW.department_name, CURRENT_TIMESTAMP);
    
    -- 通知相关员工(模拟)
    UPDATE notifications 
    SET message = '您的部门已更名为:' || NEW.department_name,
        created_at = CURRENT_TIMESTAMP
    WHERE employee_id IN (
        SELECT employee_id FROM employees WHERE department_id = NEW.department_id
    );
END;

-- INSTEAD OF 触发器(用于视图)
CREATE VIEW employee_summary AS
SELECT 
    e.employee_id,
    e.first_name || ' ' || e.last_name AS full_name,
    e.salary,
    d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;

CREATE TRIGGER update_employee_summary
INSTEAD OF UPDATE ON employee_summary
FOR EACH ROW
BEGIN
    UPDATE employees 
    SET salary = NEW.salary
    WHERE employee_id = NEW.employee_id;
END;

触发器管理

-- 查看所有触发器
SELECT name, sql 
FROM sqlite_master 
WHERE type = 'trigger';

-- 查看特定表的触发器
SELECT name, sql 
FROM sqlite_master 
WHERE type = 'trigger' 
AND tbl_name = 'employees';

-- 删除触发器
DROP TRIGGER IF EXISTS update_modified_date;

-- 临时禁用触发器(通过重命名)
ALTER TABLE employees RENAME TO employees_temp;
CREATE TABLE employees AS SELECT * FROM employees_temp;
-- 执行不需要触发器的操作
DROP TABLE employees_temp;
-- 重新创建触发器

13. 事务处理

基本事务操作

-- 开始事务
BEGIN TRANSACTION;
-- 或
BEGIN;

-- 执行操作
UPDATE employees SET salary = salary * 1.1 WHERE department_id = 2;
INSERT INTO salary_history (employee_id, old_salary, new_salary, change_date)
SELECT employee_id, salary / 1.1, salary, CURRENT_TIMESTAMP
FROM employees WHERE department_id = 2;

-- 提交事务
COMMIT;

-- 回滚事务
BEGIN TRANSACTION;
DELETE FROM employees WHERE employee_id = 1;
-- 发现错误,回滚
ROLLBACK;

事务隔离

-- 查看当前隔离级别
PRAGMA read_uncommitted;

-- 设置隔离级别
PRAGMA read_uncommitted = TRUE;  -- 允许脏读
PRAGMA read_uncommitted = FALSE; -- 默认,不允许脏读

-- 事务模式
BEGIN DEFERRED;    -- 默认,延迟获取锁
BEGIN IMMEDIATE;   -- 立即获取保留锁
BEGIN EXCLUSIVE;   -- 立即获取排他锁

-- IMMEDIATE 事务示例
BEGIN IMMEDIATE;
UPDATE employees SET salary = salary * 1.05;
-- 其他连接无法写入,但可以读取
COMMIT;

-- EXCLUSIVE 事务示例
BEGIN EXCLUSIVE;
-- 批量数据导入或大规模更新
DELETE FROM old_data;
INSERT INTO new_data SELECT * FROM temp_import;
COMMIT;

保存点(Savepoint)

BEGIN TRANSACTION;

-- 第一阶段操作
UPDATE employees SET salary = salary * 1.1 WHERE department_id = 1;
SAVEPOINT stage1;

-- 第二阶段操作
UPDATE employees SET salary = salary * 1.1 WHERE department_id = 2;
SAVEPOINT stage2;

-- 第三阶段操作出错
UPDATE employees SET salary = 'invalid';  -- 这会出错

-- 回滚到 stage2
ROLLBACK TO SAVEPOINT stage2;

-- 继续其他操作
UPDATE employees SET salary = salary * 1.05 WHERE department_id = 2;

-- 提交事务
COMMIT;

-- 释放保存点
RELEASE SAVEPOINT stage1;

事务错误处理

-- 使用触发器实现约束和错误处理
CREATE TRIGGER enforce_salary_increase
BEFORE UPDATE OF salary ON employees
FOR EACH ROW
WHEN NEW.salary < OLD.salary
BEGIN
    SELECT RAISE(ABORT, '不允许降低薪资');
END;

-- 在应用程序中处理事务错误(伪代码)
/*
try {
    db.execute("BEGIN TRANSACTION");
    db.execute("UPDATE employees SET salary = salary * 1.1");
    db.execute("INSERT INTO salary_log ...");
    db.execute("COMMIT");
} catch (error) {
    db.execute("ROLLBACK");
    console.error("事务失败:", error);
}
*/

-- 使用 ON CONFLICT 处理冲突
INSERT INTO employees (employee_id, first_name, last_name, email)
VALUES (1, '张', '三', 'new.email@company.com')
ON CONFLICT(employee_id) DO UPDATE 
SET email = excluded.email;

WAL 模式(Write-Ahead Logging)

-- 启用 WAL 模式
PRAGMA journal_mode = WAL;

-- 查看当前日志模式
PRAGMA journal_mode;

-- WAL 检查点
PRAGMA wal_checkpoint;  -- 手动检查点
PRAGMA wal_checkpoint(PASSIVE);  -- 被动模式
PRAGMA wal_checkpoint(FULL);     -- 完全模式
PRAGMA wal_checkpoint(RESTART);  -- 重启模式
PRAGMA wal_checkpoint(TRUNCATE); -- 截断模式

-- 设置自动检查点阈值
PRAGMA wal_autocheckpoint = 1000;  -- 1000 页后自动检查点

-- 查看 WAL 状态
PRAGMA wal_checkpoint;
-- 返回值:busy, checkpointed, total

14. 全文搜索

创建 FTS5 表

-- 创建基本的全文搜索表
CREATE VIRTUAL TABLE articles USING fts5(
    title, 
    content, 
    author, 
    tags
);

-- 插入数据
INSERT INTO articles (title, content, author, tags) VALUES
    ('SQLite 入门指南', 'SQLite 是一个轻量级的嵌入式数据库...', '张三', 'SQLite,数据库,教程'),
    ('高级 SQL 技巧', '本文介绍一些高级的 SQL 查询技巧...', '李四', 'SQL,查询,优化'),
    ('数据库性能优化', '如何优化数据库查询性能...', '王五', '性能,优化,索引');

-- 创建带列权重的 FTS 表
CREATE VIRTUAL TABLE weighted_articles USING fts5(
    title, 
    content, 
    author, 
    tags,
    tokenize = 'unicode61'
);

-- 创建带外部内容的 FTS 表
CREATE TABLE article_data (
    id INTEGER PRIMARY KEY,
    title TEXT,
    content TEXT,
    author TEXT,
    created_date DATE
);

CREATE VIRTUAL TABLE article_fts USING fts5(
    title, 
    content,
    content = article_data,
    content_rowid = id
);

全文搜索查询

-- 基本搜索
SELECT * FROM articles WHERE articles MATCH 'SQLite';

-- 搜索特定列
SELECT * FROM articles WHERE title MATCH 'SQLite';
SELECT * FROM articles WHERE content MATCH '优化';

-- 短语搜索
SELECT * FROM articles WHERE articles MATCH '"数据库 性能"';

-- 布尔搜索
SELECT * FROM articles WHERE articles MATCH 'SQLite AND 教程';
SELECT * FROM articles WHERE articles MATCH 'SQLite OR MySQL';
SELECT * FROM articles WHERE articles MATCH 'SQLite NOT MySQL';

-- 前缀搜索
SELECT * FROM articles WHERE articles MATCH 'SQL*';

-- NEAR 搜索
SELECT * FROM articles WHERE articles MATCH 'NEAR(SQLite 数据库, 5)';

-- 使用 rank 函数
SELECT 
    title,
    content,
    rank
FROM articles 
WHERE articles MATCH 'SQLite' 
ORDER BY rank;

-- 高亮显示搜索结果
SELECT 
    highlight(articles, 0, '<b>', '</b>') AS highlighted_title,
    snippet(articles, 1, '<b>', '</b>', '...', 10) AS content_snippet
FROM articles 
WHERE articles MATCH 'SQLite';

FTS5 高级功能

-- 自定义分词器
CREATE VIRTUAL TABLE chinese_articles USING fts5(
    title, 
    content,
    tokenize = 'unicode61 remove_diacritics 2'
);

-- 使用辅助函数
-- bm25() - 相关性评分
SELECT 
    title,
    bm25(articles, 1.2, 0.75) AS score
FROM articles 
WHERE articles MATCH 'SQLite'
ORDER BY score DESC;

-- 获取匹配信息
SELECT 
    title,
    length(matchinfo(articles, 'pcnalx')) AS match_info
FROM articles 
WHERE articles MATCH 'SQLite';

-- 列权重配置
INSERT INTO articles(articles, rank) VALUES('rank', 'bm25(10.0, 5.0, 1.0, 0.5)');

-- 同义词支持(使用触发器)
CREATE TRIGGER articles_ai AFTER INSERT ON articles BEGIN
    INSERT INTO articles(articles) VALUES('insert', NEW.rowid || ' ' || 
        REPLACE(REPLACE(NEW.content, 'database', 'database db'), 'SQL', 'SQL Structured Query Language'));
END;

15. JSON 支持

JSON 数据存储

-- 创建包含 JSON 列的表
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    email TEXT UNIQUE,
    preferences JSON,
    metadata JSON DEFAULT '{}'
);

-- 插入 JSON 数据
INSERT INTO users (name, email, preferences, metadata) VALUES
    ('张三', 'zhang@example.com', 
     '{"theme": "dark", "language": "zh-CN", "notifications": true}',
     '{"login_count": 5, "last_login": "2024-01-15"}'),
    ('李四', 'li@example.com',
     '{"theme": "light", "language": "en-US", "notifications": false}',
     '{"login_count": 10, "last_login": "2024-01-16"}');

-- 创建订单表(嵌套 JSON)
CREATE TABLE orders (
    order_id INTEGER PRIMARY KEY,
    customer_id INTEGER,
    order_date DATE DEFAULT CURRENT_DATE,
    items JSON,
    shipping_address JSON,
    total_amount REAL
);

INSERT INTO orders (customer_id, items, shipping_address, total_amount) VALUES
    (1, 
     '[{"product_id": 101, "name": "iPhone 15", "quantity": 1, "price": 999.99},
       {"product_id": 102, "name": "AirPods", "quantity": 2, "price": 199.99}]',
     '{"street": "123 Main St", "city": "Beijing", "postal_code": "100000"}',
     1399.97);

JSON 查询函数

-- json_extract() - 提取 JSON 值
SELECT 
    name,
    json_extract(preferences, '$.theme') AS theme,
    json_extract(preferences, '$.language') AS language,
    json_extract(metadata, '$.login_count') AS login_count
FROM users;

-- 使用 -> 和 ->> 操作符(SQLite 3.38.0+)
SELECT 
    name,
    preferences -> '$.theme' AS theme_json,
    preferences ->> '$.theme' AS theme_text,
    metadata ->> '$.last_login' AS last_login
FROM users;

-- 查询嵌套 JSON
SELECT 
    order_id,
    json_extract(items, '$[0].name') AS first_item_name,
    json_extract(items, '$[0].price') AS first_item_price,
    json_extract(shipping_address, '$.city') AS city
FROM orders;

-- json_type() - 获取 JSON 值类型
SELECT 
    name,
    json_type(preferences) AS pref_type,
    json_type(preferences, '$.notifications') AS notif_type
FROM users;

-- json_array_length() - 获取数组长度
SELECT 
    order_id,
    json_array_length(items) AS item_count
FROM orders;

JSON 修改函数

-- json_set() - 设置或更新值
UPDATE users 
SET preferences = json_set(preferences, '$.theme', 'dark')
WHERE id = 2;

-- json_insert() - 仅插入新值
UPDATE users 
SET preferences = json_insert(preferences, '$.new_feature', true)
WHERE id = 1;

-- json_replace() - 仅替换现有值
UPDATE users 
SET preferences = json_replace(preferences, '$.language', 'ja-JP')
WHERE id = 1;

-- json_remove() - 删除值
UPDATE users 
SET preferences = json_remove(preferences, '$.notifications')
WHERE id = 2;

-- json_patch() - 应用 JSON 补丁
UPDATE users 
SET metadata = json_patch(metadata, '{"login_count": 15, "vip": true}')
WHERE id = 1;

-- 更新嵌套数组
UPDATE orders 
SET items = json_set(
    items, 
    '$[0].quantity', 
    json_extract(items, '$[0].quantity') + 1
)
WHERE order_id = 1;

JSON 聚合和表函数

-- json_group_array() - 聚合为 JSON 数组
SELECT 
    department_id,
    json_group_array(first_name || ' ' || last_name) AS employee_names
FROM employees
GROUP BY department_id;

-- json_group_object() - 聚合为 JSON 对象
SELECT json_group_object(
    email,
    json_object('name', first_name || ' ' || last_name, 'salary', salary)
) AS employee_map
FROM employees;

-- json_each() - 遍历 JSON 对象
SELECT 
    u.name,
    j.key,
    j.value
FROM users u,
     json_each(u.preferences) j;

-- json_tree() - 遍历整个 JSON 结构
SELECT 
    key,
    value,
    type,
    path
FROM orders,
     json_tree(orders.items)
WHERE type != 'object';

-- 查询包含特定项目的订单
SELECT DISTINCT order_id
FROM orders, json_each(orders.items)
WHERE json_extract(value, '$.product_id') = 101;

JSON 索引和性能

-- 为 JSON 提取值创建索引
CREATE INDEX idx_user_theme ON users(json_extract(preferences, '$.theme'));
CREATE INDEX idx_user_language ON users(json_extract(preferences, '$.language'));

-- 使用生成列优化查询
ALTER TABLE users ADD COLUMN theme TEXT 
    GENERATED ALWAYS AS (json_extract(preferences, '$.theme')) STORED;

CREATE INDEX idx_theme ON users(theme);

-- 查询优化后的列
SELECT * FROM users WHERE theme = 'dark';

-- 创建 JSON 验证触发器
CREATE TRIGGER validate_json_preferences
BEFORE INSERT ON users
FOR EACH ROW
WHEN json_type(NEW.preferences) != 'object'
BEGIN
    SELECT RAISE(ABORT, 'preferences 必须是有效的 JSON 对象');
END;

16. 窗口函数

排名函数

-- ROW_NUMBER() - 行号
SELECT 
    first_name || ' ' || last_name AS full_name,
    salary,
    department_id,
    ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num
FROM employees;

-- RANK() - 排名(有并列)
SELECT 
    first_name || ' ' || last_name AS full_name,
    salary,
    RANK() OVER (ORDER BY salary DESC) AS salary_rank
FROM employees;

-- DENSE_RANK() - 密集排名
SELECT 
    first_name || ' ' || last_name AS full_name,
    salary,
    DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank
FROM employees;

-- NTILE() - 分组
SELECT 
    first_name || ' ' || last_name AS full_name,
    salary,
    NTILE(4) OVER (ORDER BY salary DESC) AS quartile
FROM employees;

-- PERCENT_RANK() - 百分比排名
SELECT 
    first_name || ' ' || last_name AS full_name,
    salary,
    ROUND(PERCENT_RANK() OVER (ORDER BY salary DESC) * 100, 2) AS percentile
FROM employees;

分区窗口函数

-- 部门内排名
SELECT 
    first_name || ' ' || last_name AS full_name,
    salary,
    department_id,
    ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dept_rank
FROM employees;

-- 多个窗口函数
SELECT 
    first_name || ' ' || last_name AS full_name,
    salary,
    department_id,
    ROW_NUMBER() OVER w AS dept_row_num,
    RANK() OVER w AS dept_rank,
    DENSE_RANK() OVER w AS dept_dense_rank,
    ROUND(PERCENT_RANK() OVER w * 100, 2) AS dept_percentile
FROM employees
WINDOW w AS (PARTITION BY department_id ORDER BY salary DESC);

聚合窗口函数

-- 累计聚合
SELECT 
    employee_id,
    first_name || ' ' || last_name AS full_name,
    hire_date,
    salary,
    SUM(salary) OVER (ORDER BY hire_date) AS running_total,
    AVG(salary) OVER (ORDER BY hire_date) AS running_avg,
    COUNT(*) OVER (ORDER BY hire_date) AS running_count
FROM employees;

-- 移动窗口
SELECT 
    employee_id,
    hire_date,
    salary,
    -- 前2行到当前行的平均值
    AVG(salary) OVER (
        ORDER BY hire_date 
        ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
    ) AS moving_avg_3,
    -- 前1行到后1行的和
    SUM(salary) OVER (
        ORDER BY hire_date 
        ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
    ) AS centered_sum_3
FROM employees;

-- 范围窗口
SELECT 
    employee_id,
    hire_date,
    salary,
    -- 过去30天内的平均薪资
    AVG(salary) OVER (
        ORDER BY hire_date
        RANGE BETWEEN INTERVAL '30' DAY PRECEDING AND CURRENT ROW
    ) AS avg_30_days
FROM employees;

值函数

-- LAG() 和 LEAD()
SELECT 
    employee_id,
    first_name || ' ' || last_name AS full_name,
    salary,
    LAG(salary, 1, 0) OVER (ORDER BY salary) AS prev_salary,
    LEAD(salary, 1, 0) OVER (ORDER BY salary) AS next_salary,
    salary - LAG(salary, 1, 0) OVER (ORDER BY salary) AS salary_gap
FROM employees;

-- FIRST_VALUE() 和 LAST_VALUE()
SELECT 
    first_name || ' ' || last_name AS full_name,
    salary,
    department_id,
    FIRST_VALUE(salary) OVER (
        PARTITION BY department_id 
        ORDER BY salary DESC
    ) AS dept_max_salary,
    LAST_VALUE(salary) OVER (
        PARTITION BY department_id 
        ORDER BY salary DESC
        ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
    ) AS dept_min_salary
FROM employees;

-- NTH_VALUE()
SELECT 
    first_name || ' ' || last_name AS full_name,
    salary,
    department_id,
    NTH_VALUE(salary, 2) OVER (
        PARTITION BY department_id 
        ORDER BY salary DESC
    ) AS second_highest_salary
FROM employees;

实际应用示例

-- 计算同比增长
WITH monthly_sales AS (
    SELECT 
        strftime('%Y-%m', order_date) AS month,
        SUM(total_amount) AS total_sales
    FROM orders
    GROUP BY month
)
SELECT 
    month,
    total_sales,
    LAG(total_sales, 12) OVER (ORDER BY month) AS last_year_sales,
    ROUND(
        (total_sales - LAG(total_sales, 12) OVER (ORDER BY month)) * 100.0 / 
        LAG(total_sales, 12) OVER (ORDER BY month), 
        2
    ) AS yoy_growth_percent
FROM monthly_sales;

-- 查找每个部门薪资前3名
WITH ranked_employees AS (
    SELECT 
        *,
        ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rank
    FROM employees
)
SELECT 
    first_name || ' ' || last_name AS full_name,
    salary,
    department_id,
    rank
FROM ranked_employees
WHERE rank <= 3;

-- 计算移动平均线
SELECT 
    date,
    value,
    AVG(value) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma7,
    AVG(value) OVER (ORDER BY date ROWS BETWEEN 29 PRECEDING AND CURRENT ROW) AS ma30
FROM daily_metrics;

17. 备份和恢复

数据库备份方法

-- 方法1:使用 .backup 命令(在 SQLite shell 中)
.backup backup.db
.backup main backup.db  -- 备份主数据库

-- 方法2:使用 VACUUM INTO
VACUUM INTO 'backup.db';

-- 方法3:复制数据库文件(确保没有活动连接)
-- cp company.db company_backup.db

-- 方法4:使用 SQL 导出
.output backup.sql
.dump
.output stdout

-- 导出特定表
.output employees_backup.sql
.dump employees
.output stdout

数据恢复

-- 从备份文件恢复
.restore backup.db

-- 从 SQL 文件恢复
.read backup.sql

-- 使用 ATTACH 恢复特定表
ATTACH DATABASE 'backup.db' AS backup;

-- 恢复单个表
DROP TABLE IF EXISTS employees;
CREATE TABLE employees AS SELECT * FROM backup.employees;

-- 恢复多个表
INSERT INTO employees SELECT * FROM backup.employees;

DETACH DATABASE backup;

增量备份

-- 创建备份日志表
CREATE TABLE backup_log (
    backup_id INTEGER PRIMARY KEY,
    backup_date DATETIME DEFAULT CURRENT_TIMESTAMP,
    last_modified_rowid INTEGER
);

-- 记录最后修改的 rowid
INSERT INTO backup_log (last_modified_rowid)
SELECT MAX(rowid) FROM employees;

-- 增量备份(仅备份新增/修改的记录)
ATTACH DATABASE 'incremental_backup.db' AS incr;

CREATE TABLE incr.employees_delta AS
SELECT * FROM main.employees
WHERE rowid > (SELECT last_modified_rowid FROM backup_log ORDER BY backup_id DESC LIMIT 1);

DETACH DATABASE incr;

自动备份脚本

-- 创建备份存储过程(使用触发器模拟)
CREATE TABLE backup_schedule (
    id INTEGER PRIMARY KEY,
    next_backup DATETIME,
    interval_hours INTEGER DEFAULT 24
);

-- 备份验证
-- 检查备份文件完整性
PRAGMA integrity_check;

-- 检查外键约束
PRAGMA foreign_key_check;

-- 验证备份数据
ATTACH DATABASE 'backup.db' AS backup;
SELECT 
    (SELECT COUNT(*) FROM main.employees) AS main_count,
    (SELECT COUNT(*) FROM backup.employees) AS backup_count,
    CASE 
        WHEN (SELECT COUNT(*) FROM main.employees) = (SELECT COUNT(*) FROM backup.employees)
        THEN '备份完整'
        ELSE '备份不完整'
    END AS status;
DETACH DATABASE backup;

18. 性能优化

查询优化

-- 使用 EXPLAIN QUERY PLAN 分析查询
EXPLAIN QUERY PLAN
SELECT e.*, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE e.salary > 10000;

-- 优化前:使用 OR
SELECT * FROM employees 
WHERE department_id = 1 OR department_id = 2 OR department_id = 3;

-- 优化后:使用 IN
SELECT * FROM employees 
WHERE department_id IN (1, 2, 3);

-- 优化前:使用 NOT IN 子查询
SELECT * FROM employees 
WHERE employee_id NOT IN (SELECT employee_id FROM inactive_employees);

-- 优化后:使用 LEFT JOIN
SELECT e.* 
FROM employees e
LEFT JOIN inactive_employees ie ON e.employee_id = ie.employee_id
WHERE ie.employee_id IS NULL;

-- 优化前:多次查询
SELECT COUNT(*) FROM employees WHERE department_id = 1;
SELECT AVG(salary) FROM employees WHERE department_id = 1;
SELECT MAX(salary) FROM employees WHERE department_id = 1;

-- 优化后:一次查询
SELECT 
    COUNT(*) AS emp_count,
    AVG(salary) AS avg_salary,
    MAX(salary) AS max_salary
FROM employees 
WHERE department_id = 1;

PRAGMA 优化设置

-- 查询优化器设置
PRAGMA optimize;  -- 运行 ANALYZE
PRAGMA analysis_limit = 1000;  -- 限制分析的行数

-- 内存和缓存设置
PRAGMA cache_size = -64000;  -- 64MB 缓存(负值表示 KB)
PRAGMA temp_store = MEMORY;  -- 临时表存储在内存中
PRAGMA mmap_size = 268435456;  -- 256MB 内存映射

-- 日志和同步设置
PRAGMA journal_mode = WAL;  -- Write-Ahead Logging
PRAGMA synchronous = NORMAL;  -- 平衡性能和安全性
PRAGMA wal_autocheckpoint = 1000;  -- 1000页后自动检查点

-- 查询性能设置
PRAGMA threads = 4;  -- 使用4个线程(如果编译时启用)
PRAGMA query_only = FALSE;  -- 允许写操作

-- 页面设置
PRAGMA page_size = 4096;  -- 4KB 页面大小(创建数据库时设置)
PRAGMA auto_vacuum = INCREMENTAL;  -- 增量自动清理

-- 编译选项
PRAGMA compile_options;  -- 查看编译选项

索引优化策略

-- 分析表统计信息
ANALYZE;
ANALYZE employees;

-- 查看查询计划中的索引使用
.eqp on
SELECT * FROM employees WHERE email = 'test@example.com';
.eqp off

-- 强制使用特定索引
SELECT * FROM employees INDEXED BY idx_employees_email 
WHERE email = 'test@example.com';

-- 禁止使用索引
SELECT * FROM employees NOT INDEXED 
WHERE employee_id = 1;

-- 创建覆盖索引减少表访问
CREATE INDEX idx_emp_dept_salary ON employees(department_id, salary, first_name, last_name);

-- 部分索引优化
CREATE INDEX idx_active_employees ON employees(department_id, salary) 
WHERE is_active = 1;

-- 表达式索引
CREATE INDEX idx_employees_lower_email ON employees(lower(email));
SELECT * FROM employees WHERE lower(email) = 'test@example.com';

批量操作优化

-- 批量插入优化
PRAGMA synchronous = OFF;
PRAGMA journal_mode = MEMORY;
BEGIN TRANSACTION;

-- 大量 INSERT 操作
INSERT INTO large_table VALUES (...);
-- ... 更多插入

COMMIT;
PRAGMA synchronous = NORMAL;
PRAGMA journal_mode = WAL;

-- 使用预处理语句(在应用程序中)
-- 创建一次,多次执行

-- 批量更新优化
CREATE TEMP TABLE updates (id INTEGER PRIMARY KEY, new_value TEXT);
INSERT INTO updates VALUES (1, 'value1'), (2, 'value2');

UPDATE main_table 
SET value = updates.new_value
FROM updates
WHERE main_table.id = updates.id;

DROP TABLE updates;

-- 批量删除优化
DELETE FROM large_table
WHERE id IN (
    SELECT id FROM large_table 
    WHERE condition 
    ORDER BY id 
    LIMIT 10000
);

查询结果缓存

-- 使用临时表缓存复杂查询结果
CREATE TEMP TABLE cached_summary AS
SELECT 
    department_id,
    COUNT(*) AS emp_count,
    AVG(salary) AS avg_salary,
    MAX(salary) AS max_salary
FROM employees
GROUP BY department_id;

-- 多次使用缓存结果
SELECT * FROM cached_summary WHERE avg_salary > 10000;
SELECT * FROM cached_summary ORDER BY emp_count DESC;

-- 使用视图缓存常用查询
CREATE VIEW employee_summary AS
SELECT 
    e.*,
    d.department_name,
    m.first_name || ' ' || m.last_name AS manager_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
LEFT JOIN employees m ON d.manager_id = m.employee_id;

-- 定期更新统计信息
CREATE TABLE statistics_cache (
    stat_name TEXT PRIMARY KEY,
    stat_value TEXT,
    last_updated DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TRIGGER update_statistics
AFTER INSERT ON employees
BEGIN
    DELETE FROM statistics_cache WHERE stat_name = 'employee_count';
    INSERT INTO statistics_cache (stat_name, stat_value)
    VALUES ('employee_count', (SELECT COUNT(*) FROM employees));
END;

监控和诊断

-- 数据库统计信息
SELECT * FROM sqlite_stat1;  -- 表和索引统计
SELECT * FROM sqlite_stat4;  -- 详细统计(如果可用)

-- 查看数据库大小
SELECT 
    page_count * page_size / 1024.0 / 1024.0 AS size_mb,
    page_count,
    page_size
FROM pragma_page_count(), pragma_page_size();

-- 查看表大小
SELECT 
    name,
    SUM(pgsize) / 1024.0 / 1024.0 AS size_mb
FROM (
    SELECT name, pageno * page_size AS pgsize
    FROM dbstat
    JOIN pragma_page_size()
)
GROUP BY name
ORDER BY size_mb DESC;

-- 检查碎片
SELECT 
    name,
    100.0 * (1.0 - CAST(used AS REAL) / CAST(pgcnt AS REAL)) AS fragmentation_percent
FROM dbstat
WHERE aggregate = TRUE
ORDER BY fragmentation_percent DESC;

-- 慢查询日志(需要在应用层实现)
CREATE TABLE query_log (
    id INTEGER PRIMARY KEY,
    query TEXT,
    execution_time_ms INTEGER,
    timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 数据库健康检查
PRAGMA integrity_check;
PRAGMA foreign_key_check;
PRAGMA quick_check;

19. 常用函数

字符串函数

-- 字符串连接
SELECT 
    first_name || ' ' || last_name AS full_name,
    printf('%s %s', first_name, last_name) AS formatted_name
FROM employees;

-- 大小写转换
SELECT 
    upper(first_name) AS upper_name,
    lower(email) AS lower_email
FROM employees;

-- 字符串长度
SELECT 
    length(first_name) AS name_length,
    length(email) - length(replace(email, '@', '')) AS at_count
FROM employees;

-- 子字符串
SELECT 
    substr(email, 1, instr(email, '@') - 1) AS username,
    substr(email, instr(email, '@') + 1) AS domain
FROM employees;

-- 字符串替换
SELECT 
    replace(phone, '-', '') AS cleaned_phone,
    replace(replace(phone, '-', ''), ' ', '') AS fully_cleaned_phone
FROM employees;

-- 去除空格
SELECT 
    trim(first_name) AS trimmed_name,
    ltrim(first_name) AS left_trimmed,
    rtrim(first_name) AS right_trimmed,
    trim(first_name, '张') AS custom_trim
FROM employees;

-- 字符串查找
SELECT 
    instr(email, '@') AS at_position,
    CASE 
        WHEN instr(email, '.com') > 0 THEN 'COM域名'
        WHEN instr(email, '.cn') > 0 THEN 'CN域名'
        ELSE '其他域名'
    END AS domain_type
FROM employees;

数学函数

-- 基本数学函数
SELECT 
    abs(-10) AS absolute_value,
    round(3.14159, 2) AS rounded,
    ceil(3.14) AS ceiling,
    floor(3.14) AS floor_value,
    mod(10, 3) AS modulo,
    power(2, 10) AS power_result;

-- 聚合数学函数
SELECT 
    avg(salary) AS average,
    sum(salary) AS total,
    min(salary) AS minimum,
    max(salary) AS maximum,
    count(*) AS count,
    count(DISTINCT department_id) AS unique_departments
FROM employees;

-- 随机数
SELECT 
    random() AS random_number,
    abs(random() % 100) AS random_0_to_99,
    abs(random() % 100) + 1 AS random_1_to_100;

-- 随机选择记录
SELECT * FROM employees ORDER BY random() LIMIT 5;

-- 数学计算
SELECT 
    salary,
    salary * 0.1 AS tax,
    salary * 0.9 AS after_tax,
    round(salary * 12 / 365, 2) AS daily_salary
FROM employees;

日期时间函数

-- 当前日期时间
SELECT 
    date('now') AS current_date,
    time('now') AS current_time,
    datetime('now') AS current_datetime,
    datetime('now', 'localtime') AS local_datetime;

-- 日期格式化
SELECT 
    strftime('%Y-%m-%d', 'now') AS formatted_date,
    strftime('%H:%M:%S', 'now') AS formatted_time,
    strftime('%Y年%m月%d日', 'now') AS chinese_date,
    strftime('%w', 'now') AS day_of_week,  -- 0=周日
    strftime('%W', 'now') AS week_of_year;

-- 日期计算
SELECT 
    date('now', '+1 day') AS tomorrow,
    date('now', '-1 day') AS yesterday,
    date('now', '+1 month') AS next_month,
    date('now', 'start of month') AS month_start,
    date('now', 'start of month', '+1 month', '-1 day') AS month_end;

-- 日期差异
SELECT 
    employee_id,
    hire_date,
    julianday('now') - julianday(hire_date) AS days_employed,
    CAST((julianday('now') - julianday(hire_date)) / 365.25 AS INTEGER) AS years_employed
FROM employees;

-- 工作日计算(排除周末)
WITH RECURSIVE dates(date) AS (
    SELECT date('2024-01-01')
    UNION ALL
    SELECT date(date, '+1 day')
    FROM dates
    WHERE date < date('2024-01-31')
)
SELECT COUNT(*) AS workdays
FROM dates
WHERE strftime('%w', date) NOT IN ('0', '6');  -- 不是周六周日

类型转换函数

-- CAST 函数
SELECT 
    CAST('123' AS INTEGER) AS int_value,
    CAST(123 AS TEXT) AS text_value,
    CAST('123.45' AS REAL) AS real_value,
    CAST(1 AS BOOLEAN) AS bool_value;

-- 类型检查
SELECT 
    typeof(123) AS type_int,
    typeof('123') AS type_text,
    typeof(123.45) AS type_real,
    typeof(NULL) AS type_null,
    typeof(x'0123') AS type_blob;

-- 隐式类型转换
SELECT 
    '123' + 456 AS implicit_sum,  -- 579
    123 || 456 AS concat_numbers,  -- '123456'
    '123' > 45 AS text_compare;  -- 文本比较

-- 数值格式化
SELECT 
    printf('%.2f', 123.456) AS formatted_decimal,
    printf('%04d', 42) AS padded_integer,
    printf('%e', 123456.789) AS scientific,
    hex(255) AS hexadecimal;

条件和逻辑函数

-- CASE 表达式
SELECT 
    first_name,
    salary,
    CASE 
        WHEN salary < 5000 THEN '低'
        WHEN salary < 10000 THEN '中'
        ELSE '高'
    END AS salary_level
FROM employees;

-- COALESCE(返回第一个非NULL值)
SELECT 
    first_name,
    COALESCE(phone, email, '无联系方式') AS contact
FROM employees;

-- NULLIF(相等时返回NULL)
SELECT 
    salary,
    bonus,
    NULLIF(bonus, 0) AS non_zero_bonus,
    salary / NULLIF(bonus, 0) AS salary_bonus_ratio
FROM employee_compensation;

-- IIF(内联IF,SQLite 3.32.0+)
SELECT 
    first_name,
    salary,
    IIF(salary > 10000, '高薪', '普通') AS salary_category
FROM employees;

-- MIN/MAX 用于非数值比较
SELECT 
    MIN('apple', 'banana', 'cherry') AS first_alphabetically,
    MAX('apple', 'banana', 'cherry') AS last_alphabetically;

系统函数

-- SQLite 版本和编译信息
SELECT 
    sqlite_version() AS version,
    sqlite_source_id() AS source_id;

-- 最后插入的 ROWID
INSERT INTO employees (first_name, last_name) VALUES ('测试', '用户');
SELECT last_insert_rowid() AS last_id;

-- 受影响的行数
UPDATE employees SET salary = salary * 1.05 WHERE department_id = 1;
SELECT changes() AS rows_affected;

-- 总变更数
SELECT total_changes() AS total_changes_in_connection;

-- 数据库列表
SELECT * FROM pragma_database_list();

-- 表信息
SELECT * FROM pragma_table_info('employees');

-- 外键信息
SELECT * FROM pragma_foreign_key_list('employees');

20. 实用技巧和最佳实践

数据库设计最佳实践

-- 1. 始终使用主键
CREATE TABLE best_practices (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    -- 其他列
);

-- 2. 适当使用索引
-- 为经常查询的列创建索引
CREATE INDEX idx_email ON users(email);
-- 为外键创建索引
CREATE INDEX idx_user_id ON orders(user_id);

-- 3. 规范化设计
-- 避免数据冗余
-- 不好的设计
CREATE TABLE orders_bad (
    order_id INTEGER PRIMARY KEY,
    customer_name TEXT,
    customer_email TEXT,
    customer_phone TEXT,
    product_name TEXT,
    product_price REAL
);

-- 好的设计
CREATE TABLE customers (
    customer_id INTEGER PRIMARY KEY,
    name TEXT,
    email TEXT,
    phone TEXT
);

CREATE TABLE products (
    product_id INTEGER PRIMARY KEY,
    name TEXT,
    price REAL
);

CREATE TABLE orders_good (
    order_id INTEGER PRIMARY KEY,
    customer_id INTEGER REFERENCES customers(customer_id),
    product_id INTEGER REFERENCES products(product_id),
    quantity INTEGER,
    order_date DATE
);

-- 4. 使用合适的数据类型
CREATE TABLE data_types_example (
    -- 使用 INTEGER 作为主键
    id INTEGER PRIMARY KEY,
    -- 使用 TEXT 存储字符串
    name TEXT NOT NULL,
    -- 使用 REAL 存储小数
    price REAL CHECK (price >= 0),
    -- 使用 INTEGER 存储布尔值
    is_active INTEGER DEFAULT 1 CHECK (is_active IN (0, 1)),
    -- 使用 TEXT 存储日期(ISO 8601格式)
    created_date TEXT DEFAULT CURRENT_TIMESTAMP
);

查询优化技巧

-- 1. 使用 EXPLAIN QUERY PLAN
EXPLAIN QUERY PLAN
SELECT * FROM large_table WHERE column1 = 'value';

-- 2. 避免 SELECT *
-- 不好
SELECT * FROM employees;

-- 好
SELECT employee_id, first_name, last_name, salary 
FROM employees;

-- 3. 使用 LIMIT 限制结果集
SELECT * FROM large_table 
ORDER BY created_date DESC 
LIMIT 100;

-- 4. 优化 JOIN 顺序(小表在前)
-- 假设 departments 表比 employees 表小
SELECT e.*, d.department_name
FROM departments d
JOIN employees e ON d.department_id = e.department_id;

-- 5. 使用子查询代替 JOIN(当只需要存在性检查时)
-- 使用 EXISTS
SELECT * FROM employees e
WHERE EXISTS (
    SELECT 1 FROM departments d 
    WHERE d.department_id = e.department_id 
    AND d.location = 'Beijing'
);

-- 6. 批量操作使用事务
BEGIN TRANSACTION;
-- 多个操作
COMMIT;

数据完整性保护

-- 1. 启用外键约束
PRAGMA foreign_keys = ON;

-- 2. 使用触发器维护数据一致性
CREATE TRIGGER update_inventory_on_order
AFTER INSERT ON order_items
FOR EACH ROW
BEGIN
    UPDATE products 
    SET stock = stock - NEW.quantity
    WHERE product_id = NEW.product_id;
    
    -- 检查库存
    SELECT CASE
        WHEN (SELECT stock FROM products WHERE product_id = NEW.product_id) < 0
        THEN RAISE(ABORT, '库存不足')
    END;
END;

-- 3. 使用 CHECK 约束
CREATE TABLE products (
    product_id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    price REAL CHECK (price > 0),
    stock INTEGER CHECK (stock >= 0),
    created_date TEXT CHECK (created_date IS datetime(created_date))
);

-- 4. 定期检查数据完整性
PRAGMA integrity_check;
PRAGMA foreign_key_check;

安全性建议

-- 1. 使用参数化查询(在应用程序中)
-- 不要这样做:
-- query = "SELECT * FROM users WHERE name = '" + userName + "'";

-- 应该这样做:
-- query = "SELECT * FROM users WHERE name = ?";
-- 然后绑定参数

-- 2. 限制数据库文件权限
-- chmod 600 database.db  # 仅所有者可读写

-- 3. 加密敏感数据
-- 使用 SQLCipher 或在应用层加密

-- 4. 定期备份
-- 创建备份计划脚本

-- 5. 审计日志
CREATE TABLE audit_log (
    log_id INTEGER PRIMARY KEY,
    table_name TEXT,
    operation TEXT,
    user TEXT,
    timestamp TEXT DEFAULT CURRENT_TIMESTAMP,
    old_value TEXT,
    new_value TEXT
);

-- 创建审计触发器
CREATE TRIGGER audit_employees_update
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
    INSERT INTO audit_log (table_name, operation, user, old_value, new_value)
    VALUES (
        'employees',
        'UPDATE',
        'current_user',
        json_object('salary', OLD.salary),
        json_object('salary', NEW.salary)
    );
END;

开发和调试技巧

-- 1. 使用 .mode 和 .headers 改善输出
.mode column
.headers on
.width 20 30 15

-- 2. 保存查询结果
.output results.csv
.mode csv
SELECT * FROM employees;
.output stdout

-- 3. 使用 .timer 测量查询时间
.timer on
SELECT COUNT(*) FROM large_table;
.timer off
-- 4. 创建测试数据
-- 创建数字序列
WITH RECURSIVE series(value) AS (
    SELECT 1
    UNION ALL
    SELECT value + 1 FROM series
    WHERE value < 1000
)
INSERT INTO test_table (id, random_value)
SELECT 
    value,
    abs(random() % 1000)
FROM series;

-- 生成随机字符串
WITH RECURSIVE random_names(id, name) AS (
    SELECT 
        1,
        substr('ABCDEFGHIJKLMNOPQRSTUVWXYZ', abs(random() % 26) + 1, 1) ||
        substr('abcdefghijklmnopqrstuvwxyz', abs(random() % 26) + 1, 1) ||
        substr('abcdefghijklmnopqrstuvwxyz', abs(random() % 26) + 1, 1)
    UNION ALL
    SELECT 
        id + 1,
        substr('ABCDEFGHIJKLMNOPQRSTUVWXYZ', abs(random() % 26) + 1, 1) ||
        substr('abcdefghijklmnopqrstuvwxyz', abs(random() % 26) + 1, 1) ||
        substr('abcdefghijklmnopqrstuvwxyz', abs(random() % 26) + 1, 1)
    FROM random_names
    WHERE id < 100
)
INSERT INTO users_test (username)
SELECT name FROM random_names;

-- 5. 使用 WITH 子句进行复杂调试
WITH debug_info AS (
    SELECT 
        'Total Employees' AS metric,
        COUNT(*) AS value
    FROM employees
    UNION ALL
    SELECT 
        'Average Salary',
        AVG(salary)
    FROM employees
    UNION ALL
    SELECT 
        'Departments',
        COUNT(DISTINCT department_id)
    FROM employees
)
SELECT * FROM debug_info;

-- 6. 创建调试视图
CREATE VIEW debug_employee_stats AS
SELECT 
    e.employee_id,
    e.first_name || ' ' || e.last_name AS full_name,
    e.salary,
    d.department_name,
    e.salary - (SELECT AVG(salary) FROM employees) AS salary_diff_from_avg,
    RANK() OVER (ORDER BY e.salary DESC) AS salary_rank
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id;

-- 7. 使用 PRAGMA 进行诊断
PRAGMA table_info(employees);
PRAGMA index_list(employees);
PRAGMA foreign_key_list(employees);
PRAGMA database_list;
PRAGMA compile_options;

性能监控和分析

-- 创建性能监控表
CREATE TABLE query_performance (
    id INTEGER PRIMARY KEY,
    query_text TEXT,
    execution_time_ms INTEGER,
    rows_affected INTEGER,
    timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 创建慢查询视图
CREATE VIEW slow_queries AS
SELECT 
    query_text,
    execution_time_ms,
    rows_affected,
    timestamp
FROM query_performance
WHERE execution_time_ms > 100
ORDER BY execution_time_ms DESC;

-- 分析表大小和增长
CREATE VIEW table_sizes AS
SELECT 
    name AS table_name,
    SUM(pgsize) AS size_bytes,
    SUM(pgsize) / 1024.0 AS size_kb,
    SUM(pgsize) / 1024.0 / 1024.0 AS size_mb,
    COUNT(*) AS page_count
FROM (
    SELECT name, pageno, pageno * (SELECT page_size FROM pragma_page_size()) AS pgsize
    FROM dbstat
    WHERE pageno > 0
)
GROUP BY name
ORDER BY size_bytes DESC;

-- 索引使用率分析
CREATE VIEW index_usage AS
SELECT 
    m.name AS table_name,
    i.name AS index_name,
    i.unique,
    CASE 
        WHEN i.origin = 'pk' THEN 'PRIMARY KEY'
        WHEN i.origin = 'u' THEN 'UNIQUE'
        ELSE 'NORMAL'
    END AS index_type
FROM sqlite_master m
JOIN pragma_index_list(m.name) i
WHERE m.type = 'table'
ORDER BY m.name, i.name;

21. 常见问题和解决方案

数据库锁定问题

-- 问题:database is locked
-- 解决方案:

-- 1. 检查活动连接
PRAGMA busy_timeout = 5000;  -- 设置5秒超时

-- 2. 使用 WAL 模式减少锁定
PRAGMA journal_mode = WAL;

-- 3. 优化长事务
-- 将长事务拆分为多个短事务
BEGIN IMMEDIATE;
-- 执行部分操作
COMMIT;

BEGIN IMMEDIATE;
-- 执行下一部分操作
COMMIT;

-- 4. 处理死锁
-- 在应用程序中实现重试机制
-- Python 示例:
/*
import sqlite3
import time

def execute_with_retry(conn, query, max_retries=3):
    for i in range(max_retries):
        try:
            cursor = conn.execute(query)
            return cursor
        except sqlite3.OperationalError as e:
            if "locked" in str(e) and i < max_retries - 1:
                time.sleep(0.1 * (i + 1))  # 指数退避
                continue
            raise
*/

性能问题诊断

-- 问题:查询速度慢
-- 诊断步骤:

-- 1. 分析查询计划
EXPLAIN QUERY PLAN
SELECT e.*, d.department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
WHERE e.salary > 10000;

-- 2. 检查索引
-- 查看现有索引
SELECT sql FROM sqlite_master 
WHERE type = 'index' AND tbl_name = 'employees';

-- 创建缺失的索引
CREATE INDEX idx_employees_salary ON employees(salary);

-- 3. 更新统计信息
ANALYZE;

-- 4. 检查表碎片
SELECT 
    name,
    100.0 * (pgsize - used) / pgsize AS fragmentation_percent
FROM dbstat
WHERE aggregate = TRUE
ORDER BY fragmentation_percent DESC;

-- 5. 优化查询
-- 使用覆盖索引
CREATE INDEX idx_covering ON employees(department_id, salary, first_name, last_name);

-- 6. 批量处理
-- 将多个小查询合并为一个大查询

数据完整性问题

-- 问题:外键约束失败
-- 解决方案:

-- 1. 检查外键约束
PRAGMA foreign_key_check;

-- 2. 找出违反约束的记录
SELECT e.*
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
WHERE d.department_id IS NULL AND e.department_id IS NOT NULL;

-- 3. 修复数据
-- 选项1:删除无效记录
DELETE FROM employees 
WHERE department_id NOT IN (SELECT department_id FROM departments);

-- 选项2:更新为有效值
UPDATE employees 
SET department_id = NULL
WHERE department_id NOT IN (SELECT department_id FROM departments);

-- 选项3:添加缺失的父记录
INSERT INTO departments (department_id, department_name)
SELECT DISTINCT e.department_id, 'Unknown Department ' || e.department_id
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
WHERE d.department_id IS NULL AND e.department_id IS NOT NULL;

内存使用问题

-- 问题:内存使用过高
-- 解决方案:

-- 1. 调整缓存大小
PRAGMA cache_size = -2000;  -- 2MB 缓存

-- 2. 限制临时存储
PRAGMA temp_store = FILE;  -- 使用磁盘而非内存

-- 3. 使用流式查询(在应用程序中)
-- 避免一次加载所有结果

-- 4. 释放未使用的内存
PRAGMA shrink_memory;

-- 5. 限制内存映射大小
PRAGMA mmap_size = 0;  -- 禁用内存映射

数据迁移问题

-- 问题:大数据量迁移
-- 解决方案:

-- 1. 使用附加数据库
ATTACH DATABASE 'source.db' AS source;
ATTACH DATABASE 'target.db' AS target;

-- 2. 批量迁移
-- 创建目标表
CREATE TABLE target.employees AS 
SELECT * FROM source.employees WHERE 1=0;

-- 分批迁移数据
INSERT INTO target.employees
SELECT * FROM source.employees
ORDER BY employee_id
LIMIT 10000 OFFSET 0;

-- 3. 使用事务优化
BEGIN;
PRAGMA target.synchronous = OFF;
PRAGMA target.journal_mode = MEMORY;

-- 执行迁移
INSERT INTO target.employees SELECT * FROM source.employees;

COMMIT;
PRAGMA target.synchronous = NORMAL;
PRAGMA target.journal_mode = WAL;

-- 4. 验证数据完整性
SELECT 
    (SELECT COUNT(*) FROM source.employees) AS source_count,
    (SELECT COUNT(*) FROM target.employees) AS target_count;

编码和字符集问题

-- 问题:中文乱码
-- 解决方案:

-- 1. 确保使用 UTF-8 编码
PRAGMA encoding = 'UTF-8';

-- 2. 检查当前编码
PRAGMA encoding;

-- 3. 转换编码(在应用程序中处理)
-- Python 示例:
/*
text = text.encode('utf-8').decode('utf-8')
*/

-- 4. 使用 BLOB 存储二进制数据
CREATE TABLE files (
    id INTEGER PRIMARY KEY,
    filename TEXT,
    content BLOB
);

-- 5. 正确处理特殊字符
-- 使用参数化查询避免编码问题

并发访问问题

-- 问题:多用户并发访问冲突
-- 解决方案:

-- 1. 使用 WAL 模式
PRAGMA journal_mode = WAL;
PRAGMA wal_autocheckpoint = 1000;

-- 2. 实现乐观锁
ALTER TABLE employees ADD COLUMN version INTEGER DEFAULT 1;

-- 更新时检查版本
UPDATE employees 
SET salary = 12000,
    version = version + 1
WHERE employee_id = 1 
AND version = 5;  -- 检查版本是否匹配

-- 3. 使用行级时间戳
ALTER TABLE employees 
ADD COLUMN last_modified DATETIME DEFAULT CURRENT_TIMESTAMP;

CREATE TRIGGER update_last_modified
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
    UPDATE employees 
    SET last_modified = CURRENT_TIMESTAMP
    WHERE employee_id = NEW.employee_id;
END;

-- 4. 连接池配置(应用层)
-- 限制最大连接数
-- 设置连接超时
-- 实现连接重试机制

22. 版本兼容性说明

SQLite 版本特性对照

-- SQLite 3.35.0+ (2021-03)
-- 新增功能:
-- 1. ALTER TABLE DROP COLUMN
ALTER TABLE employees DROP COLUMN unused_column;

-- 2. 内置数学函数
SELECT sin(0.5), cos(0.5), tan(0.5), sqrt(16), log(10), exp(1);

-- SQLite 3.33.0+ (2020-08)
-- 新增功能:UPDATE FROM
UPDATE employees
SET salary = ns.new_salary
FROM (SELECT employee_id, salary * 1.1 AS new_salary FROM employees) ns
WHERE employees.employee_id = ns.employee_id;

-- SQLite 3.32.0+ (2020-05)
-- 新增功能:IIF() 函数
SELECT IIF(salary > 10000, 'High', 'Normal') AS category FROM employees;

-- SQLite 3.31.0+ (2020-01)
-- 新增功能:生成列 (Generated Columns)
CREATE TABLE products (
    id INTEGER PRIMARY KEY,
    price REAL,
    tax_rate REAL DEFAULT 0.1,
    total_price REAL GENERATED ALWAYS AS (price * (1 + tax_rate)) STORED
);

-- SQLite 3.30.0+ (2019-10)
-- 新增功能:NULLS FIRST/LAST
SELECT * FROM employees 
ORDER BY department_id NULLS LAST, salary DESC NULLS FIRST;

-- SQLite 3.28.0+ (2019-04)
-- 新增功能:窗口函数改进
SELECT 
    employee_id,
    salary,
    NTH_VALUE(salary, 2) OVER (ORDER BY salary DESC) AS second_highest
FROM employees;

-- SQLite 3.25.0+ (2018-09)
-- 新增功能:ALTER TABLE RENAME COLUMN
ALTER TABLE employees RENAME COLUMN phone TO phone_number;

-- SQLite 3.24.0+ (2018-06)
-- 新增功能:UPSERT
INSERT INTO employees (employee_id, email, salary)
VALUES (1, 'new@email.com', 15000)
ON CONFLICT(employee_id) DO UPDATE 
SET email = excluded.email,
    salary = excluded.salary;

功能兼容性检查

-- 检查 SQLite 版本
SELECT sqlite_version();

-- 检查编译选项
SELECT * FROM pragma_compile_options();

-- 检查特定功能是否可用
-- 检查 JSON 支持
SELECT json('{"test": true}');

-- 检查全文搜索支持
CREATE VIRTUAL TABLE test_fts USING fts5(content);
DROP TABLE test_fts;

-- 检查 RTREE 支持
CREATE VIRTUAL TABLE test_rtree USING rtree(id, minX, maxX, minY, maxY);
DROP TABLE test_rtree;

-- 创建版本兼容性视图
CREATE VIEW version_info AS
SELECT 
    sqlite_version() AS version,
    (SELECT COUNT(*) FROM pragma_compile_options() WHERE compile_options LIKE '%ENABLE_JSON%') AS has_json,
    (SELECT COUNT(*) FROM pragma_compile_options() WHERE compile_options LIKE '%ENABLE_FTS5%') AS has_fts5,
    (SELECT COUNT(*) FROM pragma_compile_options() WHERE compile_options LIKE '%ENABLE_RTREE%') AS has_rtree;

向后兼容性策略

-- 1. 条件创建(检查版本)
-- 创建兼容性函数
CREATE TABLE IF NOT EXISTS db_version (
    version TEXT PRIMARY KEY,
    applied_date DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 2. 特性检测和降级方案
-- 例如:如果不支持 DROP COLUMN,使用重建表方式
-- 创建版本检查函数(应用层实现)

-- 3. 使用视图抽象版本差异
CREATE VIEW employee_info AS
SELECT 
    employee_id,
    first_name || ' ' || last_name AS full_name,
    email,
    salary,
    -- 使用 CASE 代替 IIF(兼容旧版本)
    CASE 
        WHEN salary > 10000 THEN 'High'
        ELSE 'Normal'
    END AS salary_category
FROM employees;

-- 4. 数据库迁移脚本模板
-- migration_001.sql
BEGIN TRANSACTION;

-- 检查是否已应用
INSERT OR IGNORE INTO db_version (version) VALUES ('001');

-- 如果是新迁移,执行变更
-- ... 数据库变更语句 ...

COMMIT;

-- 5. 功能降级示例
-- 新版本使用 UPDATE FROM
-- UPDATE employees SET ... FROM ...;

-- 旧版本兼容写法
UPDATE employees 
SET salary = (
    SELECT salary * 1.1 
    FROM employees e2 
    WHERE e2.employee_id = employees.employee_id
)
WHERE EXISTS (
    SELECT 1 FROM employees e3 
    WHERE e3.employee_id = employees.employee_id
);

升级建议

-- 1. 升级前备份
VACUUM INTO 'backup_before_upgrade.db';

-- 2. 检查数据完整性
PRAGMA integrity_check;
PRAGMA foreign_key_check;

-- 3. 测试兼容性
-- 在测试环境运行所有查询和操作

-- 4. 渐进式升级
-- 先升级开发环境
-- 再升级测试环境
-- 最后升级生产环境

-- 5. 监控升级后的性能
CREATE TABLE upgrade_metrics (
    metric_name TEXT PRIMARY KEY,
    before_value REAL,
    after_value REAL,
    change_percent REAL GENERATED ALWAYS AS 
        ((after_value - before_value) * 100.0 / before_value) STORED,
    measured_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 记录升级前后的性能指标
INSERT INTO upgrade_metrics (metric_name, before_value, after_value)
VALUES 
    ('avg_query_time_ms', 45.2, 38.7),
    ('db_size_mb', 156.3, 148.9),
    ('index_count', 12, 15);

网站公告

今日签到

点亮在社区的每一天
去签到