SQLite 數據庫操作完整指南

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_salaryFROM employeesGROUP 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 CASCADEON 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_idFROM departmentsWHERE 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_salaryFROM employeesGROUP 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_countFROM employeesGROUP BY department_id
),
dept_projects AS (SELECT department_id, COUNT(*) AS proj_countFROM projectsGROUP 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 pathFROM employees eWHERE 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_nameFROM employees eJOIN 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.05WHEN department_id = 2 THEN salary * 1.10WHEN department_id = 3 THEN salary * 1.08ELSE 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_salaryFROM employeesWHERE department_id = 2
) s
WHERE employees.employee_id = s.employee_id;-- 使用 CTE 更新
WITH salary_updates AS (SELECT employee_id,salary * 1.15 AS new_salaryFROM employeesWHERE employee_id IN (SELECT employee_idFROM employeesORDER BY salary DESCLIMIT 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 employeesGROUP BY email
);

高級刪除操作

-- 使用 CTE 刪除
WITH inactive_employees AS (SELECT employee_idFROM employeesWHERE is_active = 0AND 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_idFROM log_tableWHERE timestamp < date('now', '-30 days')ORDER BY timestampLIMIT 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 ALLSELECT date(date, '+1 day')FROM datesWHERE 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_rankFROM employeesWHERE 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
BEGINSELECT RAISE(ABORT, '薪資必須大于0');
END;-- AFTER INSERT 觸發器
CREATE TRIGGER log_new_employee
AFTER INSERT ON employees
FOR EACH ROW
BEGININSERT 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
BEGINUPDATE 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
BEGININSERT 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)
BEGINSELECT RAISE(ABORT, '不能刪除部門經理');
END;-- AFTER DELETE 觸發器
CREATE TRIGGER archive_deleted_employee
AFTER DELETE ON employees
FOR EACH ROW
BEGININSERT 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 CASEWHEN NEW.email NOT LIKE '%_@_%.__%' THENRAISE(ABORT, '郵箱格式無效')END;-- 驗證部門存在SELECT CASEWHEN NOT EXISTS (SELECT 1 FROM departments WHERE department_id = NEW.department_id) THENRAISE(ABORT, '部門不存在')END;-- 驗證薪資范圍SELECT CASEWHEN NEW.salary < 3000 OR NEW.salary > 100000 THENRAISE(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_TIMESTAMPWHERE 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
BEGINUPDATE employees SET salary = NEW.salaryWHERE 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
BEGINSELECT 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 BEGININSERT 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'
BEGINSELECT 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_dateRANGE 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 DESCROWS 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_salesFROM ordersGROUP 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 rankFROM 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
BEGINDELETE 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 pgsizeFROM dbstatJOIN 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 ALLSELECT date(date, '+1 day')FROM datesWHERE 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,  -- 579123 || 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
BEGINUPDATE products SET stock = stock - NEW.quantityWHERE product_id = NEW.product_id;-- 檢查庫存SELECT CASEWHEN (SELECT stock FROM products WHERE product_id = NEW.product_id) < 0THEN 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
BEGININSERT 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 1UNION ALLSELECT value + 1 FROM seriesWHERE 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 ALLSELECT 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_namesWHERE 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 valueFROM employeesUNION ALLSELECT 'Average Salary',AVG(salary)FROM employeesUNION ALLSELECT '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 pgsizeFROM dbstatWHERE 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 timedef execute_with_retry(conn, query, max_retries=3):for i in range(max_retries):try:cursor = conn.execute(query)return cursorexcept sqlite3.OperationalError as e:if "locked" in str(e) and i < max_retries - 1:time.sleep(0.1 * (i + 1))  # 指數退避continueraise
*/

性能問題診斷

-- 問題:查詢速度慢
-- 診斷步驟:-- 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
BEGINUPDATE employees SET last_modified = CURRENT_TIMESTAMPWHERE 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);

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/85803.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/85803.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/85803.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python Luigi 【工作流管理庫】簡介

想全面了解DeepSeek的看過來 【包郵】DeepSeek全攻略 人人需要的AI通識課 零基礎掌握DeepSeek的實用操作手冊指南【限量作者親筆簽名版售完即止】 玩轉DeepSeek這本就夠了 【自營包郵】DeepSeek實戰指南 deepseek從入門到精通實用操作指南現代科技科普讀物AI普及知識讀物人工智…

微服務中分布式事務:Saga模式、TCC模式與消息隊列

Saga模式 Saga模式是一種基于補償的事務管理機制&#xff0c;它將一個長事務分解為多個本地事務&#xff0c;每個本地事務都有一個對應的補償事務。當某個本地事務執行失敗時&#xff0c;Saga模式會依次調用前面已成功執行的本地事務的補償事務&#xff0c;以實現事務的回滾。…

唯美復古風景人像攝影Lr調色教程,手機濾鏡PS+Lightroom預設下載!

調色教程 “唯美復古風景人像攝影 Lr 調色”&#xff0c;是將人物置于如畫的風景之中進行拍攝&#xff0c;再運用 Lightroom&#xff08;Lr&#xff09;軟件&#xff0c;通過專業的調色操作&#xff0c;為照片賦予復古的藝術氣息&#xff0c;讓畫面兼具唯美的視覺享受與懷舊的情…

華為云Flexus+DeepSeek征文|體驗華為云ModelArts快速搭建Dify-LLM應用開發平臺并創建b站視頻總結大模型

華為云FlexusDeepSeek征文&#xff5c;體驗華為云ModelArts快速搭建Dify-LLM應用開發平臺并創建b站視頻總結大模型 什么是華為云ModelArts 華為云ModelArts ModelArts是華為云提供的全流程AI開發平臺&#xff0c;覆蓋從數據準備到模型部署的全生命周期管理&#xff0c;幫助企…

線程池異步處理

List<CompletableFuture<Void>> futures new ArrayList<>();// 提交10個異步任務 for (int i 0; i < 10; i) {CompletableFuture<Void> future CompletableFuture.runAsync(() -> {insertData(batchData); // 每個任務插入一批數據}, pool).t…

STM32HAL庫 -- 8.串口UART通信并開啟printf功能

目錄 1.簡介 2.串口和UART 2.1串口的簡介 2.2UART的簡介 2.3UART通信協議 2.3.1波特率 2.3.2空閑位 2.3.3起始位 2.3.4數據位 2.3.5校驗位 2.3.6停止位 3.STM32的UART 4.HAL庫中常用的操作UART的函數 4.1UART初始化函數 -- HAL_UART_Init 4.2硬件初始化回調函數…

【PyTorch項目實戰】CycleGAN:無需成對訓練樣本,支持跨領域圖像風格遷移

文章目錄 一、風格遷移模型&#x1f3a8;1、發展時間線2、分類與優缺點3、選擇建議4、HuggingFace Demo&#xff08;instruct-pix2pix&#xff09; —— 在線測試 二、論文簡讀&#xff08;1&#xff09;FastStyleTransfer&#xff08;快速風格遷移&#xff0c;Johnson et al.,…

C#Halcon從零開發_Day14_AOI缺陷檢測策略1_Bolb分析+特征分析_餅干破損檢測

一、引言 *缺陷檢測策略1&#xff1a;Blob分析特征分析 *Blob分析&#xff1a;閾值分割、開閉運算 (1) 圖像預處理 目的&#xff1a;增強目標與背景的對比度&#xff0c;抑制噪聲。 常用算子&#xff1a; threshold()&#xff1a;通過閾值分割將圖像轉換為二值圖像。 mean_…

18年磨一劍!開利科技啟動數字化增量投資新時代

當傳統營銷陷入流量困局&#xff0c;當數字化轉型變成"面子工程"&#xff0c;廣州開利網絡用18年技術積淀給出新答案——"蚓鏈數字化生態營銷系統"&#xff0c;讓企業不再為數字化而數字化&#xff0c;而是通過數字化實現利潤增長&#xff01; 核心亮點&am…

65-Oracle Undo機制

前一期看了Oracle在redo上保持事務前滾的一致性&#xff0c;同樣Oracle在Undo的管理機制也是現代事務型數據庫的工程典范。核心在于通過多版本并發控制&#xff08;MVCC&#xff09;技術&#xff0c;在保障數據一致性與提升系統性能之間實現精妙平衡。Undo機制構建了原子事務的…

【ESP32攝像頭開發實例】-實現遙控視頻小車

ESP32-CAM實現遙控視頻小車 文章目錄 ESP32-CAM實現遙控視頻小車1、硬件準備2、代碼實現3、代碼解析4、程序下載到開發板在本文中,將詳細介紹如何使用ESP32-CAM制作一輛Wi-Fi遠程控制的小車。 1、硬件準備 在開始這個項目之前,我們將強調用于構建機器人的最重要的特性和組件…

圖片加載優化(縮略圖)

縮略圖 系統目前的問題&#xff1a;首頁直接加載原圖&#xff0c;原圖文件通常比縮略圖大數倍甚至數十倍&#xff0c;不僅導致加載時間長&#xff0c;還會造成大量流量浪費。 解決方案&#xff1a;上傳圖片時&#xff0c;同時生成一份較小尺寸的縮略圖。用戶瀏覽圖片列表時加…

GO語言---數組

文章目錄 數組的基本特性數組的聲明和初始化數組元素的訪問和操作數組遍歷多維數組數組的長度和容量數組作為函數參數數組與切片的區別注意 數組是Go語言中最基本的數據結構之一&#xff0c;它是一組相同類型元素的固定長度序列。 數組的基本特性 1、固定長度&#xff1a;數組…

7.5.3_1處理沖突的方法-拉鏈法

知識總覽&#xff1a; 拉鏈法&#xff1a; 開始散列表中沒有存儲任何數據元素即散列地址上的元素是空的&#xff0c;散列地址可以視為鏈表的頭指針&#xff0c;即沒有插入任何元素前鏈表的頭指針是空的。一個散列地址對應一個鏈表&#xff0c;散列地址上實際沒有存數據元素&am…

鴻蒙運動項目開發:項目運行環境切換器

##鴻蒙核心技術##運動開發# 在開發鴻蒙運動項目時&#xff0c;管理不同運行環境&#xff08;如開發環境、測試環境、生產環境&#xff09;是一個常見的需求。通過合理地切換運行環境&#xff0c;開發者可以方便地進行調試、測試和部署。本文將介紹如何實現一個項目運行環境切換…

Linux內核中安全創建套接字:為何inet_create未導出及正確替代方案

引言 在Linux內核開發中,當驅動程序需要創建網絡套接字時,開發者常會遇到一個關鍵問題:核心函數inet_create(負責初始化IPv4套接字)并未導出到內核符號表。本文深入剖析這一設計決策背后的邏輯,并提供驅動程序安全創建套接字的實踐方案。 一、inet_create未導出的深層原…

63、不同路徑II

題目 解答&#xff1a; 初始化和特殊情況比較麻煩的dp obstacleGrid(0,0)1的&#xff0c;直接return 0即可。入口都被堵住了還怎么走。 mn1情況&#xff0c;直接判斷 第一行初始化&#xff1a;dp[1][0]->dp[i][0] 碰到有障礙物的&#xff0c;從當前格子開始到末尾全部置…

wx小程序登錄設置角色

背景。pc端登錄后在訪問業務鏈接時可以根據固定獲取用戶的方法LoginUser user LoginHelper.getLoginUser(); 獲取到用戶信息。但wx端登錄后無法獲取。原因處在登陸時對用戶信息的設置方面pc端和小程序端登錄沒有使用相同的登錄方法。排除得知wx端小程序登錄時沒有設置角色。所…

MySQL5.7 慢查詢SQL語句集合

文章目錄 1. 按平均執行時間排序的慢查詢2. 按總執行時長排序的慢查詢3. MySQL 5.7 慢查詢配置檢查4. 掃描行數分析&#xff08;找出全表掃描&#xff09;5. 高頻執行的慢查詢6. 當前正在執行的查詢7. 慢查詢統計匯總8. 表結構和索引分析8.1 表索引詳情查詢8.2 表大小統計 1. 按…

MySQL學習(1)——基礎庫操作

歡迎來到博主的專欄:MySQL學習 博主ID:代碼小豪 文章目錄 數據庫原理基礎庫操作增刪數據庫數據庫編碼與校驗規則驗證不同的校驗規則對于庫中數據的影響 備份與恢復數據庫 數據庫原理 mysql版本:mysql8.0 操作系統:ubuntu22.4 為了減少由于環境配置以及權限限制帶來的使用問題&…