测试SQLite支持的SQL语句分类
为了全面测试SQLite支持的SQL语句,需要设计一个包含多种类型的表结构,并编写各种SQL语句来测试这些功能。目前按照以下分类进行测试:
- 数据定义语言(DDL)
- 数据操作语言(DML)
- 数据控制语言(DCL)
- 事务控制语言(TCL)
- 查询语言(QL)
1.表结构设计
我们将创建一个简单的数据库,包含三个表:users
、products
和orders
,以及一个关联表order_items
。
-- 创建用户表
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT, -- 主键,自增长
username TEXT NOT NULL UNIQUE, -- 用户名,不为空且唯一
password TEXT NOT NULL, -- 密码,不为空
email TEXT NOT NULL UNIQUE, -- 电子邮件,不为空且唯一
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 创建时间,默认为当前时间戳
);
-- 创建产品表
CREATE TABLE products (
id INTEGER PRIMARY KEY AUTOINCREMENT, -- 主键,自增长
name TEXT NOT NULL, -- 产品名称,不为空
description TEXT, -- 产品描述
price REAL NOT NULL, -- 价格,不为空
stock INTEGER NOT NULL, -- 库存,不为空
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 创建时间,默认为当前时间戳
);
-- 创建订单表
CREATE TABLE orders (
id INTEGER PRIMARY KEY AUTOINCREMENT, -- 主键,自增长
user_id INTEGER NOT NULL, -- 用户ID,不为空
total REAL NOT NULL, -- 总价,不为空
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间,默认为当前时间戳
FOREIGN KEY (user_id) REFERENCES users (id) -- 外键,引用用户表的ID
);
-- 创建订单项目表
CREATE TABLE order_items (
order_id INTEGER NOT NULL, -- 订单ID,不为空
product_id INTEGER NOT NULL, -- 产品ID,不为空
quantity INTEGER NOT NULL, -- 数量,不为空
price REAL NOT NULL, -- 价格,不为空
PRIMARY KEY (order_id, product_id), -- 主键,由订单ID和产品ID组成的复合主键
FOREIGN KEY (order_id) REFERENCES orders (id), -- 外键,引用订单表的ID
FOREIGN KEY (product_id) REFERENCES products (id) -- 外键,引用产品表的ID
);
2.测试SQL语句分类
*2.1 数据定义语言(DDL)
关于数据定义MDB内部采用特定语法解决,略过不测。
创建表
-- 基础表创建
CREATE TABLE basic_table (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER,
salary REAL,
hire_date DATE DEFAULT CURRENT_DATE
);
-- 带有各种约束的表创建
CREATE TABLE constraint_table (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
email TEXT NOT NULL UNIQUE,
phone TEXT CHECK (LENGTH(phone) = 10),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (id) REFERENCES basic_table (id)
);
修改表结构
-- 增加列
ALTER TABLE basic_table ADD COLUMN address TEXT;
-- 删除列(SQLite 不支持直接删除列,需要创建新表并迁移数据)
-- 迁移示例
CREATE TABLE new_basic_table AS SELECT id, name, age, salary, hire_date FROM basic_table;
DROP TABLE basic_table;
ALTER TABLE new_basic_table RENAME TO basic_table;
-- 修改列(SQLite 不支持直接修改列属性,需要创建新表并迁移数据)
-- 迁移示例
CREATE TABLE modified_basic_table (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER,
salary REAL,
hire_date DATE DEFAULT CURRENT_DATE,
address TEXT
);
INSERT INTO modified_basic_table (id, name, age, salary, hire_date, address)
SELECT id, name, age, salary, hire_date, address FROM basic_table;
DROP TABLE basic_table;
ALTER TABLE modified_basic_table RENAME TO basic_table;
基本数据类型
-- DEFAULT: 在创建表时指定默认值
CREATE TABLE test_default (
id INTEGER PRIMARY KEY,
name TEXT DEFAULT 'Unknown'
);
-- UNSIGNED: 无符号整数类型
CREATE TABLE test_unsigned (
id INTEGER UNSIGNED PRIMARY KEY,
value INTEGER UNSIGNED
);
DATE
-- 插入带有日期的数据
INSERT INTO users (username, password, email, created_at) VALUES ('user1', 'password1', 'user1@example.com', '2024-05-20');
INSERT INTO products (name, description, price, stock, created_at) VALUES ('Product 1', 'Description for Product 1', 100.00, 50, '2024-05-20');
INSERT INTO orders (user_id, total, created_at) VALUES (1, 100.00, '2024-05-20');
INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (1, 1, 2, 200.00);
-- 查询特定日期的数据
SELECT * FROM users WHERE created_at = '2024-05-20';
SELECT * FROM orders WHERE created_at BETWEEN '2024-05-20 00:00:00' AND '2024-05-20 23:59:59';
-- 更新日期数据
UPDATE users SET created_at = '2024-05-21' WHERE id = 1;
UPDATE orders SET created_at = '2024-05-21 10:00:00' WHERE id = 1;
-- 查询更新后的日期数据
SELECT * FROM users WHERE created_at = '2024-05-21';
SELECT * FROM orders WHERE created_at BETWEEN '2024-05-21 00:00:00' AND '2024-05-21 23:59:59';
-- 使用日期函数进行查询
SELECT * FROM users WHERE DATE(created_at) = '2024-05-21';
-- 删除特定日期的数据
DELETE FROM users WHERE DATE(created_at) = '2024-05-21';
DELETE FROM orders WHERE DATE(created_at) = '2024-05-21';
-- 检查是否成功删除特定日期的数据
SELECT * FROM users;
SELECT * FROM orders;
删除表
-- 删除表
DROP TABLE constraint_table;
创建索引
-- 创建索引
CREATE INDEX idx_basic_table_name ON basic_table (name);
CREATE UNIQUE INDEX idx_constraint_table_email ON constraint_table (email);
-- 创建复合索引
CREATE INDEX idx_basic_table_name_age ON basic_table (name, age);
-- 删除索引
DROP INDEX idx_basic_table_name;
创建视图
-- 创建简单视图
CREATE VIEW view_basic_table AS
SELECT id, name, salary FROM basic_table;
-- 创建带条件的视图
CREATE VIEW view_high_salary AS
SELECT id, name, salary FROM basic_table WHERE salary > 50000;
-- 删除视图
DROP VIEW view_basic_table;
创建触发器
触发器用于在特定事件发生时自动执行SQL语句。
-- 创建触发器,在插入数据到 basic_table 时记录日志
CREATE TABLE log_table (
log_id INTEGER PRIMARY KEY AUTOINCREMENT,
table_name TEXT,
operation TEXT,
operation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TRIGGER trg_after_insert_basic_table
AFTER INSERT ON basic_table
BEGIN
INSERT INTO log_table (table_name, operation) VALUES ('basic_table', 'INSERT');
END;
-- 创建触发器,在删除数据时记录日志
CREATE TRIGGER trg_after_delete_basic_table
AFTER DELETE ON basic_table
BEGIN
INSERT INTO log_table (table_name, operation) VALUES ('basic_table', 'DELETE');
END;
-- 删除触发器
DROP TRIGGER trg_after_insert_basic_table;
创建复杂的表结构
包括外键和级联操作等高级特性。
-- 创建带外键的表
CREATE TABLE department (
dept_id INTEGER PRIMARY KEY AUTOINCREMENT,
dept_name TEXT NOT NULL
);
CREATE TABLE employee (
emp_id INTEGER PRIMARY KEY AUTOINCREMENT,
emp_name TEXT NOT NULL,
dept_id INTEGER,
FOREIGN KEY (dept_id) REFERENCES department (dept_id) ON DELETE SET NULL ON UPDATE CASCADE
);
创建约束
约束对数据的完整性进行了限制,包括主键、唯一约束、外键和检查约束等。
-- 创建主键约束
CREATE TABLE constraint_table (
id INTEGER PRIMARY KEY,
username TEXT NOT NULL,
email TEXT UNIQUE
);
-- 创建唯一约束
CREATE TABLE unique_constraint_table (
id INTEGER PRIMARY KEY,
username TEXT UNIQUE,
email TEXT
);
-- 创建外键约束
CREATE TABLE orders (
order_id INTEGER PRIMARY KEY,
customer_id INTEGER,
total REAL,
FOREIGN KEY (customer_id) REFERENCES customers (customer_id)
);
-- 创建检查约束
CREATE TABLE check_constraint_table (
id INTEGER PRIMARY KEY,
age INTEGER CHECK (age >= 18)
);
管理数据库
这包括创建和删除数据库的操作。
-- 创建数据库
CREATE DATABASE test_database;
-- 删除数据库
DROP DATABASE test_database;
创建和管理用户
SQLite通常不支持用户管理,但是在某些特定的SQLite扩展中,可以实现用户管理功能。以下是示例SQL,但在标准SQLite中并不适用。
-- 创建用户
CREATE USER new_user IDENTIFIED BY 'password';
-- 删除用户
DROP USER old_user;
-- 授予权限
GRANT SELECT ON table_name TO user_name;
-- 撤销权限
REVOKE INSERT ON table_name FROM user_name;
管理存储过程和函数
SQLite通常不支持存储过程和函数,但在某些特定的SQLite扩展中,可以实现这些功能。以下是示例SQL,但在标准SQLite中并不适用。
-- 创建存储过程
CREATE PROCEDURE calculate_tax (IN price DECIMAL(10,2), OUT tax DECIMAL(10,2))
BEGIN
SET tax = price * 0.1;
END;
-- 删除存储过程
DROP PROCEDURE calculate_tax;
-- 创建函数
CREATE FUNCTION get_customer_name (customer_id INT) RETURNS VARCHAR(100)
BEGIN
DECLARE customer_name VARCHAR(100);
SELECT name INTO customer_name FROM customers WHERE id = customer_id;
RETURN customer_name;
END;
-- 删除函数
DROP FUNCTION get_customer_name;
创建和管理序列
序列是一种生成唯一数字序列的对象,在某些数据库系统中常用于生成主键值。
-- 创建序列
CREATE SEQUENCE seq_id START WITH 1 INCREMENT BY 1;
-- 删除序列
DROP SEQUENCE seq_id;
-- 获取序列下一个值
SELECT NEXT VALUE FOR seq_id;
-- 重置序列
ALTER SEQUENCE seq_id RESTART WITH 100;
管理存储空间和表空间
存储空间管理允许管理员管理数据库的物理存储结构,而表空间管理允许将表存储在不同的物理位置上。
-- 创建表空间(SQLite 不支持)
CREATE TABLESPACE ts1 LOCATION '/path/to/disk1';
-- 删除表空间(SQLite 不支持)
DROP TABLESPACE ts1;
-- 将表移到指定表空间(SQLite 不支持)
ALTER TABLE table_name SET TABLESPACE ts1;
定义事件和调度任务
事件和调度任务允许数据库自动执行特定的操作。
-- 创建事件(SQLite 不支持)
CREATE EVENT event_name ON SCHEDULE AT '2024-05-25 03:00:00' DO DELETE FROM logs WHERE timestamp < NOW() - INTERVAL 1 MONTH;
-- 删除事件(SQLite 不支持)
DROP EVENT event_name;
-- 创建调度任务(SQLite 不支持)
CREATE SCHEDULE schedule_name
ON SCHEDULE EVERY 1 DAY
STARTS '2024-05-25 00:00:00'
DO BACKUP DATABASE;
-- 删除调度任务(SQLite 不支持)
DROP SCHEDULE schedule_name;
备份和恢复数据库
备份和恢复操作是数据库管理的关键部分,但在SQLite中通常由外部工具完成。
-- 备份数据库(SQLite 通常使用外部工具如sqlite3命令行工具进行备份)
.backup test_database backup_file;
-- 恢复数据库(SQLite 通常使用外部工具如sqlite3命令行工具进行恢复)
.restore backup_file;
创建和管理存储过程
存储过程是一组预编译的SQL语句,可被多次调用。
-- 创建存储过程(SQLite 不直接支持存储过程,但可以使用用户定义函数来模拟)
CREATE PROCEDURE calculate_total(IN price REAL, IN quantity INTEGER, OUT total REAL)
BEGIN
SET total = price * quantity;
END;
-- 调用存储过程
CALL calculate_total(10.50, 5, @result);
SELECT @result;
-- 删除存储过程(SQLite 不支持,但可以删除用户定义函数)
DROP PROCEDURE calculate_total;
创建和管理用户定义函数
用户定义函数允许在SQL查询中调用自定义函数。
-- 创建用户定义函数
CREATE FUNCTION greet(name TEXT) RETURNS TEXT
BEGIN
RETURN 'Hello, ' || name || '!';
END;
-- 调用用户定义函数
SELECT greet('John');
-- 删除用户定义函数
DROP FUNCTION greet;
创建和管理全文搜索虚拟表
全文搜索虚拟表允许对文本数据进行全文搜索操作。
-- 创建全文搜索虚拟表
CREATE VIRTUAL TABLE documents USING fts5(content TEXT);
-- 插入数据
INSERT INTO documents(content) VALUES ('This is a document.');
-- 全文搜索
SELECT * FROM documents WHERE documents MATCH 'document';
-- 删除全文搜索虚拟表
DROP TABLE documents;
创建和管理模块
SQLite支持使用扩展模块来增强数据库功能,这包括自定义的数据类型、函数、虚拟表等。
-- 加载模块
.load /path/to/module.so
-- 卸载模块
.UNLOAD module_name;
创建和管理外部表
外部表允许SQLite访问外部数据源,如CSV文件、其他数据库等。
-- 创建外部表(SQLite 不直接支持,但可以通过使用虚拟表和外部工具实现)
CREATE VIRTUAL TABLE external_table USING csv(filename='/path/to/file.csv');
-- 查询外部表
SELECT * FROM external_table;
-- 删除外部表(SQLite 不直接支持,但可以删除虚拟表)
DROP TABLE external_table;
创建和管理扩展模块
SQLite支持通过加载动态链接库来扩展其功能,这些动态链接库可以包含自定义的函数、虚拟表等。
-- 加载扩展模块
.load /path/to/extension_module.so
-- 查看已加载的扩展模块
.echo on
-- 可以查看到已加载的模块信息
-- 卸载扩展模块
.UNLOAD extension_module;
创建和管理存储过程
存储过程是预编译的一组SQL语句,可以多次调用。
-- 创建存储过程(SQLite 不直接支持存储过程,但可以通过创建包含多个SQL语句的脚本来模拟)
CREATE PROCEDURE get_user(IN user_id INTEGER)
BEGIN
SELECT * FROM users WHERE id = user_id;
END;
-- 调用存储过程
CALL get_user(1);
-- 删除存储过程
DROP PROCEDURE get_user;
创建和管理用户定义函数
用户定义函数允许在SQL查询中调用自定义函数。
-- 创建用户定义函数
CREATE FUNCTION calculate_discount(price REAL, discount_rate REAL) RETURNS REAL
BEGIN
RETURN price * (1 - discount_rate);
END;
-- 调用用户定义函数
SELECT calculate_discount(100, 0.1);
-- 删除用户定义函数
DROP FUNCTION calculate_discount;
创建和管理外部表
外部表允许SQLite访问外部数据源,如其他数据库、CSV文件等。
-- 创建外部表(SQLite 不直接支持,但可以通过虚拟表和外部工具实现)
CREATE VIRTUAL TABLE external_table USING csv(filename='/path/to/file.csv');
-- 查询外部表
SELECT * FROM external_table;
-- 删除外部表(SQLite 不直接支持,但可以删除虚拟表)
DROP TABLE external_table;
创建和管理全文搜索虚拟表
全文搜索虚拟表允许对文本数据进行全文搜索操作。
-- 创建全文搜索虚拟表
CREATE VIRTUAL TABLE documents USING fts5(content TEXT);
-- 插入数据
INSERT INTO documents(content) VALUES ('This is a document.');
-- 全文搜索
SELECT * FROM documents WHERE documents MATCH 'document';
-- 删除全文搜索虚拟表
DROP TABLE documents;
创建和管理触发器
触发器允许在特定的数据库事件发生时自动执行一系列SQL语句。
-- 创建触发器
CREATE TRIGGER after_insert_user
AFTER INSERT ON users
BEGIN
INSERT INTO user_logs(user_id, action, timestamp) VALUES (NEW.id, 'INSERT', CURRENT_TIMESTAMP);
END;
-- 删除触发器
DROP TRIGGER after_insert_user;
创建和管理分区表
分区表允许将表数据分割存储在不同的物理存储区域中,以提高查询性能和管理数据。
-- 创建分区表(SQLite 不直接支持,但可以通过表分割和视图来模拟)
CREATE TABLE partitioned_table (
id INTEGER,
name TEXT,
partition_key INTEGER,
PRIMARY KEY (id, partition_key)
);
-- 创建分区
CREATE TABLE partition_1 AS SELECT * FROM partitioned_table WHERE partition_key = 1;
CREATE TABLE partition_2 AS SELECT * FROM partitioned_table WHERE partition_key = 2;
-- 创建视图以查询分区表
CREATE VIEW partitioned_view AS
SELECT * FROM partition_1
UNION ALL
SELECT * FROM partition_2;
-- 删除分区表
DROP TABLE partition_1;
DROP TABLE partition_2;
-- 删除视图
DROP VIEW partitioned_view;
创建和管理临时表
临时表是在会话期间存在的临时表,通常用于存储中间结果或临时数据。
-- 创建临时表(SQLite 不需要显式创建临时表,可以直接使用临时表)
CREATE TEMP TABLE temp_table (
id INTEGER PRIMARY KEY,
name TEXT
);
-- 插入数据
INSERT INTO temp_table VALUES (1, 'John'), (2, 'Alice');
-- 查询临时表
SELECT * FROM temp_table;
-- 删除临时表(会话结束后自动删除)
-- 不需要显式删除
创建和管理备份表
备份表用于存储原始数据的备份副本,通常用于数据恢复或历史记录。
-- 创建备份表(SQLite 不需要显式创建备份表,可以直接使用)
CREATE TABLE backup_table AS SELECT * FROM original_table;
-- 查询备份表
SELECT * FROM backup_table;
-- 删除备份表
DROP TABLE backup_table;
创建和管理包含多个数据类型的表
表可以包含多种数据类型的列,包括整数、浮点数、文本、日期等。
-- 创建包含多个数据类型的表
CREATE TABLE mixed_type_table (
id INTEGER PRIMARY KEY,
name TEXT,
age INTEGER,
height REAL,
birth_date DATE
);
创建和管理连接表
连接表是多对多关系的表,用于表示两个实体之间的关联关系。
-- 创建连接表
CREATE TABLE user_role (
user_id INTEGER,
role_id INTEGER,
PRIMARY KEY (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (role_id) REFERENCES roles(id)
);
创建和管理自增长列
自增长列是一种特殊的列,每次插入新记录时都会自动递增生成新的唯一值。
-- 创建带自增长列的表
CREATE TABLE auto_increment_table (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT
);
-- 插入数据
INSERT INTO auto_increment_table (name) VALUES ('John');
INSERT INTO auto_increment_table (name) VALUES ('Alice');
-- 查询数据
SELECT * FROM auto_increment_table;
创建和管理视图
视图是虚拟表,其内容由查询定义,可以简化复杂查询操作。
-- 创建视图
CREATE VIEW user_summary AS
SELECT id, username, email FROM users WHERE role = 'admin';
-- 查询视图
SELECT * FROM user_summary;
创建和管理全局临时表
全局临时表是所有会话都可以访问的临时表,数据在所有会话结束后被删除。
-- 创建全局临时表
CREATE GLOBAL TEMP TABLE global_temp_table (
id INTEGER PRIMARY KEY,
name TEXT
);
-- 插入数据
INSERT INTO global_temp_table VALUES (1, 'John');
-- 查询数据
SELECT * FROM global_temp_table;
创建和管理列别名
列别名是在查询中给列起别名,使查询结果更易读。
-- 查询中使用列别名
SELECT id AS user_id, name AS user_name FROM users;
创建和管理索引
索引用于加速对表中数据的检索操作,可以提高查询性能。
-- 创建索引
CREATE INDEX idx_username ON users(username);
CREATE INDEX idx_product_price ON products(price);
-- 删除索引
DROP INDEX idx_username;
创建和管理全文搜索索引
全文搜索索引用于在文本数据上执行全文搜索操作,可以加速搜索操作。
-- 创建全文搜索索引
CREATE VIRTUAL TABLE documents_fts USING fts5(content TEXT);
-- 插入数据
INSERT INTO documents_fts(content) VALUES ('This is a document.');
-- 全文搜索
SELECT * FROM documents_fts WHERE documents_fts MATCH 'document';
-- 删除全文搜索索引
DROP TABLE documents_fts;
创建和管理计算列
计算列是通过对其他列进行计算得到的值,不存储在表中,只在查询时动态计算。
-- 创建计算列
CREATE TABLE calculated_column_table (
id INTEGER PRIMARY KEY,
quantity INTEGER,
price REAL,
total REAL AS (quantity * price)
);
创建和管理分组集
分组集用于将表按指定列分组,并对每个组进行汇总计算。
-- 创建分组集
CREATE TABLE sales (
id INTEGER PRIMARY KEY,
product_id INTEGER,
quantity INTEGER,
price REAL
);
-- 查询并分组集
SELECT product_id, SUM(quantity) AS total_quantity, SUM(price) AS total_price FROM sales GROUP BY product_id;
创建和管理架构
架构是数据库中的逻辑容器,用于组织和管理对象(如表、视图等)。
-- 创建架构
CREATE SCHEMA my_schema;
-- 在指定架构中创建表
CREATE TABLE my_schema.users (
id INTEGER PRIMARY KEY,
username TEXT
);
-- 查询指定架构中的表
SELECT * FROM my_schema.users;
-- 删除架构及其包含的对象
DROP SCHEMA my_schema CASCADE;
创建和管理权限
权限管理允许管理员控制用户对数据库对象的访问权限。
-- 创建角色
CREATE ROLE admin_role;
-- 授予角色权限
GRANT SELECT, INSERT, UPDATE, DELETE ON users TO admin_role;
-- 创建用户
CREATE USER admin_user IDENTIFIED BY 'password';
-- 授予用户角色
GRANT admin_role TO admin_user;
创建和管理外键约束
外键约束用于维护表之间的关联关系,确保引用完整性。
-- 创建外键约束
CREATE TABLE orders (
order_id INTEGER PRIMARY KEY,
customer_id INTEGER,
total REAL,
FOREIGN KEY (customer_id) REFERENCES customers (customer_id)
);
创建和管理存储过程
存储过程是一组预编译的SQL语句,可被多次调用。
-- 创建存储过程(SQLite 不直接支持存储过程,但可以使用用户定义函数来模拟)
CREATE PROCEDURE calculate_total(IN price DECIMAL(10,2), IN quantity INT, OUT total DECIMAL(10,2))
BEGIN
SET total = price * quantity;
END;
-- 删除存储过程
DROP PROCEDURE calculate_total;
创建和管理用户定义函数
用户定义函数允许在SQL查询中调用自定义函数。
-- 创建用户定义函数
CREATE FUNCTION get_customer_name (customer_id INT) RETURNS VARCHAR(100)
BEGIN
DECLARE customer_name VARCHAR(100);
SELECT name INTO customer_name FROM customers WHERE id = customer_id;
RETURN customer_name;
END;
-- 删除用户定义函数
DROP FUNCTION get_customer_name;
创建和管理全文搜索虚拟表
全文搜索虚拟表允许对文本数据进行全文搜索操作。
-- 创建全文搜索虚拟表
CREATE VIRTUAL TABLE documents_fts USING fts5(content TEXT);
-- 插入数据
INSERT INTO documents_fts(content) VALUES ('This is a document.');
-- 全文搜索
SELECT * FROM documents_fts WHERE documents_fts MATCH 'document';
-- 删除全文搜索虚拟表
DROP TABLE documents_fts;
创建和管理序列
序列是一种生成唯一数字序列的对象,常用于生成主键值。
-- 创建序列
CREATE SEQUENCE seq_id START WITH 1 INCREMENT BY 1;
-- 获取序列下一个值
SELECT NEXT VALUE FOR seq_id;
-- 重置序列
ALTER SEQUENCE seq_id RESTART WITH 100;
-- 删除序列
DROP SEQUENCE seq_id;
创建和管理存储空间和表空间
存储空间管理允许管理员管理数据库的物理存储结构,表空间管理允许将表存储在不同的物理位置上。
-- 创建表空间(SQLite 不支持)
CREATE TABLESPACE ts1 LOCATION '/path/to/disk1';
-- 删除表空间(SQLite 不支持)
DROP TABLESPACE ts1;
-- 将表移到指定表空间(SQLite 不支持)
ALTER TABLE table_name SET TABLESPACE ts1;
创建和管理事件和调度任务
事件和调度任务允许数据库自动执行特定的操作。
-- 创建事件(SQLite 不支持)
CREATE EVENT event_name ON SCHEDULE AT '2024-05-25 03:00:00' DO DELETE FROM logs WHERE timestamp < NOW() - INTERVAL 1 MONTH;
-- 删除事件(SQLite 不支持)
DROP EVENT event_name;
-- 创建调度任务(SQLite 不支持)
CREATE SCHEDULE schedule_name
ON SCHEDULE EVERY 1 DAY
STARTS '2024-05-25 00:00:00'
DO BACKUP DATABASE;
-- 删除调度任务(SQLite 不支持)
DROP SCHEDULE schedule_name;
备份和恢复数据库
备份和恢复操作是数据库管理的关键部分,但在SQLite中通常由外部工具完成。
-- 备份数据库(SQLite 通常使用外部工具如sqlite3命令行工具进行备份)
.backup test_database backup_file;
-- 恢复数据库(SQLite 通常使用外部工具如sqlite3命令行工具进行恢复)
.restore backup_file;
创建和管理存储过程和函数
SQLite通常不支持存储过程和函数,但在某些特定的SQLite扩展中,可以实现这些功能。
-- 创建存储过程
CREATE PROCEDURE calculate_tax (IN price DECIMAL(10,2), OUT tax DECIMAL(10,2))
BEGIN
SET tax = price * 0.1;
END;
-- 删除存储过程
DROP PROCEDURE calculate_tax;
-- 创建函数
CREATE FUNCTION get_customer_name (customer_id INT) RETURNS VARCHAR(100)
BEGIN
DECLARE customer_name VARCHAR(100);
SELECT name INTO customer_name FROM customers WHERE id = customer_id;
RETURN customer_name;
END;
-- 删除函数
DROP FUNCTION get_customer_name;
*2.2 数据操作语言(DML)
标注*的为扩展内容,略过不测。
插入数据
除了基本的单条插入外,我们还测试批量插入和插入查询结果等。
-- 单条插入
INSERT INTO basic_table (name, age, salary) VALUES ('Alice', 30, 50000.00);
-- 批量插入
INSERT INTO basic_table (name, age, salary) VALUES
('Bob', 35, 60000.00),
('Charlie', 40, 70000.00),
('David', 45, 80000.00);
-- 从另一个表插入数据
INSERT INTO basic_table (name, age, salary)
SELECT name, age, salary FROM temp_table;
-- 正常插入操作
INSERT INTO users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
INSERT INTO users (username, password, email) VALUES ('user2', 'password2', 'user2@example.com');
INSERT INTO products (name, description, price, stock) VALUES ('Product 1', 'Description 1', 10.99, 100);
INSERT INTO products (name, description, price, stock) VALUES ('Product 2', 'Description 2', 20.99, 200);
-- 重复数据插入操作(唯一约束)
-- 此处尝试插入已存在的username和email
INSERT INTO users (username, password, email) VALUES ('user1', 'password3', 'user3@example.com'); -- 应该失败,username已存在
INSERT INTO users (username, password, email) VALUES ('user3', 'password3', 'user1@example.com'); -- 应该失败,email已存在
-- 外键约束测试
-- 尝试插入不存在的user_id
INSERT INTO orders (user_id, total) VALUES (100, 50.00); -- 应该失败,user_id不存在
-- 正常插入订单及订单项目
INSERT INTO orders (user_id, total) VALUES (1, 100.00);
INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (1, 1, 2, 20.00);
更新数据
更新数据包括单表更新、带条件更新和跨表更新等。
-- 单表更新
UPDATE basic_table SET salary = 55000.00 WHERE name = 'Alice';
-- 带条件更新
UPDATE basic_table SET salary = salary * 1.1 WHERE age > 40;
-- 跨表更新(SQLite 不支持 JOIN UPDATE,但可以通过子查询实现)
UPDATE basic_table
SET salary = (
SELECT salary * 1.05 FROM basic_table WHERE name = 'Alice'
)
WHERE name = 'Alice';
-- 批量更新
UPDATE basic_table
SET salary = salary * 1.05
WHERE age BETWEEN 30 AND 40;
-- 更新用户信息:将用户名为'john_doe'的用户的密码修改为'new_password'
UPDATE users SET password = 'new_password' WHERE username = 'john_doe';
-- 更新产品信息:将名为'Laptop'的产品的描述修改为'Powerful gaming laptop'
UPDATE products SET description = 'Powerful gaming laptop' WHERE name = 'Laptop';
-- 更新订单信息:将总价大于1000的订单的总价修改为1200
UPDATE orders SET total = 1200 WHERE total > 1000;
-- 更新订单项目信息:将订单号为1,产品号为1的订单项目的数量修改为2
UPDATE order_items SET quantity = 2 WHERE order_id = 1 AND product_id = 1;
-- 更新不存在的用户:尝试将用户名为'non_existent_user'的用户的邮箱修改为'new_email@example.com'
-- 期望结果:不会影响任何行,因为用户不存在,所以不会有行受到影响
UPDATE users SET email = 'new_email@example.com' WHERE username = 'non_existent_user';
-- 更新时违反唯一约束:尝试将用户名为'john_doe'的用户的邮箱修改为'john_new@example.com',这个邮箱已经被另一个用户使用
-- 期望结果:更新操作失败,不会修改任何行,并且数据库保持不变
UPDATE users SET email = 'john_new@example.com' WHERE username = 'john_doe';
-- 更新时违反外键约束:尝试将订单表中不存在的用户ID(999)更新到订单表中的某条订单的user_id字段
-- 期望结果:更新操作失败,不会修改任何行,并且数据库保持不变
UPDATE orders SET user_id = 999 WHERE id = 1;
-- 内联更新
-- 在订单表中,将所有订单总额小于100的订单的总额增加10%
UPDATE orders SET total = total * 1.1 WHERE total < 100;
-- 在订单表中,将用户ID为1的所有订单的总额增加50
UPDATE orders SET total = total + 50 WHERE user_id = 1;
删除数据
删除数据也涉及单表删除和带条件删除。
-- 单表删除
DELETE FROM basic_table WHERE name = 'David';
-- 带条件删除
DELETE FROM basic_table WHERE age > 40;
-- 删除所有数据
DELETE FROM basic_table;
-- 删除单个表中的所有数据
DELETE FROM users;
-- 删除单个表中满足条件的数据
DELETE FROM products WHERE price > 1000.00;
-- 使用子查询来删除符合条件的数据
DELETE FROM users WHERE id IN (SELECT user_id FROM orders WHERE total > 1000.00);
-- 删除关联表中的数据
DELETE FROM order_items WHERE order_id IN (SELECT id FROM orders WHERE user_id = 1);
-- 删除具有外键关联的主表数据(将级联删除从属表中的数据)
DELETE FROM users WHERE id = 1;
-- 删除具有外键关联的从属表数据(如果未使用级联删除)
DELETE FROM orders WHERE user_id = 2;
-- 删除所有表中的数据(谨慎使用!)
DELETE FROM users;
DELETE FROM products;
DELETE FROM orders;
DELETE FROM order_items;
-- 测试内联条件删除
-- 准备数据
INSERT INTO users (username, password, email) VALUES
('user1', 'password1', 'user1@example.com'),
('user2', 'password2', 'user2@example.com'),
('user3', 'password3', 'user3@example.com');
-- 删除 username 为 'user2' 的用户
DELETE FROM users WHERE username = 'user2';
-- 清空 users 表(TRUNCATE)
-- 注意:SQLite不支持TRUNCATE TABLE语法,因此我们使用DELETE FROM来清空表
DELETE FROM users;
合并数据
SQLite不支持MERGE语句,但我们可以使用INSERT和UPDATE语句模拟MERGE操作。
-- 合并数据(模拟)
INSERT INTO target_table (id, name, value)
SELECT source_id, source_name, source_value
FROM source_table
ON CONFLICT(id) DO UPDATE SET
name = excluded.name,
value = excluded.value;
数据复制
-- 复制数据到另一个表
INSERT INTO new_table (name, age, salary)
SELECT name, age, salary FROM basic_table;
数据删除与备份
-- 删除表数据并备份
CREATE TABLE backup_table AS SELECT * FROM basic_table;
DELETE FROM basic_table;
数据清理与维护
-- 清理旧数据
DELETE FROM basic_table WHERE hire_date < DATE('now', '-1 year');
-- 数据备份与维护
CREATE TABLE backup_table AS SELECT * FROM basic_table;
VACUUM; -- 压缩数据库文件
数据操作的事务处理
-- 开始事务
BEGIN TRANSACTION;
-- 插入数据
INSERT INTO basic_table (name, age, salary) VALUES ('Eve', 25, 45000.00);
-- 提交事务
COMMIT;
数据转换与格式化
-- 数据转换与格式化
SELECT
name,
age,
'$' || CAST(salary AS TEXT) AS formatted_salary
FROM
basic_table;
数据的统计与分析
-- 数据的统计与分析
SELECT
COUNT(*) AS total_employees,
AVG(salary) AS avg_salary,
MAX(salary) AS max_salary,
MIN(salary) AS min_salary
FROM
basic_table;
数据的加密与解密
-- 数据的加密与解密(SQLite不原生支持加密,需要借助扩展库或应用层加密)
-- 示例:使用SQLCipher扩展库
PRAGMA key='password'; -- 设置加密密码
ATTACH DATABASE 'encrypted.db' AS encrypted KEY 'password';
SELECT sqlcipher_export('encrypted');
DETACH DATABASE encrypted;
数据的随机生成
-- 数据的随机生成
INSERT INTO basic_table (name, age, salary)
VALUES
('Random1', ROUND(RANDOM() * 50 + 20), ROUND(RANDOM() * 50000 + 30000)),
('Random2', ROUND(RANDOM() * 50 + 20), ROUND(RANDOM() * 50000 + 30000)),
('Random3', ROUND(RANDOM() * 50 + 20), ROUND(RANDOM() * 50000 + 30000));
数据的格式化输出
-- 数据的格式化输出
.mode column
.headers on
SELECT * FROM basic_table;
数据的版本控制与变更跟踪
-- 数据的版本控制与变更跟踪
-- 使用触发器实现数据变更记录
CREATE TABLE change_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
table_name TEXT,
operation TEXT,
old_data TEXT,
new_data TEXT,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TRIGGER trg_change_log
AFTER INSERT OR UPDATE OR DELETE ON basic_table
BEGIN
INSERT INTO change_log (table_name, operation, old_data, new_data)
VALUES ('basic_table',
CASE
WHEN OLD.id IS NULL THEN 'INSERT'
WHEN NEW.id IS NULL THEN 'DELETE'
ELSE 'UPDATE'
END,
OLD.*,
NEW.*);
END;
数据的转置与旋转
-- 数据的转置与旋转(SQLite 不支持原生的数据转置与旋转,但可以通过复杂的查询实现)
SELECT
MAX(CASE WHEN age = 30 THEN name ELSE NULL END) AS age_30,
MAX(CASE WHEN age = 35 THEN name ELSE NULL END) AS age_35,
MAX(CASE WHEN age = 40 THEN name ELSE NULL END) AS age_40
FROM
basic_table;
数据的归一化与反归一化
-- 数据的归一化与反归一化(通过标准化或反标准化数据)
-- 示例:将工资数据进行归一化
SELECT
name,
(salary - MIN(salary)) / (MAX(salary) - MIN(salary)) AS normalized_salary
FROM
basic_table;
####数据的检查与验证
-- 数据的检查与验证(通过约束和触发器实现)
-- 示例:检查年龄是否大于等于18岁
CREATE TRIGGER trg_check_age
BEFORE INSERT ON basic_table
BEGIN
SELECT CASE WHEN NEW.age < 18 THEN RAISE(FAIL, 'Age must be 18 or above') END;
END;
数据的清理与过滤
-- 数据的清理与过滤(通过DELETE语句实现)
DELETE FROM basic_table WHERE salary < 30000;
数据的聚合与分类
-- 数据的聚合与分类(通过GROUP BY实现)
SELECT
CASE
WHEN age < 30 THEN 'Young'
WHEN age >= 30 AND age < 40 THEN 'Middle-aged'
ELSE 'Senior'
END AS age_group,
COUNT(*) AS count
FROM
basic_table
GROUP BY
age_group;
数据的分区与汇总
-- 数据的分区与汇总(通过窗口函数实现)
SELECT
name,
age,
salary,
SUM(salary) OVER (PARTITION BY age) AS total_salary_by_age
FROM
basi_table;
数据的连接与匹配
-- 数据的连接与匹配(通过JOIN实现)
SELECT
basic_table.name,
department.dept_name
FROM
basic_table
JOIN
department ON basic_table.dept_id = department.ept_id;
数据的时间处理与计算
-- 数据的时间处理与计算(通过日期函数实现)
SELECT
name,
hire_date,
STRFTIME('%Y', hire_date) AS hire_year,
DATE('now') - hire_date AS days_since_hire
FROM
basic_table;
数据的序列与编号
-- 数据的序列与编号(通过ROW_NUMBER()窗口函数实现)
SELECT
ROW_NUMBER() OVER (ORDER BY salary DESC) AS ranking,
name,
salary
FROM
basic_table;
数据的交叉与分割
-- 数据的交叉与分割(通过交叉连接实现)
SELECT
t1.name AS name1,
t2.name AS name2
FROM
basic_table t1
CROSS JOIN
basic_table t2
WHERE
t1.name != t2.name;
数据的分析与预测
-- 数据的分析与预测(通过统计函数实现)
SELECT
name,
age,
salary,
AVG(salary) OVER (PARTITION BY age) AS avg_salary_by_age,
LAG(salary) OVER (ORDER BY age) AS prev_salary,
LEAD(salary) OVER (ORDER BY age) AS next_salary
FROM
basic_table;
数据的归档与压缩
-- 数据的归档与压缩(通过归档表和压缩数据库实现)
-- 创建归档表
CREATE TABLE archived_basic_table AS SELECT * FROM basic_table WHERE hire_date < DATE('now', '-1 year');
-- 压缩数据库
VACUUM;
数据的审核与审计
-- 数据的审核与审计(通过触发器实现数据变更记录)
CREATE TABLE audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
table_name TEXT,
operation TEXT,
old_data TEXT,
new_data TEXT,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TRIGGER trg_audit_log
AFTER INSERT OR UPDATE OR DELETE ON basic_table
BEGIN
INSERT INTO audit_log (table_name, operation, old_data, new_data)
VALUES ('basic_table',
CASE
WHEN OLD.id IS NULL THEN 'INSERT'
WHEN NEW.id IS NULL THEN 'DELETE'
ELSE 'UPDATE'
END,
OLD.*,
NEW.*);
END;
数据的转换与映射
-- 数据的转换与映射(通过CASE语句实现)
SELECT
name,
CASE
WHEN age < 30 THEN 'Young'
WHEN age >= 30 AND age < 50 THEN 'Middle-aged'
ELSE 'Senior'
END AS age_group
FROM
basic_table;
数据的版本控制与变更跟踪
-- 数据的版本控制与变更跟踪(通过版本号和变更日志实现)
ALTER TABLE basic_table ADD COLUMN version INTEGER DEFAULT 1;
CREATE TABLE change_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
table_name TEXT,
operation TEXT,
old_data TEXT,
new_data TEXT,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TRIGGER trg_change_log
AFTER INSERT OR UPDATE OR DELETE ON basic_table
BEGIN
INSERT INTO change_log (table_name, operation, old_data, new_data)
VALUES ('basic_table',
CASE
WHEN OLD.id IS NULL THEN 'INSERT'
WHEN NEW.id IS NULL THEN 'DELETE'
ELSE 'UPDATE'
END,
OLD.*,
NEW.*);
UPDATE basic_table SET version = version + 1 WHERE id = NEW.id;
END;
数据的权限控制与访问管理
-- 数据的权限控制与访问管理(通过授权和角色管理实现)
-- 创建角色
CREATE ROLE admin;
-- 授予角色权限
GRANT ALL ON basic_table TO admin;
-- 创建用户并授予角色
CREATE USER user1 WITH PASSWORD 'password1';
GRANT admin TO user1;
-- 撤销权限
REVOKE ALL ON basic_table FROM admin;
数据的分组与汇总
-- 数据的分组与汇总(通过GROUP BY和聚合函数实现)
SELECT
department,
COUNT(*) AS num_employees,
AVG(salary) AS avg_salary
FROM
employee_table
GROUP BY
department;
数据的筛选与过滤
-- 数据的筛选与过滤(通过WHERE子句实现)
SELECT * FROM employee_table WHERE age > 30 AND department = 'IT';
SUBSTR数据的拆分与合并
-- 数据的拆分与合并(通过字符串函数实现)
SELECT
SUBSTR(name, 1, 1) AS first_initial,
SUBSTR(name, 2) AS remaining_name
FROM
employee_table;
数据的加密与解密
-- 数据的加密与解密(SQLite不原生支持加密,需要借助扩展库或应用层加密)
-- 示例:使用SQLCipher扩展库
PRAGMA key='password'; -- 设置加密密码
ATTACH DATABASE 'encrypted.db' AS encrypted KEY 'password';
SELECT sqlcipher_export('encrypted');
DETACH DATABASE encrypted;
数据的唯一性与一致性
-- 数据的唯一性与一致性(通过UNIQUE约束实现)
CREATE TABLE unique_table (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE
);
数据的随机化与分布
-- 数据的随机化与分布(通过RANDOM函数实现)
SELECT
name,
RANDOM() AS random_number
FROM
employee_table;
数据的缓存与优化
-- 数据的缓存与优化(通过索引和视图实现)
CREATE INDEX idx_name ON employee_table (name);
CREATE VIEW high_salary_employees AS
SELECT * FROM employee_table WHERE salary > 50000;
数据的批量导入与导出
-- 数据的批量导入与导出(通过导入导出命令实现)
-- 导出数据到CSV文件
.headers on
.mode csv
.output data.csv
SELECT * FROM employee_table;
.output stdout
-- 从CSV文件导入数据
.mode csv
.import data.csv employee_table
数据的动态生成与填充
-- 数据的动态生成与填充(通过循环和自增函数实现)
INSERT INTO numbers_table (number) VALUES (1);
WITH RECURSIVE generate_numbers(n) AS (
SELECT 1
UNION ALL
SELECT n+1 FROM generate_numbers WHERE n < 100
)
INSERT INTO numbers_table (number)
SELECT n FROM generate_numbers;
数据的滑动窗口与移动平均
-- 数据的滑动窗口与移动平均(通过窗口函数实现)
SELECT
date,
value,
AVG(value) OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM
sales_data;
数据的差异与比较
-- 数据的差异与比较(通过JOIN和比较操作符实现)
SELECT
t1.name,
t1.salary AS current_salary,
t2.salary AS previous_salary,
t1.salary - t2.salary AS salary_change
FROM
employee_table t1
JOIN
previous_employee_table t2 ON t1.id = t2.id;
数据的周期性与季节性
-- 数据的周期性与季节性(通过日期函数和条件过滤实现)
SELECT
date,
value,
CASE
WHEN STRFTIME('%m', date) IN ('03', '04', '05') THEN 'Spring'
WHEN STRFTIME('%m', date) IN ('06', '07', '08') THEN 'Summer'
WHEN STRFTIME('%m', date) IN ('09', '10', '11') THEN 'Fall'
WHEN STRFTIME('%m', date) IN ('12', '01', '02') THEN 'Winter'
END AS season
FROM
sales_data;
数据的时间序列与趋势
-- 数据的时间序列与趋势(通过时间函数和聚合函数实现)
SELECT
STRFTIME('%Y-%m', date) AS month,
AVG(value) AS avg_value
FROM
sales_data
GROUP BY
month;
数据的地理空间处理
-- 数据的地理空间处理(使用SQLite的地理空间扩展)
CREATE VIRTUAL TABLE locations USING fts5(name, latitude, longitude);
INSERT INTO locations (name, latitude, longitude) VALUES ('New York', 40.7128, -74.0060);
INSERT INTO locations (name, latitude, longitude) VALUES ('Los Angeles', 34.0522, -118.2437);
SELECT * FROM locations WHERE distance(latitude, longitude, 40.7128, -74.0060) < 50;
数据的机器学习与预测
-- 数据的机器学习与预测(通过扩展库实现)
-- 示例:使用SQLiteML实现线性回归
CREATE TABLE training_data (x INTEGER, y INTEGER);
INSERT INTO training_data VALUES (1, 2), (2, 3), (3, 4), (4, 5), (5, 6);
SELECT linearmodel_create('lm', 'training_data', 'y', 'x');
SELECT linearmodel_predict('lm', 6);
数据的分布式处理
-- 数据的分布式处理(使用SQLite的分布式扩展)
-- 示例:使用DBHub.io进行分布式查询
ATTACH DATABASE 'https://dbhub.io/user/db/example.db' AS remote;
SELECT * FROM remote.employee_table;
数据的多语言处理
-- 数据的多语言处理(通过国际化和本地化实现)
CREATE TABLE multilingual_data (
id INTEGER PRIMARY KEY,
english TEXT,
chinese TEXT
);
INSERT INTO multilingual_data (english, chinese) VALUES ('Hello', '你好');
SELECT english, chinese FROM multilingual_data;
*数据的图像处理与分析
-- 数据的图像处理与分析(通过扩展库实现)
-- 示例:使用SQLite的图像处理扩展
CREATE VIRTUAL TABLE images USING rtree(id, left, top, right, bottom);
INSERT INTO images VALUES (1, 0, 0, 100, 100);
SELECT * FROM images WHERE intersects(left, top, right, bottom, 50, 50, 60, 60);
*数据的音频处理与分析
-- 数据的音频处理与分析(通过扩展库实现)
-- 示例:使用SQLite的音频处理扩展
CREATE VIRTUAL TABLE audio USING audiotable(id, data);
INSERT INTO audio VALUES (1, 'path/to/audio.wav');
SELECT * FROM audio WHERE audiotable_detect_language(data) = 'English';
数据的半结构化处理与分析
-- 数据的半结构化处理与分析(通过JSON和XML函数实现)
CREATE TABLE json_data (id INTEGER PRIMARY KEY, data JSON);
INSERT INTO json_data (data) VALUES ('{"name": "John", "age": 30}');
SELECT json_extract(data, '$.name') AS name FROM json_data;
数据的自然语言处理与分析
-- 数据的自然语言处理与分析(通过全文搜索实现)
CREATE VIRTUAL TABLE documents USING fts5(content);
INSERT INTO documents VALUES ('This is a document about SQLite.');
SELECT * FROM documents WHERE documents MATCH 'SQLite';
*数据的事件流处理与分析
-- 数据的事件流处理与分析(通过流处理库实现)
-- 示例:使用SQLite的流处理扩展
CREATE STREAM sensor_data (time TIMESTAMP, value REAL);
INSERT INTO sensor_data VALUES ('2024-05-20 08:00:00', 25.5);
SELECT * FROM sensor_data WHERE time >= '2024-05-20 08:00:00';
*数据的知识图谱处理与分析
-- 数据的知识图谱处理与分析(通过图数据库实现)
-- 示例:使用SQLite的图数据库扩展
CREATE TABLE graph (source TEXT, target TEXT);
INSERT INTO graph VALUES ('A', 'B'), ('B', 'C'), ('C', 'D');
WITH RECURSIVE traverse_graph(source, target, depth) AS (
SELECT source, target, 1 FROM graph
UNION ALL
SELECT g.source, g.target, tg.depth + 1 FROM graph AS g
JOIN traverse_graph AS tg ON g.source = tg.target
)
SELECT * FROM traverse_graph;
数据的可视化与报表
-- 数据的可视化与报表(通过查询和图表库实现)
-- 示例:使用SQLite的图表扩展
CREATE VIEW sales_report AS
SELECT
STRFTIME('%Y-%m', date) AS month,
SUM(amount) AS total_sales
FROM
sales_data
GROUP BY
month;
SELECT * FROM sales_report;
*数据的智能推荐与个性化
-- 数据的智能推荐与个性化(通过机器学习和推荐算法实现)
-- 示例:使用SQLite的推荐扩展
CREATE TABLE user_preferences (user_id INTEGER, item_id INTEGER, rating INTEGER);
INSERT INTO user_preferences VALUES (1, 1, 5), (1, 2, 4), (2, 1, 4), (2, 3, 5);
SELECT * FROM recommender_model('user_preferences');
继续扩展数据操作语言(DML)的测试,我们可以添加更多的操作和场景,以覆盖更多的功能需求。
数据的异常检测与处理
-- 数据的异常检测与处理(通过统计方法实现)
-- 示例:使用Z分数进行异常检测
WITH stats AS (
SELECT
AVG(value) AS mean,
STDEV(value) AS std_dev
FROM
sensor_data
)
SELECT
sensor_data.*,
(sensor_data.value - stats.mean) / stats.std_dev AS z_score
FROM
sensor_data
CROSS JOIN
stats;
*数据的内容分析与分类
-- 数据的内容分析与分类(通过文本处理函数和机器学习模型实现)
CREATE TABLE text_data (id INTEGER PRIMARY KEY, text TEXT, category TEXT);
INSERT INTO text_data (text) VALUES ('This is a positive review'), ('This is a negative review');
CREATE VIRTUAL TABLE text_classifier USING classifier(tokenize='porter', type='svm');
INSERT INTO text_classifier(text, category) SELECT text, category FROM text_data;
SELECT * FROM text_classifier WHERE text MATCH 'positive';
数据的语义分析与推理
-- 数据的语义分析与推理(通过语义网络实现)
-- 示例:使用SQLite的语义网络扩展
CREATE TABLE semantic_network (subject TEXT, predicate TEXT, object TEXT);
INSERT INTO semantic_network VALUES ('cat', 'is_a', 'mammal'), ('mammal', 'is_a', 'animal');
SELECT * FROM semantic_network WHERE subject = 'cat';
数据的动态更新与变更
-- 数据的动态更新与变更(通过触发器实现)
CREATE TRIGGER update_timestamp AFTER UPDATE ON employee_table
BEGIN
UPDATE employee_table SET last_updated = CURRENT_TIMESTAMP WHERE id = NEW.id;
END;
数据的分级控制与权限管理
-- 数据的分级控制与权限管理(通过视图和授权实现)
CREATE VIEW sensitive_data AS SELECT * FROM employee_table WHERE salary > 100000;
GRANT SELECT ON sensitive_data TO manager_role;
数据的连续查询与复杂分析
-- 数据的连续查询与复杂分析(通过子查询和窗口函数实现)
WITH monthly_sales AS (
SELECT
STRFTIME('%Y-%m', date) AS month,
SUM(amount) AS total_sales
FROM
sales_data
GROUP BY
month
)
SELECT
month,
total_sales,
AVG(total_sales) OVER (ORDER BY month ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) AS moving_avg
FROM
monthly_sales;
*数据的实时流式处理与分析
-- 数据的实时流式处理与分析(通过流处理库实现)
-- 示例:使用SQLite的流处理扩展
CREATE STREAM real_time_data (time TIMESTAMP, value REAL);
INSERT INTO real_time_data VALUES ('2024-05-20 08:00:00', 25.5);
SELECT * FROM real_time_data WHERE time >= '2024-05-20 08:00:00';
*数据的多模态处理与分析
-- 数据的多模态处理与分析(通过多模态扩展库实现)
-- 示例:使用SQLite的多模态扩展库
CREATE VIRTUAL TABLE multimedia_data USING multimodal(name TEXT, image BLOB, audio BLOB, video BLOB);
INSERT INTO multimedia_data VALUES ('Item 1', 'path/to/image.jpg', 'path/to/audio.wav', 'path/to/video.mp4');
SELECT * FROM multimedia_data;
*数据的关联规则挖掘与分析
-- 数据的关联规则挖掘与分析(通过关联规则挖掘算法实现)
-- 示例:使用SQLite的关联规则挖掘扩展库
CREATE TABLE market_basket (transaction_id INTEGER, item TEXT);
INSERT INTO market_basket VALUES (1, 'apple'), (1, 'banana'), (2, 'apple'), (2, 'orange');
SELECT * FROM apriori('market_basket', 0.5, 0.5);
*数据的时间序列模式挖掘与分析
-- 数据的时间序列模式挖掘与分析(通过时间序列模式挖掘算法实现)
-- 示例:使用SQLite的时间序列模式挖掘扩展库
CREATE TABLE sensor_data (time TIMESTAMP, value REAL);
INSERT INTO sensor_data VALUES ('2024-05-20 08:00:00', 25.5), ('2024-05-20 08:15:00', 26.0);
SELECT * FROM sequence_mining('sensor_data', 'time', 2, 0.5);
*数据的演化与迁移
-- 数据的演化与迁移(通过数据迁移工具实现)
-- 示例:使用SQLite的数据迁移工具
ATTACH DATABASE 'old_data.db' AS old;
INSERT INTO new_table SELECT * FROM old.old_table;
*数据的模型管理与部署
-- 数据的模型管理与部署(通过模型管理平台实现)
-- 示例:使用SQLite的模型管理扩展库
CREATE MODEL classification_model USING svm(features, target);
数据的文档化与注释
-- 数据的文档化与注释(通过注释实现)
-- 示例:在表上添加注释
COMMENT ON TABLE employee_table IS 'This table stores employee information.';
*数据的增强与增值
-- 数据的增强与增值(通过数据增值服务实现)
-- 示例:使用SQLite的增值服务扩展库
SELECT * FROM enrich_data('employee_table');
*2.3 数据控制语言(DCL)
在SQLite中,数据控制语言(DCL)功能较为有限,因为SQLite是一个嵌入式数据库,不支持多用户管理和权限控制等高级功能。然而,我们仍然可以通过模拟一些DCL操作来测试数据控制功能。在其他数据库系统中(如MySQL、PostgreSQL、Oracle等),DCL主要包括GRANT、REVOKE等操作,用于控制用户的权限。
模拟用户和权限管理
首先,我们需要创建用户和权限表:
-- 用户表
CREATE TABLE users (
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
role TEXT NOT NULL
);
-- 权限表
CREATE TABLE permissions (
perm_id INTEGER PRIMARY KEY AUTOINCREMENT,
role TEXT NOT NULL,
resource TEXT NOT NULL,
can_read BOOLEAN NOT NULL,
can_write BOOLEAN NOT NULL,
can_delete BOOLEAN NOT NULL
);
-- 插入用户
INSERT INTO users (username, password, role) VALUES ('admin', 'admin_password', 'admin');
INSERT INTO users (username, password, role) VALUES ('editor', 'editor_password', 'editor');
INSERT INTO users (username, password, role) VALUES ('viewer', 'viewer_password', 'viewer');
-- 插入权限
INSERT INTO permissions (role, resource, can_read, can_write, can_delete) VALUES
('admin', 'all', 1, 1, 1),
('editor', 'articles', 1, 1, 0),
('viewer', 'articles', 1, 0, 0);
模拟GRANT操作
模拟授予权限的操作:
-- 给编辑用户授予删除文章的权限
UPDATE permissions SET can_delete = 1 WHERE role = 'editor' AND resource = 'articles';
-- 给查看用户授予写文章的权限
UPDATE permissions SET can_write = 1 WHERE role = 'viewer' AND resource = 'articles';
模拟REVOKE操作
模拟撤销权限的操作:
-- 从编辑用户撤销删除文章的权限
UPDATE permissions SET can_delete = 0 WHERE role = 'editor' AND resource = 'articles';
-- 从查看用户撤销写文章的权限
UPDATE permissions SET can_write = 0 WHERE role = 'viewer' AND resource = 'articles';
检查权限
查询用户的权限:
-- 查询编辑用户的文章权限
SELECT * FROM permissions WHERE role = 'editor' AND resource = 'articles';
-- 查询查看用户的文章权限
SELECT * FROM permissions WHERE role = 'viewer' AND resource = 'articles';
模拟权限控制的具体应用
结合用户和权限表来控制具体操作:
-- 示例:插入文章时检查权限
INSERT INTO articles (title, content, author_id)
SELECT 'New Article', 'This is a new article.', user_id
FROM users
WHERE username = 'editor' AND (SELECT can_write FROM permissions WHERE role = users.role AND resource = 'articles') = 1;
-- 示例:删除文章时检查权限
DELETE FROM articles
WHERE article_id = 1 AND
(SELECT can_delete FROM permissions WHERE role = (SELECT role FROM users WHERE username = 'editor') AND resource = 'articles') = 1;
-- 示例:查询文章时检查权限
SELECT * FROM articles
WHERE (SELECT can_read FROM permissions WHERE role = (SELECT role FROM users WHERE username = 'viewer') AND resource = 'articles') = 1;
模拟用户权限验证
模拟用户登录并验证其权限:
-- 用户登录验证
SELECT * FROM users WHERE username = 'admin' AND password = 'admin_password';
-- 验证用户是否具有特定权限
SELECT * FROM permissions WHERE role = 'editor' AND resource = 'articles' AND can_write = 1;
模拟用户权限管理
模拟用户的权限管理操作:
-- 添加新角色
INSERT INTO permissions (role, resource, can_read, can_write, can_delete) VALUES ('author', 'articles', 1, 1, 0);
-- 删除角色及其权限
DELETE FROM permissions WHERE role = 'author';
模拟角色间权限继承
模拟角色之间权限的继承和覆盖:
-- 给编辑用户的角色继承管理员的所有权限
INSERT INTO permissions (role, resource, can_read, can_write, can_delete)
SELECT 'editor', resource, can_read, can_write, can_delete FROM permissions WHERE role = 'admin';
-- 修改编辑用户的角色的某些权限
UPDATE permissions SET can_delete = 0 WHERE role = 'editor' AND resource = 'articles';
模拟角色的特殊权限
模拟特殊角色拥有的特殊权限:
-- 给超级管理员角色添加特殊权限
INSERT INTO permissions (role, resource, can_read, can_write, can_delete) VALUES ('super_admin', 'all', 1, 1, 1);
-- 确认用户是否拥有超级管理员权限
SELECT * FROM permissions WHERE role = 'super_admin' AND resource = 'all';
模拟权限的细粒度控制
模拟对特定用户或角色的特定资源的细粒度权限控制:
-- 给特定用户授予特定资源的特定权限
INSERT INTO permissions (role, resource, can_read, can_write, can_delete) VALUES ('editor', 'specific_resource', 1, 0, 0);
-- 确认特定用户是否拥有特定资源的特定权限
SELECT * FROM permissions WHERE role = 'editor' AND resource = 'specific_resource';
模拟用户账户管理
模拟用户账户的创建、禁用和删除操作:
-- 创建新用户账户
INSERT INTO users (username, password, role) VALUES ('new_user', 'new_password', 'viewer');
-- 禁用用户账户
UPDATE users SET status = 'disabled' WHERE username = 'new_user';
-- 删除用户账户
DELETE FROM users WHERE username = 'new_user';
模拟用户密码重置
模拟管理员重置用户密码的操作:
-- 管理员重置用户密码
UPDATE users SET password = 'new_password' WHERE username = 'user_to_reset';
模拟用户角色调整
模拟管理员调整用户角色的操作:
-- 管理员将用户角色从编辑者调整为查看者
UPDATE users SET role = 'viewer' WHERE username = 'user_to_adjust';
模拟用户登录记录
模拟记录用户登录信息的操作:
-- 创建用户登录记录表
CREATE TABLE login_logs (
log_id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER,
login_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
-- 记录用户登录信息
INSERT INTO login_logs (user_id) VALUES ((SELECT user_id FROM users WHERE username = 'user_to_log'));
模拟用户活动日志记录
模拟记录用户操作日志的操作:
-- 创建用户操作日志表
CREATE TABLE activity_logs (
log_id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER,
activity TEXT,
activity_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
-- 记录用户操作日志
INSERT INTO activity_logs (user_id, activity) VALUES ((SELECT user_id FROM users WHERE username = 'user_to_log'), 'Performed action X');
2.4 事务控制语言(TCL)
开始事务
-- 开始一个简单事务
BEGIN TRANSACTION;
提交事务
-- 提交一个简单事务
COMMIT;
回滚事务
-- 回滚一个简单事务
ROLLBACK;
保存点和回滚到保存点
-- 使用保存点
BEGIN TRANSACTION;
-- 插入一些数据
INSERT INTO users (username, password, email) VALUES ('test_user1', 'password1', 'test1@example.com');
-- 创建保存点
SAVEPOINT savepoint1;
-- 插入更多数据
INSERT INTO users (username, password, email) VALUES ('test_user2', 'password2', 'test2@example.com');
-- 回滚到保存点
ROLLBACK TO savepoint1;
-- 提交事务
COMMIT;
嵌套事务(通过保存点模拟)
SQLite不支持真正的嵌套事务,但可以通过保存点模拟:
BEGIN TRANSACTION;
-- 插入数据
INSERT INTO users (username, password, email) VALUES ('nested_user1', 'password', 'nested1@example.com');
-- 第一个保存点
SAVEPOINT savepoint1;
-- 插入数据
INSERT INTO users (username, password, email) VALUES ('nested_user2', 'password', 'nested2@example.com');
-- 第二个保存点
SAVEPOINT savepoint2;
-- 插入数据
INSERT INTO users (username, password, email) VALUES ('nested_user3', 'password', 'nested3@example.com');
-- 回滚到第二个保存点
ROLLBACK TO savepoint2;
-- 提交事务
COMMIT;
检查事务的一致性和完整性
通过事务确保一致性和完整性:
BEGIN TRANSACTION;
-- 插入用户和订单
INSERT INTO users (username, password, email) VALUES ('transaction_user', 'password', 'trans@example.com');
INSERT INTO orders (user_id, total) VALUES ((SELECT id FROM users WHERE username = 'transaction_user'), 500.00);
-- 检查插入是否成功
SELECT * FROM users WHERE username = 'transaction_user';
SELECT * FROM orders WHERE user_id = (SELECT id FROM users WHERE username = 'transaction_user');
-- 如果以上操作成功,则提交事务
COMMIT;
-- 如果操作失败,则回滚事务
ROLLBACK;
事务控制中的异常处理
模拟在事务过程中出现异常,并进行回滚:
BEGIN TRANSACTION;
-- 插入数据
INSERT INTO users (username, password, email) VALUES ('exception_user', 'password', 'exception@example.com');
-- 模拟异常(尝试插入重复的唯一键)
INSERT INTO users (username, password, email) VALUES ('exception_user', 'password', 'exception@example.com');
-- 如果插入成功,则提交事务
COMMIT;
-- 如果插入失败,则回滚事务
ROLLBACK;
高级事务管理
模拟复杂的事务管理,包括多表操作:
BEGIN TRANSACTION;
-- 插入用户
INSERT INTO users (username, password, email) VALUES ('complex_user', 'password', 'complex@example.com');
-- 创建保存点
SAVEPOINT savepoint1;
-- 插入订单
INSERT INTO orders (user_id, total) VALUES ((SELECT id FROM users WHERE username = 'complex_user'), 1000.00);
-- 创建第二个保存点
SAVEPOINT savepoint2;
-- 插入订单项目
INSERT INTO order_items (order_id, product_id, quantity, price)
VALUES ((SELECT id FROM orders WHERE user_id = (SELECT id FROM users WHERE username = 'complex_user')), 1, 2, 500.00);
-- 回滚到第一个保存点
ROLLBACK TO savepoint1;
-- 提交事务
COMMIT;
使用事务实现原子性操作
在多个操作组成的逻辑单元中使用事务,保证操作的原子性:
BEGIN TRANSACTION;
-- 查询用户信息
SELECT * FROM users WHERE username = 'atomic_user';
-- 插入订单
INSERT INTO orders (user_id, total) VALUES ((SELECT id FROM users WHERE username = 'atomic_user'), 200.00);
-- 更新用户信息
UPDATE users SET email = 'atomic@example.com' WHERE username = 'atomic_user';
-- 提交事务
COMMIT;
回滚到指定保存点
在事务过程中,回滚到之前创建的指定保存点,撤销部分操作:
BEGIN TRANSACTION;
-- 插入用户信息
INSERT INTO users (username, password, email) VALUES ('rollback_user', 'password', 'rollback@example.com');
-- 创建保存点
SAVEPOINT savepoint1;
-- 插入订单
INSERT INTO orders (user_id, total) VALUES ((SELECT id FROM users WHERE username = 'rollback_user'), 300.00);
-- 插入订单项目
INSERT INTO order_items (order_id, product_id, quantity, price) VALUES ((SELECT id FROM orders WHERE user_id = (SELECT id FROM users WHERE username = 'rollback_user')), 1, 2, 150.00);
-- 回滚到保存点
ROLLBACK TO savepoint1;
-- 提交事务
COMMIT;
多次保存点和回滚
在一个事务中创建多个保存点,并在需要时回滚到不同的保存点:
BEGIN TRANSACTION;
-- 插入用户信息
INSERT INTO users (username, password, email) VALUES ('multi_savepoint_user', 'password', 'multi_savepoint@example.com');
-- 创建保存点1
SAVEPOINT savepoint1;
-- 插入订单
INSERT INTO orders (user_id, total) VALUES ((SELECT id FROM users WHERE username = 'multi_savepoint_user'), 400.00);
-- 创建保存点2
SAVEPOINT savepoint2;
-- 插入订单项目
INSERT INTO order_items (order_id, product_id, quantity, price) VALUES ((SELECT id FROM orders WHERE user_id = (SELECT id FROM users WHERE username = 'multi_savepoint_user')), 1, 2, 200.00);
-- 回滚到保存点1
ROLLBACK TO savepoint1;
-- 提交事务
COMMIT;
模拟事务超时和自动回滚
模拟长时间执行的事务,在指定时间后自动回滚:
BEGIN TRANSACTION;
-- 设置事务超时时间为10秒
PRAGMA busy_timeout = 10000;
-- 插入大量数据(模拟长时间执行)
INSERT INTO big_table (data) SELECT randomblob(1000000) FROM generate_series(1, 10000);
-- 提交事务
COMMIT;
执行事务中的查询
在事务中执行查询操作,保证数据的一致性和可靠性:
BEGIN TRANSACTION;
-- 查询用户信息
SELECT * FROM users WHERE username = 'transaction_user';
-- 查询订单信息
SELECT * FROM orders WHERE user_id = (SELECT id FROM users WHERE username = 'transaction_user');
-- 提交事务
COMMIT;
事务的嵌套和异常处理
模拟在事务内部发生异常时的回滚操作,包括事务的嵌套:
BEGIN TRANSACTION;
-- 插入用户信息
INSERT INTO users (username, password, email) VALUES ('nested_transaction_user', 'password', 'nested@example.com');
-- 创建保存点
SAVEPOINT savepoint1;
-- 开始内部事务
BEGIN TRANSACTION;
-- 尝试插入重复的数据,模拟异常
INSERT INTO users (username, password, email) VALUES ('nested_transaction_user', 'password', 'nested@example.com');
-- 如果出现异常,回滚到保存点
ROLLBACK TO savepoint1;
-- 提交内部事务
COMMIT;
-- 提交外部事务
COMMIT;
手动设置事务隔离级别
测试不同的事务隔离级别,包括READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE:
-- 设置事务隔离级别为READ UNCOMMITTED
PRAGMA read_uncommitted = true;
-- 设置事务隔离级别为READ COMMITTED
PRAGMA read_uncommitted = false;
-- 设置事务隔离级别为REPEATABLE READ
PRAGMA read_uncommitted = false;
-- 设置事务隔离级别为SERIALIZABLE
PRAGMA read_uncommitted = false;
模拟长时间事务和死锁
模拟长时间执行的事务和事务之间的死锁情况:
-- 创建一个长时间执行的事务
BEGIN TRANSACTION;
-- 插入大量数据
INSERT INTO big_table (data) SELECT randomblob(1000000) FROM generate_series(1, 10000);
-- 查询操作(模拟长时间执行)
SELECT * FROM users;
-- 提交事务
COMMIT;
事务监控和管理
监控和管理事务,包括查看当前事务、终止事务等操作:
-- 查看当前活动的事务
SELECT * FROM sqlite_master WHERE type = 'table' AND name = 'sqlite_transaction';
-- 终止指定的事务
PRAGMA foreign_keys = true; -- 需要启用外键约束
DELETE FROM sqlite_transaction WHERE tid = <transaction_id>;
模拟多线程并发事务
模拟多个线程同时执行事务的情况,测试事务的并发性和一致性:
-- 在多个客户端连接中执行并发事务操作
-- 客户端1
BEGIN TRANSACTION;
-- 客户端2
BEGIN TRANSACTION;
-- ... 其他客户端
-- 提交或回滚事务
模拟长时间读事务导致的阻塞
模拟一个长时间运行的读事务,在此期间其他事务被阻塞:
-- 客户端1开始一个读事务
BEGIN TRANSACTION;
-- 客户端2尝试修改受客户端1事务影响的数据
-- 如果客户端1的事务一直处于活动状态,客户端2的事务将被阻塞
模拟长时间写事务导致的阻塞
模拟一个长时间运行的写事务,在此期间其他事务被阻塞:
-- 客户端1开始一个写事务
BEGIN TRANSACTION;
-- 客户端2尝试读取受客户端1事务影响的数据
-- 如果客户端1的事务一直处于活动状态,客户端2的读操作将被阻塞
模拟事务日志满导致的阻塞
模拟当事务日志满时导致的阻塞情况:
-- 启用事务日志
PRAGMA journal_mode = WAL;
-- 使事务日志满,导致后续事务操作被阻塞
测试事务日志文件大小控制
测试设置事务日志文件的最大大小,以控制事务日志文件的大小:
-- 设置事务日志文件的最大大小
PRAGMA journal_size_limit = 1000000; -- 1MB
-- 当事务日志文件达到最大大小时,SQLite会自动回滚事务并清空日志文件,以防止文件过大影响性能
模拟事务的间隔提交
测试间隔提交(Batch Commit)的性能和影响:
-- 开启间隔提交模式
PRAGMA synchronous = OFF;
-- 在大批量插入或更新操作后,显式提交事务以减少IO压力和提高性能
模拟事务的手动调优
测试手动调优事务参数以提高性能:
-- 调整事务日志模式和同步方式
PRAGMA journal_mode = MEMORY; -- 使用内存模式以提高性能
PRAGMA synchronous = OFF; -- 关闭同步以减少IO压力
-- 完成大量数据插入或更新操作后,显式提交事务
COMMIT;
模拟事务的自动回滚
测试在发生错误时事务的自动回滚:
-- 开始事务
BEGIN TRANSACTION;
-- 插入数据
INSERT INTO users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
-- 模拟错误:尝试插入重复的唯一键
INSERT INTO users (username, password, email) VALUES ('user1', 'password2', 'user2@example.com');
-- 提交事务
COMMIT;
-- 如果发生错误,事务将自动回滚
模拟长时间事务和资源占用
测试长时间运行的事务对系统资源的占用情况:
-- 开始事务
BEGIN TRANSACTION;
-- 模拟长时间执行的操作
INSERT INTO big_table (data) SELECT randomblob(1000000) FROM generate_series(1, 10000);
-- 提交事务
COMMIT;
模拟事务的异常处理和日志记
测试异常处理和事务日志记录的情况:
-- 开始事务
BEGIN TRANSACTION;
-- 模拟异常处理:尝试插入无效数据
INSERT INTO users (username, password, email) VALUES (NULL, 'password', 'invalid_email');
-- 如果发生异常,记录错误日志并回滚事务
-- INSERT INTO error_log (timestamp, message) VALUES (CURRENT_TIMESTAMP, 'Failed to insert user data.');
-- 提交事务
COMMIT;
模拟事务的并发性
测试多个客户端同时执行事务时的并发性:
-- 客户端1
BEGIN TRANSACTION;
-- 执行事务操作
COMMIT;
-- 客户端2
BEGIN TRANSACTION;
-- 执行事务操作
COMMIT;
-- 客户端3
BEGIN TRANSACTION;
-- 执行事务操作
COMMIT;
-- ...
模拟事务的一致性和隔离性
测试事务的一致性和隔离级别:
-- 客户端1
BEGIN TRANSACTION;
-- 执行读取操作
SELECT * FROM users;
-- 客户端2
BEGIN TRANSACTION;
-- 执行写入操作
INSERT INTO users (username, password, email) VALUES ('new_user', 'password', 'new@example.com');
-- 提交客户端2的事务
COMMIT;
-- 客户端1再次执行读取操作
SELECT * FROM users;
-- 客户端1提交事务
COMMIT;
模拟事务的恢复与重试
测试在事务过程中发生错误时的恢复和重试:
-- 开始事务
BEGIN TRANSACTION;
-- 插入数据
INSERT INTO users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
-- 模拟可能出现的错误:违反唯一约束
INSERT INTO users (username, password, email) VALUES ('user1', 'password2', 'user2@example.com');
-- 如果出现错误,则回滚事务并重试
ROLLBACK;
-- 再次尝试插入数据
BEGIN TRANSACTION;
INSERT INTO users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
COMMIT;
模拟事务的版本控制和乐观锁定
测试在多个事务同时访问同一数据时的版本控制和乐观锁定:
-- 客户端1开始事务
BEGIN TRANSACTION;
-- 查询用户信息
SELECT * FROM users WHERE username = 'user1';
-- 客户端2开始事务
BEGIN TRANSACTION;
-- 修改用户密码
UPDATE users SET password = 'new_password' WHERE username = 'user1';
-- 客户端1尝试修改用户邮箱
UPDATE users SET email = 'new_email@example.com' WHERE username = 'user1';
-- 提交客户端1的事务
COMMIT;
-- 提交客户端2的事务
COMMIT;
模拟分布式事务
测试跨多个数据库的分布式事务:
-- 在数据库A上开始事务
BEGIN TRANSACTION;
-- 在数据库B上开始事务
BEGIN TRANSACTION;
-- 在数据库A上插入数据
INSERT INTO databaseA.users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
-- 在数据库B上插入数据
INSERT INTO databaseB.orders (user_id, total) VALUES ((SELECT id FROM databaseA.users WHERE username = 'user1'), 100.00);
-- 提交数据库B上的事务
COMMIT;
-- 提交数据库A上的事务
COMMIT;
模拟异步事务提交
测试异步提交事务的情况:
-- 开启异步事务提交模式
PRAGMA synchronous = NORMAL;
-- 开始事务
BEGIN TRANSACTION;
-- 插入数据
INSERT INTO users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
-- 异步提交事务
COMMIT;
模拟事务的失败处理
测试事务执行失败时的处理:
-- 开始事务
BEGIN TRANSACTION;
-- 插入数据
INSERT INTO users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
-- 提交事务(模拟失败)
COMMIT;
-- 如果事务提交失败,则进行回滚处理
ROLLBACK;
模拟事务的读取一致性
测试读取事务的一致性,即事务开始后读取的数据在事务结束前不会被修改:
-- 客户端1开始事务
BEGIN TRANSACTION;
-- 查询用户信息
SELECT * FROM users WHERE username = 'user1';
-- 客户端2同时修改用户信息
UPDATE users SET password = 'new_password' WHERE username = 'user1';
-- 客户端1再次查询用户信息
SELECT * FROM users WHERE username = 'user1';
-- 提交客户端1的事务
COMMIT;
-- 提交客户端2的事务
COMMIT;
模拟事务的写入一致性
测试写入事务的一致性,即事务开始后对数据的修改在事务结束前不会被其他事务读取:
-- 客户端1开始事务
BEGIN TRANSACTION;
-- 修改用户密码
UPDATE users SET password = 'new_password' WHERE username = 'user1';
-- 客户端2同时查询用户信息
SELECT * FROM users WHERE username = 'user1';
-- 客户端1提交事务
COMMIT;
-- 客户端2再次查询用户信息
SELECT * FROM users WHERE username = 'user1';
模拟事务的隔离级别
测试不同隔离级别下事务的行为:
-- 设置事务隔离级别为READ UNCOMMITTED
PRAGMA read_uncommitted = true;
-- 设置事务隔离级别为READ COMMITTED
PRAGMA read_uncommitted = false;
-- 设置事务隔离级别为REPEATABLE READ
PRAGMA read_uncommitted = false;
-- 设置事务隔离级别为SERIALIZABLE
PRAGMA read_uncommitted = false;
模拟事务的并发控制
测试并发控制机制对事务执行的影响:
-- 客户端1开始事务
BEGIN TRANSACTION;
-- 客户端2同时尝试修改相同数据
BEGIN TRANSACTION;
-- 客户端1提交事务
COMMIT;
-- 客户端2再次尝试提交事务
COMMIT;
模拟事务的嵌套
测试事务嵌套在不同隔离级别下的行为:
-- 客户端1开始事务
BEGIN TRANSACTION;
-- 在事务内部嵌套事务
BEGIN TRANSACTION;
-- 客户端1提交内部事务
COMMIT;
-- 客户端1再次尝试提交外部事务
COMMIT;
模拟事务的死锁
测试事务在发生死锁时的处理:
-- 客户端1开始事务
BEGIN TRANSACTION;
-- 客户端2同时尝试修改相同数据
BEGIN TRANSACTION;
-- 客户端1尝试修改其他数据,导致死锁
UPDATE users SET password = 'new_password' WHERE username = 'user2';
-- 客户端1尝试提交事务,但被阻塞
COMMIT;
-- 客户端2尝试提交事务,也被阻塞
-- 在外部环境中手动取消事务或等待超时
模拟事务的优先级
测试设置事务的优先级对并发执行的影响:
-- 设置客户端1事务的优先级为低
PRAGMA cache_size = -2000000; -- 设置缓存大小为负值表示低优先级
-- 客户端1开始事务
BEGIN TRANSACTION;
-- 客户端2同时尝试修改相同数据
BEGIN TRANSACTION;
-- 客户端1尝试提交事务,但由于低优先级而被阻塞
COMMIT;
-- 客户端2尝试提交事务,得到执行
COMMIT;
模拟事务的延迟提交
测试延迟提交事务对性能的影响:
-- 开启延迟提交模式
PRAGMA synchronous = NORMAL;
-- 开始事务
BEGIN TRANSACTION;
-- 插入大量数据
INSERT INTO big_table (data) SELECT randomblob(1000000) FROM generate_series(1, 10000);
-- 提交事务
COMMIT;
模拟事务的部分回滚
测试事务部分回滚的情况:
-- 开始事务
BEGIN TRANSACTION;
-- 插入数据
INSERT INTO users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
-- 创建保存点
SAVEPOINT savepoint1;
-- 插入更多数据
INSERT INTO users (username, password, email) VALUES ('user2', 'password2', 'user2@example.com');
-- 回滚到保存点
ROLLBACK TO savepoint1;
-- 提交事务
COMMIT;
模拟事务的预留
测试事务预留(Reserve)的情况:
-- 开始事务
BEGIN TRANSACTION;
-- 修改数据
UPDATE users SET password = 'new_password' WHERE username = 'user1';
-- 预留一部分数据,但不提交事务
RESERVE 10000; -- 预留 10000 字节的空间
-- 提交事务
COMMIT;
模拟事务的保存点嵌套
测试事务保存点的嵌套使用:
-- 开始事务
BEGIN TRANSACTION;
-- 创建保存点1
SAVEPOINT savepoint1;
-- 插入数据
INSERT INTO users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
-- 创建保存点2
SAVEPOINT savepoint2;
-- 更新数据
UPDATE users SET password = 'new_password' WHERE username = 'user1';
-- 回滚到保存点1
ROLLBACK TO savepoint1;
-- 提交事务
COMMIT;
模拟事务的保存点命名
测试命名保存点的使用:
-- 开始事务
BEGIN TRANSACTION;
-- 创建命名保存点
SAVEPOINT my_savepoint;
-- 插入数据
INSERT INTO users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
-- 回滚到保存点
ROLLBACK TO my_savepoint;
-- 提交事务
COMMIT;
模拟事务的保存点释放
测试释放保存点的效果:
-- 开始事务
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT savepoint1;
-- 插入数据
INSERT INTO users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
-- 释放保存点
RELEASE savepoint1;
-- 提交事务
COMMIT;
模拟事务的保存点回滚
测试回滚保存点的效果:
-- 开始事务
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT savepoint1;
-- 插入数据
INSERT INTO users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
-- 回滚到保存点
ROLLBACK TO savepoint1;
-- 提交事务
COMMIT;
模拟事务的保存点部分回滚
测试部分回滚到保存点的效果:
-- 开始事务
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT savepoint1;
-- 插入数据
INSERT INTO users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
-- 更新数据
UPDATE users SET password = 'new_password' WHERE username = 'user1';
-- 回滚到保存点
ROLLBACK TO savepoint1;
-- 提交事务
COMMIT;
模拟事务的保存点在多个事务中的使用
测试在多个事务中使用相同保存点的情况:
-- 开始事务1
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 在事务1中插入数据
INSERT INTO users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
-- 提交事务1
COMMIT;
-- 开始事务2
BEGIN TRANSACTION;
-- 回滚到保存点
ROLLBACK TO my_savepoint;
-- 在事务2中查询数据
SELECT * FROM users WHERE username = 'user1';
-- 提交事务2
COMMIT;
模拟事务的保存点在嵌套事务中的使用
测试在嵌套事务中使用保存点的情况:
-- 开始事务1
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 开始事务2
BEGIN TRANSACTION;
-- 在事务2中插入数据
INSERT INTO users (username, password, email) VALUES ('user2', 'password2', 'user2@example.com');
-- 提交事务2
COMMIT;
-- 回滚到保存点(事务2的修改不受影响)
ROLLBACK TO my_savepoint;
-- 提交事务1
COMMIT;
模拟事务的保存点和外键约束
测试保存点和外键约束的互动:
-- 开启外键约束
PRAGMA foreign_keys = ON;
-- 开始事务
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 在事务中插入数据
INSERT INTO users (username, password, email) VALUES ('user3', 'password3', 'user3@example.com');
-- 创建订单,但用户ID为不存在的用户,违反外键约束
INSERT INTO orders (user_id, total) VALUES (1000, 50.00);
-- 提交事务
COMMIT;
模拟事务的保存点和触发器
测试保存点和触发器的互动:
-- 创建一个触发器,当插入用户时自动插入订单
CREATE TRIGGER insert_order AFTER INSERT ON users
BEGIN
INSERT INTO orders (user_id, total) VALUES (NEW.id, 0.00);
END;
-- 开始事务
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 在事务中插入数据
INSERT INTO users (username, password, email) VALUES ('user4', 'password4', 'user4@example.com');
-- 提交事务
COMMIT;
模拟事务的保存点和索引
测试保存点和索引的互动:
-- 创建一个索引
CREATE INDEX idx_username ON users (username);
-- 开始事务
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 在事务中插入数据
INSERT INTO users (username, password, email) VALUES ('user5', 'password5', 'user5@example.com');
-- 提交事务
COMMIT;
模拟事务的保存点和查询优化
测试保存点对查询优化的影响:
-- 开始事务
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 在事务中插入数据
INSERT INTO users (username, password, email) VALUES ('user6', 'password6', 'user6@example.com');
-- 查询用户数据
SELECT * FROM users WHERE username = 'user6';
-- 回滚到保存点
ROLLBACK TO my_savepoint;
-- 查询用户数据
SELECT * FROM users WHERE username = 'user6';
-- 提交事务
COMMIT;
模拟事务的保存点和锁
测试保存点对锁的影响:
-- 开始事务1
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 查询用户数据
SELECT * FROM users WHERE username = 'user6';
-- 开始事务2
BEGIN TRANSACTION;
-- 尝试修改相同数据,但被锁定
UPDATE users SET password = 'new_password' WHERE username = 'user6';
-- 提交事务2
COMMIT;
-- 提交事务1
COMMIT;
模拟事务的保存点和并发控制
测试保存点对并发控制的影响:
-- 开始事务1
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 查询用户数据
SELECT * FROM users WHERE username = 'user6';
-- 开始事务2
BEGIN TRANSACTION;
-- 尝试修改相同数据,但被阻塞
UPDATE users SET password = 'new_password' WHERE username = 'user6';
-- 提交事务2
COMMIT;
-- 提交事务1
COMMIT;
模拟事务的保存点和查询缓存
测试保存点对查询缓存的影响:
-- 开始事务
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 查询用户数据
SELECT * FROM users WHERE username = 'user6';
-- 更新用户数据
UPDATE users SET password = 'new_password' WHERE username = 'user6';
-- 再次查询用户数据,观察是否使用缓存
SELECT * FROM users WHERE username = 'user6';
-- 回滚到保存点
ROLLBACK TO my_savepoint;
-- 再次查询用户数据,观察是否使用缓存
SELECT * FROM users WHERE username = 'user6';
-- 提交事务
COMMIT;
模拟事务的保存点和并发修改
测试保存点对并发修改的影响:
-- 开始事务1
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 查询用户数据
SELECT * FROM users WHERE username = 'user6';
-- 开始事务2
BEGIN TRANSACTION;
-- 尝试修改相同数据
UPDATE users SET password = 'new_password' WHERE username = 'user6';
-- 提交事务2
COMMIT;
-- 提交事务1
COMMIT;
模拟事务的保存点和异常处理
测试保存点与异常处理的互动:
-- 开始事务
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 尝试插入重复数据,触发异常
INSERT INTO users (username, password, email) VALUES ('user6', 'password6', 'user6@example.com');
-- 捕获异常,回滚到保存点
EXCEPTION WHEN UNIQUE_CONSTRAINT_VIOLATION THEN
ROLLBACK TO my_savepoint;
-- 提交事务
COMMIT;
模拟事务的保存点和数据完整性
测试保存点对数据完整性的维护:
-- 开启外键约束
PRAGMA foreign_keys = ON;
-- 创建两个表
CREATE TABLE parent (id INTEGER PRIMARY KEY);
CREATE TABLE child (id INTEGER PRIMARY KEY, parent_id INTEGER, FOREIGN KEY (parent_id) REFERENCES parent(id));
-- 开始事务
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 插入父表数据
INSERT INTO parent (id) VALUES (1);
-- 插入子表数据
INSERT INTO child (id, parent_id) VALUES (1, 1000);
-- 提交事务
COMMIT;
模拟事务的保存点和约束触发器
测试保存点对约束触发器的影响:
-- 创建一个触发器,当插入数据时触发约束
CREATE TRIGGER check_id BEFORE INSERT ON parent
BEGIN
SELECT CASE WHEN EXISTS (SELECT 1 FROM parent WHERE id = NEW.id) THEN RAISE(ABORT, 'ID already exists') END;
END;
-- 开始事务
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 尝试插入重复数据,触发触发器
INSERT INTO parent (id) VALUES (1);
-- 回滚到保存点
ROLLBACK TO my_savepoint;
-- 提交事务
COMMIT;
模拟事务的保存点和数据库链接
测试保存点对数据库链接的影响:
-- 在不同的数据库链接中进行操作
-- 数据库链接1
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 查询数据
SELECT * FROM users WHERE username = 'user6';
-- 数据库链接2
BEGIN TRANSACTION;
-- 尝试修改相同数据
UPDATE users SET password = 'new_password' WHERE username = 'user6';
-- 提交数据库链接2的事务
COMMIT;
-- 回滚到保存点(数据库链接1的修改不受影响)
ROLLBACK TO my_savepoint;
-- 提交数据库链接1的事务
COMMIT;
模拟事务的保存点和长时间运行
测试保存点对长时间运行事务的影响:
-- 开始事务
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 模拟长时间运行的操作
INSERT INTO big_table (data) SELECT randomblob(1000000) FROM generate_series(1, 10000);
-- 回滚到保存点
ROLLBACK TO my_savepoint;
-- 提交事务
COMMIT;
模拟事务的保存点和表锁
测试保存点对表锁的影响:
-- 开始事务1
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 查询数据
SELECT * FROM users WHERE username = 'user6';
-- 开始事务2
BEGIN TRANSACTION;
-- 尝试修改相同数据,但被阻塞
UPDATE users SET password = 'new_password' WHERE username = 'user6';
-- 提交事务2
COMMIT;
-- 回滚到保存点(事务2的修改不受影响)
ROLLBACK TO my_savepoint;
-- 提交事务1
COMMIT;
模拟事务的保存点和行级锁
测试保存点对行级锁的影响:
-- 开始事务
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 查询数据并加锁
SELECT * FROM users WHERE username = 'user6' FOR UPDATE;
-- 在另一个事务中尝试修改相同数据,但被阻塞
模拟事务的保存点和事务的继承
测试保存点在事务继承中的影响:
-- 开始事务1
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 插入数据
INSERT INTO users (username, password, email) VALUES ('user6', 'password6', 'user6@example.com');
-- 提交事务1
COMMIT;
-- 开始事务2
BEGIN TRANSACTION;
-- 查询数据(能够查询到已提交的数据)
SELECT * FROM users WHERE username = 'user6';
-- 提交事务2
COMMIT;
模拟事务的保存点和并发写入
测试保存点在并发写入场景中的影响:
-- 开始事务1
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 查询数据并加锁
SELECT * FROM users WHERE username = 'user6' FOR UPDATE;
-- 开始事务2
BEGIN TRANSACTION;
-- 尝试修改相同数据,但被阻塞
UPDATE users SET password = 'new_password' WHERE username = 'user6';
-- 提交事务2
COMMIT;
-- 回滚到保存点(事务2的修改不受影响)
ROLLBACK TO my_savepoint;
-- 提交事务1
COMMIT;
模拟事务的保存点和并发读写
测试保存点在并发读写场景中的影响:
-- 开始事务1
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 查询数据并加锁
SELECT * FROM users WHERE username = 'user6' FOR UPDATE;
-- 开始事务2
BEGIN TRANSACTION;
-- 查询相同数据(能够读取到,但不能写入)
SELECT * FROM users WHERE username = 'user6';
-- 提交事务2
COMMIT;
-- 回滚到保存点
ROLLBACK TO my_savepoint;
-- 提交事务1
COMMIT;
模拟事务的保存点和数据完整性约束
测试保存点对数据完整性约束的影响:
-- 创建一个检查约束
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT,
price REAL CHECK (price > 0)
);
-- 开始事务
BEGIN TRANSACTION;
-- 创建保存点
SAVEPOINT my_savepoint;
-- 尝试插入无效数据,触发检查约束
INSERT INTO products (id, name, price) VALUES (1, 'Product1', -10.00);
-- 回滚到保存点
ROLLBACK TO my_savepoint;
-- 提交事务
COMMIT;
2.5 数据查询语言(DQL)
sqlite本身不支持 :UNIX_TIMESTAMP、MINUTES、 INTERVAL、CONCAT、SUBSTRING_INDEX、 YEAR_MONTH、EXTRACT、FLOOR。
简单查询
-- 查询所有用户
SELECT * FROM users;
-- 查询特定列
SELECT username, email FROM users;
带条件的查询
-- 条件查询
SELECT * FROM users WHERE username = 'alice';
-- 复杂条件查询
SELECT * FROM users WHERE username = 'alice' AND email LIKE '%@example.com';
-- 使用OR条件
SELECT * FROM users WHERE username = 'alice' OR username = 'bob';
-- 使用IN条件
SELECT * FROM users WHERE username IN ('alice', 'bob', 'carol');
-- 使用BETWEEN条件
SELECT * FROM products WHERE price BETWEEN 50.00 AND 100.00;
-- 使用LIKE条件
SELECT * FROM users WHERE email LIKE '%example.com';
聚合查询aggregate function
聚合函数官网说明:Built-in Aggregate Functions (sqlite.org)
avg(X)
avg() 函数返回在一组中所有非 NULL 的 X 的平均值。不像数字的字符串和 BLOB 值会被解释为 0。无论是否所有输入为整数,avg() 的结果总是浮点值,只要至少有一个非 NULL 输入。如果没有非 NULL 输入,则 avg() 的结果为 NULL。avg() 的结果计算为 total()/count(),因此对 total() 适用的所有约束条件也适用于 avg()。
count(X) 、count(*)
count(X) 函数返回一组中 X 不为 NULL 的次数。count(*) 函数(无参数)返回组中的总行数。
group_concat(X) 、group_concat(X,Y)、 string_agg(X,Y)
group_concat() 函数返回一个字符串,该字符串是 X 的所有非 NULL 值的连接。如果参数 Y 存在,则将其用作 X 实例之间的分隔符。如果省略了 Y,则使用逗号(“,”)作为分隔符。
string_agg(X,Y) 函数是 group_concat(X,Y) 的别名。String_agg() 兼容 PostgreSQL 和 SQL-Server,group_concat() 兼容 MySQL。
除非在最后一个参数之后立即包含 ORDER BY 参数,否则连接元素的顺序是任意的。
max(X)
max() 聚合函数返回组中所有值的最大值。最大值是在同一列上的 ORDER BY 中返回的最后一个值。如果组中没有非 NULL 值,则聚合 max() 返回 NULL。
min(X)
min() 聚合函数返回组中所有值的最小非 NULL 值。最小值是在列的 ORDER BY 中出现的第一个非 NULL 值。如果组中没有非 NULL 值,则聚合 min() 返回 NULL。
sum(X)、 total(X)
sum() 和 total() 聚合函数返回组中所有非 NULL 值的总和。如果没有非 NULL 输入行,则 sum() 返回 NULL,但 total() 返回 0.0。对于行数为 0 的情况,NULL 通常不是一个有用的结果,但 SQL 标准要求这样做,大多数其他 SQL 数据库引擎也以这种方式实现 sum(),因此 SQLite 也以相同的方式实现。非标准的 total() 函数提供了一种方便的方法来解决 SQL 语言中的这个设计问题。
total() 的结果总是浮点值。如果所有非 NULL 输入都是整数,则 sum() 的结果是整数值。如果 sum() 的任何输入既不是整数也不是 NULL,则 sum() 返回一个浮点值,该值是数学求和的近似值。
如果所有输入都是整数或 NULL,并且在计算过程中发生整数溢出,则 sum() 将抛出 “integer overflow” 异常。如果之前的任何输入都是浮点值,则不会引发溢出错误。total() 永远不会引发整数溢出。
当对浮点值求和时,如果值的量级相差很大,则由于 IEEE 754 浮点值是近似值,结果可能不精确。使用十进制扩展中的 decimal_sum(X) 聚合可以获得浮点数的精确求和。考虑以下测试案例:
CREATE TABLE t1(x REAL); INSERT INTO t1 VALUES(1.55e+308),(1.23),(3.2e-16),(-1.23),(-1.55e308); SELECT sum(x), decimal_sum(x) FROM t1;
大值 ±1.55e+308 会互相抵消,但抵消直到求和结束,并且在此期间,大值 +1.55e+308 会压倒微小的 3.2e-16 值。最终结果是 sum() 的不精确结果。decimal_sum() 聚合会生成一个精确答案,但会增加额外的 CPU 和内存使用量。还要注意,decimal_sum() 不是内置于 SQLite 核心中的;它是一个可加载的扩展。
如果输入的和太大而无法表示为 IEEE 754 浮点值,则可能返回 +Infinity 或 -Infinity 结果。如果使用不同符号的非常大的值,以至于 SUM() 或 TOTAL() 函数无法确定正确的结果是 +Infinity 还是 -Infinity,或者介于两者之间的某个值,则结果为 NULL。因此,例如,以下查询返回 NULL:
WITH t1(x) AS (VALUES(1.0),(-9e+999),(2.0),(+9e+999),(3.0)) SELECT sum(x) FROM t1;
-- 计算用户表中密码的平均长度
SELECT avg(length(password)) AS avg_password_length FROM users;
-- 计算产品表中产品数量
SELECT count(*) AS product_count FROM products;
-- 计算订单表中的订单数量
SELECT count(*) AS order_count FROM orders;
-- 将用户表中所有用户名连接为一个字符串
SELECT group_concat(username, ', ') AS all_usernames FROM users;
-- 将订单表中订单总额连接为一个字符串,并使用' | '作为分隔符
SELECT group_concat(total, ' | ') AS all_order_totals FROM orders;
-- 找出产品表中价格最高的产品
SELECT max(price) AS max_price FROM products;
-- 找出用户表中注册时间最早的日期
SELECT min(created_at) AS earliest_registration FROM users;
-- 将产品表中所有产品名称连接为一个字符串,并使用' / '作为分隔符
SELECT string_agg(name, ' / ') AS all_product_names FROM products;
-- 计算订单表中所有订单的总额
SELECT sum(total) AS total_sales FROM orders;
-- 计算订单项目表中所有订单项目的数量
SELECT total(quantity) AS total_order_items FROM order_items;
分组查询
-- 分组并计算每个用户的订单数
SELECT user_id, COUNT(*) AS order_count FROM orders GROUP BY user_id;
-- 分组并计算每个产品的销售数量
SELECT product_id, SUM(quantity) AS total_quantity FROM order_items GROUP BY product_id;
-- 使用HAVING过滤分组结果
SELECT dept_id, AVG(salary) AS average_salary FROM employees GROUP BY dept_id HAVING AVG(salary) > 50000;
排序查询
-- 按用户名排序
SELECT * FROM users ORDER BY username ASC;
-- 按价格降序排序
SELECT * FROM products ORDER BY price DESC;
-- 多列排序
SELECT * FROM employees ORDER BY dept_id ASC, salary DESC;
连接查询
-- 内连接查询
SELECT
orders.id AS order_id,
users.username,
products.name AS product_name,
order_items.quantity,
order_items.price
FROM
orders
JOIN
users ON orders.user_id = users.id
JOIN
order_items ON orders.id = order_items.order_id
JOIN
products ON order_items.product_id = products.id;
-- 左连接查询
SELECT
users.username,
orders.id AS order_id,
orders.total
FROM
users
LEFT JOIN
orders ON users.id = orders.user_id;
-- 右连接查询(SQLite不支持右连接,可以通过调整表顺序模拟)
SELECT
orders.id AS order_id,
users.username,
orders.total
FROM
orders
LEFT JOIN
users ON orders.user_id = users.id;
子查询
-- 简单子查询
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE total > 100.00);
-- 相关子查询
SELECT username, (SELECT COUNT(*) FROM orders WHERE orders.user_id = users.id) AS order_count FROM users;
-- 从子查询中选择
SELECT * FROM (SELECT username, email FROM users) AS subquery WHERE email LIKE '%example.com';
-- 使用子查询进行聚合
SELECT
dept_id,
(SELECT AVG(salary) FROM employees AS e WHERE e.dept_id = d.dept_id) AS average_salary
FROM
department AS d;
-- 使用子查询进行多层嵌套查询
SELECT *
FROM
(SELECT * FROM users WHERE id IN
(SELECT user_id FROM orders WHERE total > 1000)
) AS subquery;
联合查询
-- UNION操作
SELECT username FROM users WHERE email LIKE '%@example.com'
UNION
SELECT username FROM archived_users WHERE email LIKE '%@example.com';
-- UNION ALL操作
SELECT username FROM users WHERE email LIKE '%@example.com'
UNION ALL
SELECT username FROM archived_users WHERE email LIKE '%@example.com';
-- INTERSECT操作
SELECT username FROM users WHERE email LIKE '%@example.com'
INTERSECT
SELECT username FROM archived_users WHERE email LIKE '%@example.com';
-- EXCEPT操作
SELECT username FROM users WHERE email LIKE '%@example.com'
EXCEPT
SELECT username FROM archived_users WHERE email LIKE '%@example.com';
-- JOIN内连接查询:返回两个表中匹配的行
SELECT *
FROM orders
JOIN users ON orders.user_id = users.id;
-- LEFT JOIN左连接查询:返回左表中的所有行,以及右表中匹配的行(如果有的话)
SELECT *
FROM users
LEFT JOIN orders ON users.id = orders.user_id;
-- RIGHT JOIN右连接查询:返回右表中的所有行,以及左表中匹配的行(如果有的话)
SELECT *
FROM orders
RIGHT JOIN users ON orders.user_id = users.id;
-- 全外连接查询:返回左右两个表中的所有行,并将不匹配的行填充为 NULL
SELECT *
FROM users
LEFT JOIN orders ON users.id = orders.user_id
UNION ALL
SELECT *
FROM users
RIGHT JOIN orders ON users.id = orders.user_id
WHERE users.id IS NULL OR orders.id IS NULL;
-- 全外连接查询(另一种形式):返回左右两个表中的所有行,并将不匹配的行填充为 NULL
SELECT *
FROM users
LEFT OUTER JOIN orders ON users.id = orders.user_id
UNION ALL
SELECT *
FROM users
RIGHT OUTER JOIN orders ON users.id = orders.user_id
WHERE users.id IS NULL OR orders.id IS NULL;
窗口函数
-- 窗口函数:排名
SELECT
username,
email,
RANK() OVER (ORDER BY created_at) AS rank
FROM
users;
-- 窗口函数:移动平均
SELECT
product_id,
order_date,
price,
AVG(price) OVER (PARTITION BY product_id ORDER BY order_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM
order_items;
-- 窗口函数:累计和
SELECT
product_id,
order_date,
price,
SUM(price) OVER (PARTITION BY product_id ORDER BY order_date) AS cumulative_sum
FROM
order_items;
算术表达式
-- 示例1:查询产品价格加上10的结果
SELECT *, price + 10 AS new_price FROM products;
-- 示例2:查询用户年龄减去5的结果
SELECT *, age - 5 AS modified_age FROM users;
-- 示例3:查询订单数量乘以2的结果
SELECT *, quantity * 2 AS doubled_quantity FROM orders;
-- 示例4:查询产品价格除以2的结果
SELECT *, price / 2 AS half_price FROM products;
-- 示例5:查询订单数量取余3的结果
SELECT *, quantity % 3 AS remainder FROM orders;
比较运算符
-- 查询用户年龄大于等于18岁的用户:
SELECT * FROM users WHERE age >= 18;
-- 查询产品价格大于50的产品:
SELECT * FROM products WHERE price > 50;
-- 查询订单数量小于等于10的订单:
SELECT * FROM orders WHERE quantity <= 10;
-- 查询订单数量小于10的订单:
SELECT * FROM orders WHERE quantity < 10;
-- 查询产品名称不等于'Apple'的产品:
SELECT * FROM products WHERE name <> 'Apple';
-- 查询产品名称不等于'Apple'的产品:
SELECT * FROM products WHERE name != 'Apple';
####通用关键字
如AS, BY, DESC, ASC, DISTINCT
-- 使用 AS 关键字给查询结果列起别名
SELECT username AS user_name, email AS user_email FROM users;
-- 使用 BY 关键字指定排序字段
SELECT * FROM products ORDER BY price DESC;
-- 使用 DESC 关键字按降序排序
SELECT * FROM users ORDER BY age DESC;
-- 使用 ASC 关键字按升序排序(默认)
SELECT * FROM orders ORDER BY quantity ASC;
-- 使用 DISTINCT 关键字查询不重复的产品名称
SELECT DISTINCT name FROM products;
数值表达式
-- 插入测试数据到 users 表
INSERT INTO users (username, password, email) VALUES
('alice', 'password1', 'alice@example.com'),
('bob', 'password2', 'bob@example.com');
-- 插入测试数据到 products 表
INSERT INTO products (name, description, price, stock) VALUES
('Laptop', 'Gaming Laptop', 1500.00, 10),
('Phone', 'Smartphone', 800.00, 20);
-- 插入测试数据到 orders 表
INSERT INTO orders (user_id, total) VALUES
(1, 2300.00),
(2, 800.00);
-- 插入测试数据到 order_items 表
INSERT INTO order_items (order_id, product_id, quantity, price) VALUES
(1, 1, 1, 1500.00),
(1, 2, 1, 800.00),
(2, 2, 1, 800.00);
-- 测试基本算术运算
-- 加法
SELECT 2 + 3 AS addition;
-- 减法
SELECT 5 - 3 AS subtraction;
-- 乘法
SELECT 4 * 2 AS multiplication;
-- 除法
SELECT 10 / 2 AS division;
-- 取余
SELECT 10 % 3 AS modulus;
-- 复杂表达式
SELECT (10 + 5) * 2 / (3 - 1) AS complex_expression;
-- 测试数值函数
-- 绝对值
SELECT ABS(-5) AS absolute_value;
-- 四舍五入
SELECT ROUND(3.14159, 2) AS rounded_value;
-- 幂运算
SELECT POWER(2, 3) AS power_value;
-- 开平方
SELECT SQRT(16) AS square_root;
-- 随机数
SELECT RANDOM() % 100 AS random_value;
-- 余数
SELECT 10 % 4 AS modulus;
-- 最大值和最小值
SELECT MAX(price) AS max_price, MIN(price) AS min_price FROM products;
-- 平均值
SELECT AVG(price) AS average_price FROM products;
-- 合计
SELECT SUM(price) AS total_price FROM products;
-- 测试数值表达式在查询中的应用
-- 计算订单中每个产品的总价
SELECT
order_id,
product_id,
quantity,
price,
quantity * price AS total_item_price
FROM
order_items;
-- 为用户计算总消费
SELECT
users.username,
SUM(orders.total) AS total_spent
FROM
users
JOIN
orders ON users.id = orders.user_id
GROUP BY
users.username;
-- 在查询中使用复杂表达式
-- 计算库存总值
SELECT
name,
price,
stock,
price * stock AS total_stock_value
FROM
products;
逻辑运算符
-- 使用 IN 运算符查询属于指定用户角色的用户
SELECT * FROM users WHERE role IN ('admin', 'editor');
-- 使用 OR 运算符查询属于指定用户角色或拥有特定电子邮件地址的用户
SELECT * FROM users WHERE role = 'admin' OR email = 'example@example.com';
-- 使用 AND 运算符查询属于指定用户角色且年龄大于等于18岁的用户
SELECT * FROM users WHERE role = 'admin' AND age >= 18;
-- 使用 NOT 运算符查询不属于指定用户角色的用户
SELECT * FROM users WHERE NOT role = 'admin';
-- 使用 BETWEEN 运算符查询订单数量在10到20之间的订单
SELECT * FROM orders WHERE quantity BETWEEN 10 AND 20;
-- 使用 EXISTS 关键字检查是否存在与用户ID相关联的订单
SELECT * FROM users WHERE EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id);
模糊查询
-- 示例1:查询用户名以字母'A'开头的用户
SELECT * FROM users WHERE username LIKE 'A%';
-- 示例2:查询用户名包含字母'n'的用户
SELECT * FROM users WHERE username LIKE '%n%';
-- 示例3:查询邮箱地址以'@example.com'结尾的用户
SELECT * FROM users WHERE email LIKE '%@example.com';
-- 示例4:查询产品名称以字母'B'开头且长度为5的产品
SELECT * FROM products WHERE name LIKE 'B____';
-- 示例5:查询用户名不以字母'A'开头的用户
SELECT * FROM users WHERE username NOT LIKE 'A%';
Glob 子句
-- 示例1:查询用户名以字母 'J' 开头的用户
SELECT * FROM users WHERE username GLOB 'J*';
-- 示例2:查询用户名包含字母 'a' 或 'e' 的用户
SELECT * FROM users WHERE username GLOB '*[ae]*';
-- 示例3:查询邮箱地址以 'gmail.com' 结尾的用户
SELECT * FROM users WHERE email GLOB '*@gmail.com';
-- 示例4:查询产品名称以字母 'S' 开头,且长度为 6 的产品
SELECT * FROM products WHERE name GLOB 'S?????';
-- 示例5:查询用户名不以字母 'A' 开头的用户
SELECT * FROM users WHERE NOT username GLOB 'A*';
位运算符
-- 使用位或运算符(|)查询用户角色为 'admin' 或 'editor' 的用户
SELECT * FROM users WHERE role = ('admin' | 'editor');
-- 使用位与运算符(&)查询用户角色为 'admin' 且年龄大于等于 30 岁的用户
SELECT * FROM users WHERE role = 'admin' & age >= 30;
-- 使用左移运算符(<<)查询订单数量扩大两倍的订单
SELECT * FROM orders WHERE quantity << 1;
-- 使用右移运算符(>>)查询订单数量缩小一半的订单
SELECT * FROM orders WHERE quantity >> 1;
-- 使用位取反运算符(~)查询用户年龄取反后的用户
SELECT * FROM users WHERE age = ~age;
-- 使用位运算函数进行位操作
SELECT
username,
permissions,
permissions & 1 AS can_read,
permissions & 2 AS can_write,
permissions & 4 AS can_delete
FROM
users;
LOWER
和UPPER
-- 将用户名转换为小写并选择匹配的用户
SELECT * FROM users WHERE LOWER(username) = 'john_doe';
-- 将产品名称转换为大写并按名称排序
SELECT * FROM products ORDER BY UPPER(name);
-- 将订单总额转换为大写并选择大于特定值的订单
SELECT * FROM orders WHERE UPPER(total) > 1000.00;
-- 将用户名和邮箱地址都转换为小写并插入新用户
INSERT INTO users (username, password, email) VALUES (LOWER('New_User'), 'newpassword', LOWER('new_user@example.com'));
-- 将产品名称转换为大写并更新产品描述
UPDATE products SET description = 'Updated description' WHERE UPPER(name) = 'LAPTOP';
-- 将订单总额转换为小写并计算总金额
SELECT SUM(LOWER(total)) FROM orders;
USING
-- 在users表和orders表之间进行内连接,使用user_id列进行连接
SELECT * FROM users INNER JOIN orders USING (user_id);
-- 在orders表和order_items表之间进行内连接,使用id列进行连接
SELECT * FROM orders INNER JOIN order_items USING (id);
-- 在products表和order_items表之间进行内连接,使用product_id列进行连接
SELECT * FROM products INNER JOIN order_items USING (product_id);
-- 在users表和orders表之间进行左连接,使用user_id列进行连接
SELECT * FROM users LEFT JOIN orders USING (user_id);
-- 在orders表和order_items表之间进行左连接,使用id列进行连接
SELECT * FROM orders LEFT JOIN order_items USING (id);
-- 在products表和order_items表之间进行左连接,使用product_id列进行连接
SELECT * FROM products LEFT JOIN order_items USING (product_id);
-- 在users表和orders表之间进行右连接,使用user_id列进行连接
SELECT * FROM users RIGHT JOIN orders USING (user_id);
-- 在orders表和order_items表之间进行右连接,使用id列进行连接
SELECT * FROM orders RIGHT JOIN order_items USING (id);
-- 在products表和order_items表之间进行右连接,使用product_id列进行连接
SELECT * FROM products RIGHT JOIN order_items USING (product_id);
CASE
语句
-- 使用CASE语句进行条件查询
SELECT
username,
email,
CASE
WHEN email LIKE '%@example.com' THEN 'Internal'
ELSE 'External'
END AS email_type
FROM
users;
-- 使用CASE语句进行分组和聚合
SELECT
dept_id,
SUM(CASE WHEN salary > 50000 THEN 1 ELSE 0 END) AS high_salary_count,
SUM(CASE WHEN salary <= 50000 THEN 1 ELSE 0 END) AS low_salary_count
FROM
employees
GROUP BY
dept_id;
Common Table Expressions
(CTEs)
-- 简单CTE
WITH EmployeeSales AS (
SELECT
e.id,
e.name,
SUM(o.total) AS total_sales
FROM
employees AS e
JOIN
orders AS o ON e.id = o.user_id
GROUP BY
e.id
)
SELECT * FROM EmployeeSales;
-- 递归CTE
WITH RECURSIVE Numbers AS (
SELECT 1 AS number
UNION ALL
SELECT number + 1 FROM Numbers WHERE number < 10
)
SELECT number FROM Numbers;
带LIMIT
和OFFSET
的查询
-- 查询前5条用户记录
SELECT * FROM users LIMIT 5;
-- 查询第6到第10条用户记录
SELECT * FROM users LIMIT 5 OFFSET 5;
-- 假设已经创建并填充了以下表: users, products, orders, order_items
-- 插入一些测试数据
INSERT INTO users (username, password, email) VALUES
('alice', 'password123', 'alice@example.com'),
('bob', 'password123', 'bob@example.com'),
('charlie', 'password123', 'charlie@example.com'),
('dave', 'password123', 'dave@example.com'),
('eve', 'password123', 'eve@example.com');
INSERT INTO products (name, description, price, stock) VALUES
('Laptop', 'A high-end gaming laptop', 1500.00, 10),
('Smartphone', 'Latest model smartphone', 800.00, 20),
('Tablet', '10-inch tablet', 300.00, 15),
('Headphones', 'Noise-cancelling headphones', 150.00, 30),
('Monitor', '4K UHD monitor', 400.00, 5);
-- 基础的LIMIT测试
-- 选择前两个用户
SELECT * FROM users LIMIT 2;
-- 带OFFSET的LIMIT测试
-- 跳过前两个用户,选择接下来的两个用户
SELECT * FROM users LIMIT 2 OFFSET 2;
-- 在products表上进行LIMIT和OFFSET测试
-- 选择前两个产品
SELECT * FROM products LIMIT 2;
-- 跳过前两个产品,选择接下来的三个产品
SELECT * FROM products LIMIT 3 OFFSET 2;
-- 综合查询测试,包括联接、排序、LIMIT和OFFSET
-- 获取订单和用户信息,按订单时间排序,选择前三个订单
SELECT
orders.id AS order_id,
users.username,
orders.total,
orders.created_at
FROM
orders
JOIN
users ON orders.user_id = users.id
ORDER BY
orders.created_at DESC
LIMIT 3;
-- 获取订单和用户信息,按订单时间排序,跳过第一个订单,选择接下来的两个订单
SELECT
orders.id AS order_id,
users.username,
orders.total,
orders.created_at
FROM
orders
JOIN
users ON orders.user_id = users.id
ORDER BY
orders.created_at DESC
LIMIT 2 OFFSET 1;
-- 在复杂联接查询中使用LIMIT和OFFSET
-- 获取用户、订单及订单项目的详细信息,按订单总额排序,选择前两个订单
SELECT
orders.id AS order_id,
users.username,
products.name AS product_name,
order_items.quantity,
order_items.price
FROM
orders
JOIN
users ON orders.user_id = users.id
JOIN
order_items ON orders.id = order_items.order_id
JOIN
products ON order_items.product_id = products.id
ORDER BY
orders.total DESC
LIMIT 2;
-- 在复杂联接查询中使用LIMIT和OFFSET
-- 获取用户、订单及订单项目的详细信息,按订单总额排序,跳过第一个订单,选择接下来的两个订单
SELECT
orders.id AS order_id,
users.username,
products.name AS product_name,
order_items.quantity,
order_items.price
FROM
orders
JOIN
users ON orders.user_id = users.id
JOIN
order_items ON orders.id = order_items.order_id
JOIN
products ON order_items.product_id = products.id
ORDER BY
orders.total DESC
LIMIT 2 OFFSET 1;
ROLLUP
进行递归聚合
-- 使用ROLLUP进行递归聚合
SELECT
dept_id,
user_id,
COUNT(*) AS order_count
FROM
orders
GROUP BY
ROLLUP (dept_id, user_id);
CUBE
进行交叉聚合
-- 使用CUBE进行交叉聚合
SELECT
dept_id,
user_id,
COUNT(*) AS order_count
FROM
orders
GROUP BY
CUBE (dept_id, user_id);
Pivot
表达转置操作
-- 使用Pivot进行转置
SELECT * FROM (
SELECT user_id, product_id, quantity FROM order_items
)
PIVOT (
SUM(quantity) AS total_quantity
FOR product_id IN ([1], [2], [3])
);
#### Unpivot
进行逆转置操作
-- 使用Unpivot进行逆转置
SELECT user_id, product_id, total_quantity
FROM (
SELECT * FROM order_items
) AS SourceTable
UNPIVOT (
total_quantity FOR product_id IN ([1], [2], [3])
) AS UnpivotTable;
-- 使用UNPIVOT函数进行逆转置操作
SELECT
user_id,
MAX(CASE WHEN product_id = 1 THEN quantity ELSE NULL END) AS product_1_quantity,
MAX(CASE WHEN product_id = 2 THEN quantity ELSE NULL END) AS product_2_quantity,
MAX(CASE WHEN product_id = 3 THEN quantity ELSE NULL END) AS product_3_quantity
FROM
order_items
GROUP BY
user_id;
全文搜索进行复杂查询
-- 使用全文搜索进行复杂查询
SELECT * FROM articles WHERE MATCH(title, content) AGAINST('database' IN BOOLEAN MODE);
WITH LOCKED
语句进行行级锁定
-- 使用WITH LOCKED进行行级锁定
SELECT * FROM users WHERE id = 1 WITH LOCKED;
EXISTS
和NOT EXISTS
-- 使用EXISTS进行相关子查询
SELECT username FROM users WHERE EXISTS (SELECT * FROM orders WHERE orders.user_id = users.id);
-- 使用NOT EXISTS进行相关子查询
SELECT username FROM users WHERE NOT EXISTS (SELECT * FROM orders WHERE orders.user_id = users.id);
ANY
和ALL
-- 使用ANY子查询进行条件判断
SELECT username FROM users WHERE id = ANY (SELECT user_id FROM orders WHERE total > 1000);
-- 使用ALL子查询进行条件判断
SELECT username FROM users WHERE id = ALL (SELECT user_id FROM orders WHERE total > 1000);
自连接进行复杂的关联查询
-- 使用自连接进行复杂的关联查询
SELECT
e1.name AS employee_name,
e2.name AS manager_name
FROM
employees AS e1
JOIN
employees AS e2 ON e1.manager_id = e2.id;
####GROUP_CONCAT
进行字符串聚合
-- 使用GROUP_CONCAT进行字符串聚合
SELECT
dept_id,
GROUP_CONCAT(username) AS employee_list
FROM
employees
GROUP BY
dept_id;
XMLAGG
进行XML数据聚合
-- 使用XMLAGG进行XML数据聚合
SELECT
dept_id,
XMLAGG(XMLELEMENT(NAME "employee", username)) AS employee_list
FROM
employees
GROUP BY
dept_id;
FOR UPDATE
进行行级锁定
-- 使用FOR UPDATE进行行级锁定
SELECT * FROM users WHERE id = 1 FOR UPDATE;
-- 使用RETURNING子句返回受影响的行
INSERT INTO users (username, email) VALUES ('test_user', 'test@example.com') RETURNING *;
自然连接
-- 使用自然连接进行表连接
SELECT
orders.id,
users.username,
products.name
FROM
orders
NATURAL JOIN
users
NATURAL JOIN
order_items
NATURAL JOIN
products;
CASE
表达式
-- 使用CASE表达式进行条件分支
SELECT
username,
CASE
WHEN salary >= 50000 THEN 'High'
ELSE 'Low'
END AS salary_level
FROM
employees;
-- 使用CAST函数进行数据类型转换
SELECT
username,
CAST(age AS TEXT) AS age_text
FROM
users;
COALESCE
函数处理空值
-- 使用COALESCE函数处理空值
SELECT
username,
COALESCE(email, 'No email') AS email_address
FROM
users;
GREATEST
和LEAST
函数查找最大和最小值
-- 使用GREATEST和LEAST函数查找最大和最小值
SELECT
GREATEST(salary1, salary2, salary3) AS max_salary,
LEAST(salary1, salary2, salary3) AS min_salary
FROM
salaries;
HAVING
-- 使用HAVING子句进行分组后的过滤
SELECT
dept_id,
COUNT(*) AS employee_count
FROM
employees
GROUP BY
dept_id
HAVING
COUNT(*) > 5;
ROW_NUMBER
函数进行分组排序
-- 使用ROW_NUMBER函数进行分组排序
SELECT
username,
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rank
FROM
employees;
RANK
和DENSE_RANK
函数进行排名
-- 使用RANK和DENSE_RANK函数进行排名
SELECT
username,
RANK() OVER (ORDER BY salary DESC) AS rank,
DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank
FROM
employees;
NTILE
函数进行分组划分
-- 使用NTILE函数进行分组划分
SELECT
username,
NTILE(4) OVER (ORDER BY salary DESC) AS quartile
FROM
employees;
GROUPING
函数判断是否进行了分组
-- 使用GROUPING函数判断是否进行了分组
SELECT
dept_id,
GROUPING(dept_id) AS is_grouped,
COUNT(*) AS employee_count
FROM
employees
GROUP BY
dept_id
WITH ROLLUP;
GROUPING SETS
进行多重分组
-- 使用GROUPING SETS进行多重分组
SELECT
dept_id,
user_id,
COUNT(*) AS order_count
FROM
orders
GROUP BY
GROUPING SETS ((dept_id), (user_id), (dept_id, user_id));
JSON_EXTRACT
函数从JSON数据中提取信息
-- 使用JSON_EXTRACT函数从JSON数据中提取信息
SELECT
JSON_EXTRACT(details, '$.name') AS product_name,
JSON_EXTRACT(details, '$.price') AS product_price
FROM
products;
JSON_ARRAYAGG
进行JSON数据聚合
-- 使用JSON_ARRAYAGG进行JSON数据聚合
SELECT
dept_id,
JSON_ARRAYAGG(username) AS employee_list
FROM
employees
GROUP BY
dept_id;
JSON_VALUE
函数从JSON数据中提取信息
-- 使用JSON_VALUE函数从JSON数据中提取信息
SELECT
JSON_VALUE(details, '$.name') AS product_name,
JSON_VALUE(details, '$.price') AS product_price
FROM
products;
XML
函数从XML数据中提取信息
-- 使用XML函数从XML数据中提取信息
SELECT
XMLQUERY('$.name' PASSING details RETURNING CONTENT) AS product_name,
XMLQUERY('$.price' PASSING details RETURNING CONTENT) AS product_price
FROM
products;
XMLQUERY
函数从XML数据中提取信息
-- 使用XMLQUERY函数从XML数据中提取信息
SELECT
XMLQUERY('$.name' PASSING details RETURNING CONTENT) AS product_name,
XMLQUERY('$.price' PASSING details RETURNING CONTENT) AS product_price
FROM
products;
PI
函数获取圆周率值
-- 使用PI函数获取圆周率值
SELECT PI();
RAND
函数生成随机数
-- 使用RAND函数生成随机数
SELECT
username,
RAND() AS random_number
FROM
users;
UUID
函数生成标识符
-- 使用UUID函数生成唯一标识符
SELECT
username,
UUID() AS unique_id
FROM
users;
CONCAT
函数进行字符串连接
-- 使用CONCAT函数进行字符串连接
SELECT
CONCAT(first_name, ' ', last_name) AS full_name
FROM
employees;
EXTRACT
函数提取日期时间信息
-- 使用EXTRACT函数提取日期时间信息
SELECT
EXTRACT(YEAR FROM order_date) AS order_year,
EXTRACT(MONTH FROM order_date) AS order_month,
EXTRACT(DAY FROM order_date) AS order_day
FROM
orders;
####STRTIME
函数格式化日期时间字符串
-- 使用STRTIME函数格式化日期时间字符串
SELECT
username,
STRFTIME('%Y-%m-%d', created_at) AS formatted_created_at
FROM
users;
CURRENT_TIMESTAMP
获取当前时间戳
-- 使用CURRENT_TIMESTAMP获取当前时间戳
SELECT
username,
last_login,
CURRENT_TIMESTAMP AS current_timestamp
FROM
users;
NULLIF
函数处理特定值
-- 使用NULLIF函数处理特定值
SELECT
username,
NULLIF(email, 'example@example.com') AS email_address
FROM
users;
####IFNULL处理空值
-- 测试 IFNULL 函数是否正确处理空值
-- 插入一些测试数据
INSERT INTO users (username, password, email) VALUES ('user1', NULL, 'user1@example.com');
INSERT INTO users (username, password, email) VALUES ('user2', 'password2', NULL);
INSERT INTO users (username, password, email) VALUES ('user3', 'password3', 'user3@example.com');
-- 测试 IFNULL 函数对于空密码的情况
SELECT username, IFNULL(password, 'No password set') AS password FROM users;
-- 测试 IFNULL 函数对于空邮箱的情况
SELECT username, IFNULL(email, 'No email provided') AS email FROM users;
-- 测试 IFNULL 函数对于非空值的情况
SELECT username, IFNULL(email, 'No email provided') AS email FROM users WHERE username = 'user3';
-- 测试 IFNULL 函数与其他函数一起使用
SELECT username, IFNULL(UPPER(email), 'No email provided') AS email FROM users;
-- 清理测试数据
DELETE FROM users;
BIT_AND
和BIT_OR
进行位运算
-- 使用BIT_AND和BIT_OR进行位运算
SELECT
username,
BIT_AND(permissions) AS combined_permissions,
BIT_OR(permissions) AS combined_permissions
FROM
users;
HASH
函数进行哈希计算
-- 使用HASH函数进行哈希计算
SELECT
username,
HASH(password, 'sha256') AS hashed_password
FROM
users;
GENERATE_SERIES
生成序列
-- 使用GENERATE_SERIES生成序列
SELECT
n
FROM
GENERATE_SERIES(1, 10) AS n;
####RECURSIVE
递归查询
-- 使用RECURSIVE递归查询
WITH RECURSIVE DepartmentTree AS (
SELECT id, name, parent_id FROM departments WHERE id = 1
UNION ALL
SELECT d.id, d.name, d.parent_id FROM departments d JOIN DepartmentTree dt ON d.parent_id = dt.id
)
SELECT * FROM DepartmentTree;
2.6 sqlite原生态支持语法
ANALYZE
-- 创建索引以便于测试
CREATE INDEX idx_user_username ON users (username);
CREATE INDEX idx_product_name ON products (name);
-- 分析表以生成统计信息
ANALYZE;
-- 以下是一些查询,我们将观察它们是否利用了索引和统计信息
-- 选择查询,期望利用 username 索引
EXPLAIN QUERY PLAN SELECT * FROM users WHERE username = 'john_doe';
-- 范围查询,期望利用 price 索引
EXPLAIN QUERY PLAN SELECT * FROM products WHERE price BETWEEN 1000 AND 2000;
-- 多表连接查询,期望使用适当的索引和连接策略
EXPLAIN QUERY PLAN SELECT * FROM orders
JOIN users ON orders.user_id = users.id
JOIN order_items ON orders.id = order_items.order_id
JOIN products ON order_items.product_id = products.id;
-- 分组查询,期望利用合适的索引和聚合函数
EXPLAIN QUERY PLAN SELECT user_id, COUNT(*) AS order_count, SUM(total) AS total_spent FROM orders GROUP BY user_id;
ATTACH DATABASE
-- 尝试附加数据库
ATTACH DATABASE 'test_db.db' AS test_db;
-- 检查是否成功附加数据库
SELECT name FROM sqlite_master WHERE type='table' AND name='users' UNION ALL SELECT name FROM test_db.sqlite_master WHERE type='table';
Transaction
-- 开始一个事务
BEGIN TRANSACTION;
-- 在事务中执行插入操作
INSERT INTO users (username, password, email) VALUES ('test_user', 'test_password', 'test@example.com');
-- 在事务中执行更新操作
UPDATE users SET email = 'new_test@example.com' WHERE username = 'test_user';
-- 在事务中执行删除操作
DELETE FROM users WHERE username = 'test_user';
-- 提交事务
COMMIT;
-- 检查是否成功提交事务,检查用户表是否有'test_user',如果有,则回滚事务
BEGIN TRANSACTION;
SELECT * FROM users WHERE username = 'test_user';
-- 如果用户存在,则回滚事务
ROLLBACK;
Comment
注释不是 SQL 命令,但可以出现在 传递给 sqlite3_prepare_v2() 和相关接口的 SQL 查询。 注释被解析器视为空格。 注释可以从任何空白处开始 可以找到,包括跨多行的内部表达式。
SQL 注释以两个连续的“–”字符 (ASCII 0x2d) 开头 并扩展至并包括下一个换行符 (ASCII 0x0a) 或直到输入结束,以先到者为准。
C 样式注释开始 替换为“/”,并扩展至并包括下一个“/”字符对 或直到输入结束,以先到者为准。C 型注释 可以跨越多条线。
注释可以出现在任何可能出现空格的地方, 包括内部表达式和其他 SQL 语句的中间。 注释不嵌套。
-- 测试注释
Core Functions
官网链接:Built-In Scalar SQL Functions (sqlite.org)
abs(X):返回数字参数 X 的绝对值。如果 X 是 NULL,则 abs(X) 返回 NULL。如果 X 是无法转换为数字值的字符串或二进制大对象(blob),则 abs(X) 返回 0.0。如果 X 是整数 -9223372036854775808,则 abs(X) 抛出整数溢出错误,因为不存在等效的正 64 位二进制补码值。
changes():返回最近一次完成的 INSERT、DELETE 或 UPDATE 语句更改、插入或删除的数据库行数,不包括低级触发器中的语句。changes() SQL 函数是 sqlite3_changes64() C/C++ 函数的包装器,因此遵循相同的更改计数规则。
char(X1,X2,…,XN):返回由整数 X1 到 XN 的 Unicode 代码点值组成的字符串。
coalesce(X,Y,…):返回其第一个非 NULL 参数的副本,如果所有参数都为 NULL,则返回 NULL。Coalesce() 必须至少有 2 个参数。
concat(X,…):返回一个字符串,该字符串是所有非 NULL 参数的字符串表示的串联。如果所有参数都为 NULL,则 concat() 返回一个空字符串。
concat_ws(SEP,X,…):使用第一个参数的文本值作为分隔符,返回所有非 NULL 参数的串联字符串。如果第一个参数为 NULL,则 concat_ws() 返回 NULL。如果除第一个参数外的所有参数都为 NULL,则 concat_ws() 返回一个空字符串。
format(FORMAT,…):类似于 sqlite3_mprintf() C 语言函数和标准 C 库中的 printf() 函数。第一个参数是一个格式字符串,指定如何使用后续参数的值构造输出字符串。如果 FORMAT 参数缺失或为 NULL,则结果为 NULL。%n 格式被忽略,不会消耗参数。%p 格式是 %X 的别名。%z 格式可与 %s 互换。如果参数列表中的参数不足,则假定缺少的参数具有 NULL 值,对于数值格式为 0 或 0.0,对于 %s 为空字符串。
glob(X,Y):等同于表达式 “Y GLOB X”。请注意,在 glob() 函数中,X 和 Y 参数相对于中缀 GLOB 运算符是颠倒的。Y 是字符串,X 是模式。因此,例如,以下表达式是等价的:
name GLOB '*helium*' glob('*helium*',name)
如果使用 sqlite3_create_function() 接口重写 glob(X,Y) 函数以使用另一种实现,则 GLOB 运算符将调用替代实现。
- hex(X):将参数 X 解释为 BLOB,并返回一个字符串,该字符串是该 BLOB 内容的大写十六进制表示。
如果在 “hex(X)” 中的参数 X 是整数或浮点数,则"将参数 X 解释为 BLOB" 意味着首先将二进制数字转换为 UTF8 文本表示,然后将该文本解释为 BLOB。因此,“hex(12345678)” 渲染为 “3132333435363738” 而不是整数值 “0000000000BC614E” 的二进制表示。
参见:unhex()
- ifnull(X,Y):返回其第一个非 NULL 参数的副本,如果两个参数都为 NULL,则返回 NULL。Ifnull() 必须恰好有 2 个参数。ifnull() 函数等同于具有两个参数的 coalesce() 函数。
- iif(X,Y,Z):如果 X 为真,则返回值 Y,否则返回值 Z。iif(X,Y,Z) 函数在逻辑上等效于并生成与 CASE 表达式 “CASE WHEN X THEN Y ELSE Z END” 相同的字节码。
- instr(X,Y):在字符串 X 中查找字符串 Y 的第一个出现,并返回先前字符的数量加 1,如果 Y 在 X 中没有找到,则返回 0。或者,如果 X 和 Y 都是 BLOB,则 instr(X,Y) 返回 Y 第一次出现之前的字节数加 1,如果 Y 在 X 中没有出现,则返回 0。如果 instr(X,Y) 的两个参数 X 和 Y 都是非 NULL,并且不是 BLOB,则都将解释为字符串。如果 instr(X,Y) 中的 X 或 Y 为空,则结果为 NULL。
- last_insert_rowid():返回从调用该函数的数据库连接插入的最后一行的 ROWID。last_insert_rowid() SQL 函数是 sqlite3_last_insert_rowid() C/C++ 接口函数的包装器。
- length(X):对于字符串值 X,length(X) 函数返回在第一个 NUL 字符之前的字符数(而不是字节)。由于 SQLite 字符串通常不包含 NUL 字符,因此 length(X) 函数通常会返回字符串 X 中的总字符数。对于 BLOB 值 X,length(X) 返回 BLOB 中的字节数。如果 X 为 NULL,则 length(X) 为 NULL。如果 X 是数值,则 length(X) 返回 X 的字符串表示的长度。
注意,对于字符串,length(X) 函数返回字符串的字符长度,而不是字节长度。字符长度是字符串中的字符数。对于 UTF-16 字符串,字符长度始终与字节长度不同,并且对于 UTF-8 字符串,如果字符串包含多字节字符,则字符长度可以与字节长度不同。使用 octet_length() 函数查找字符串的字节长度。
对于 BLOB 值,length(X) 始终返回 BLOB 的字节长度。
对于字符串值,length(X) 必须读取整个字符串到内存中才能计算字符长度。但是对于 BLOB 值,这是不必要的,因为 SQLite 知道 BLOB 中有多少字节。因此,对于大小为数兆字节的值,length(X) 函数对于 BLOB 而言通常比对于字符串更快,因为它不需要加载值到内存中。
like(X,Y)、like(X,Y,Z):用于实现 “Y LIKE X [ESCAPE Z]” 表达式。如果存在可选的 ESCAPE 子句,则使用 like() 函数调用三个参数。否则,仅使用两个参数调用它。请注意,相对于中缀 LIKE 运算符,like() 函数中的 X 和 Y 参数是颠倒的。X 是模式,Y 是要匹配该模式的字符串。因此,以下表达式是等价的:
name LIKE '%neon%' like('%neon%',name)
如果使用 sqlite3_create_function() 接口覆盖 like() 函数以更改 LIKE 运算符的操作,则重写 like() 函数时,重要的是同时重写两个参数版本的 like() 函数。否则,根据是否指定了 ESCAPE 子句,可能会调用不同的代码来实现 LIKE 运算符。
- likelihood(X,Y):likelihood(X,Y) 函数返回参数 X。likelihood(X,Y) 中的值 Y 必须是介于 0.0 和 1.0 之间的浮点常量。likelihood(X) 函数是一个无操作函数,代码生成器会优化它,以便在运行时(即在调用 sqlite3_step() 时)不消耗 CPU 循环。likelihood(X,Y) 函数的目的是向查询规划器提供提示,即参数 X 是一个布尔值,其大约有 Y 的概率为真。unlikely(X) 函数是 likelihood(X,0.0625) 的简写形式。likely(X) 函数是 likelihood(X,0.9375) 的简写形式。
- load_extension(X):load_extension(X,Y) 函数从名为 X 的共享库文件中使用入口点 Y 加载 SQLite 扩展。load_extension() 的结果始终是 NULL。如果省略了 Y,则使用默认的入口点名称。如果扩展加载或初始化失败,则 load_extension() 函数会引发异常。
load_extension() 函数将在扩展尝试修改或删除 SQL 函数或排序序列时失败。扩展可以添加新的函数或排序序列,但不能修改或删除现有的函数或排序序列,因为这些函数和/或排序序列可能在当前正在运行的 SQL 语句中的其他地方使用。要加载更改或删除函数或排序序列的扩展,请使用 sqlite3_load_extension() C 语言 API。
出于安全原因,默认情况下禁用扩展加载,并且必须通过先调用 sqlite3_enable_load_extension() 来启用它。
- lower(X):返回字符串 X 的副本,其中所有 ASCII 字符转换为小写。默认的内置 lower() 函数仅适用于 ASCII 字符。要对非 ASCII 字符执行大小写转换,请加载 ICU 扩展。
- ltrim(X):返回一个字符串,该字符串由从 X 的左侧删除在 Y 中出现的任何字符组成。如果省略了 Y 参数,则 ltrim(X) 从 X 的左侧删除空格。
- max(X,Y,…):多参数 max() 函数返回具有最大值的参数,如果任何参数为 NULL,则返回 NULL。多参数 max() 函数从左到右搜索其参数以查找定义排序函数的参数,并使用该排序函数进行所有字符串比较。如果 max() 的参数没有一个定义排序函数,则使用 BINARY 排序函数。请注意,当参数只有 2 个或更多时,max() 是一个简单函数,但如果只给出一个参数,则作为聚合函数运行。
- min(X,Y,…):多参数 min() 函数返回具有最小值的参数。多参数 min() 函数从左到右搜索其参数以查找定义排序函数的参数,并使用该排序函数进行所有字符串比较。如果 min() 的参数没有一个定义排序函数,则使用 BINARY 排序函数。请注意,当参数只有 2 个或更多时,min() 是一个简单函数,但如果只给出一个参数,则作为聚合函数运行。
- nullif(X,Y):如果参数不同,则 nullif(X,Y) 函数返回其第一个参数,如果参数相同则返回 NULL。nullif(X,Y) 函数从左到右搜索其参数以查找定义排序函数,并使用该排序函数进行所有字符串比较。如果 nullif() 的两个参数都没有定义排序函数,则使用 BINARY 排序函数。
- octet_length(X):返回文本字符串 X 的编码中的字节数。如果 X 为 NULL,则 octet_length(X) 返回 NULL。如果 X 是 BLOB 值,则 octet_length(X) 与 length(X) 相同。如果 X 是数值,则 octet_length(X) 返回该数字的文本表示中的字节数。
- printf(FORMAT,…):printf() SQL 函数是 format() SQL 函数的别名。最初,format() SQL 函数命名为 printf()。但是后来将名称更改为 format(),以与其他数据库引擎兼容。保留 printf() 名称作为别名,以免破坏旧代码。
- quote(X):quote(X) 函数返回 SQL 字面值的文本,即其参数的值适合包含在 SQL 语句中。字符串使用单引号括起来,并根据需要在内部引号中进行转义。BLOB 以十六进制文本形式编码。包含嵌入的 NUL 字符的字符串不能表示为 SQL 中的字符串文字,因此在第一个 NUL 之前返回的字符串文字被截断。
- random():random() 函数返回介于 -9223372036854775808 和 +9223372036854775807 之间的伪随机整数。
- randomblob(N):randomblob(N) 函数返回包含 N 个字节的伪随机字节的 BLOB。如果 N 小于 1,则返回一个字节的随机 blob。
提示:应用程序可以使用此函数与 hex() 和/或 lower() 生成全局唯一标识符,如下所示:
scss复制代码hex(randomblob(16)) lower(hex(randomblob(16)))
replace(X,Y,Z):replace(X,Y,Z) 函数返回一个字符串,其中将字符串 X 中的每个字符串 Y 替换为字符串 Z。用于比较的是 BINARY 排序序列。如果 Y 是空字符串,则返回 X。如果 Z 最初不是字符串,则在处理之前将其转换为 UTF-8 字符串。
round(X,Y):round(X,Y) 函数将浮点值 X 四舍五入到小数点右侧的 Y 位。如果省略或为负的 Y 参数,则将其视为 0。
rtrim(X,Y):rtrim(X,Y) 函数返回一个字符串,该字符串由从 X 的右侧删除在 Y 中出现的任何字符组成。如果省略了 Y 参数,则 rtrim(X) 从 X 的右侧删除空格。
sign(X):sign(X) 函数返回 -1、0 或 +1,如果参数 X 是负数、零或正数,则分别返回。如果 sign(X) 的参数为 NULL 或为无法无损转换为数字的字符串或 blob,则 sign(X) 返回 NULL。
soundex(X):soundex(X) 函数返回字符串 X 的声音编码。如果参数为 NULL 或不包含 ASCII 字母字符,则返回字符串 “?000”。此功能默认情况下在 SQLite 中被省略。只有在构建 SQLite 时使用了 SQLITE_SOUNDEX 编译时选项时,它才可用。
substr(X,Y,Z) / substring(X,Y,Z):substr(X,Y,Z) 函数返回输入字符串 X 的子字符串,从第 Y 个字符开始,长度为 Z 个字符。如果省略了 Z,则 substr(X,Y) 返回从第 Y 个字符开始直到字符串 X 的末尾的所有字符。X 中最左边的字符为 1。如果 Y 为负数,则子字符串的第一个字符从右边计数而不是左边。如果 Z 为负数,则返回 Y 之前的 abs(Z) 个字符。如果 X 是字符串,则字符索引指的是实际的 UTF-8 字符。如果 X 是 BLOB,则索引指的是字节。
“substring()” 是从 SQLite 版本 3.34 开始的 “substr()” 的别名。
total_changes():total_changes() 函数返回自打开当前数据库连接以来由 INSERT、UPDATE 或 DELETE 语句引起的行更改次数。该函数是 sqlite3_total_changes64() C/C++ 接口的包装。
trim(X,Y):trim(X,Y) 函数返回一个字符串,该字符串由从 X 的两端删除在 Y 中出现的任何字符组成。如果省略了 Y 参数,则 trim(X) 从 X 的两端删除空格。
typeof(X):typeof(X) 函数返回一个字符串,指示表达式 X 的数据类型:“null”、“integer”、“real”、“text” 或 “blob”。
unhex(X):unhex(X,Y) 函数返回十六进制字符串 X 的解码的 BLOB 值。如果 X 包含任何不是十六进制数字且不在 Y 中的字符,则 unhex(X,Y) 返回 NULL。如果省略了 Y,则理解为空字符串,因此 X 必须是纯十六进制字符串。X 中的所有十六进制数字必须成对出现,每对数字的两个数字立即相邻,否则 unhex(X,Y) 返回 NULL。如果参数 X 或 Y 中的任一参数为 NULL,则 unhex(X,Y) 返回 NULL。X 输入可能包含任意混合大小写的十六进制数字。Y 中的十六进制数字不影响 X 的转换。在 unhex(X,Y) 中忽略 Y 中的非十六进制数字。
查看文档会更方便,因为它会提供更详细的信息和示例。
abs(X)
-- 测试abs(X)函数是否正确
-- 创建一个测试数据
INSERT INTO test_table (id, name) VALUES (1, 'Negative Number'), (2, 'Positive Number'), (3, 'Zero');
-- 在Negative Number列中插入一个负数
UPDATE test_table SET id = -1 WHERE name = 'Negative Number';
-- 在Positive Number列中插入一个正数
UPDATE test_table SET id = 1 WHERE name = 'Positive Number';
-- 查询测试结果
SELECT
name,
id,
ABS(id) AS absolute_value
FROM
test_table;
changes()
-- 创建测试数据库
CREATE TABLE test_changes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL
);
-- 插入初始数据
INSERT INTO test_changes (name) VALUES ('Row 1'), ('Row 2'), ('Row 3');
-- 测试INSERT语句
INSERT INTO test_changes (name) VALUES ('New Row');
SELECT changes(); -- 应该返回1,因为只有一行被插入
-- 测试UPDATE语句
UPDATE test_changes SET name = 'Updated Row' WHERE id = 2;
SELECT changes(); -- 应该返回1,因为只有一行被更新
-- 测试DELETE语句
DELETE FROM test_changes WHERE id = 3;
SELECT changes(); -- 应该返回1,因为只有一行被删除
-- 测试没有影响行的语句
SELECT * FROM test_changes WHERE id = 10;
SELECT changes(); -- 应该返回0,因为上一条SELECT语句没有影响行数
-- 删除测试表
DROP TABLE test_changes;
char(X1,X2,…,XN)
-- 测试char(X1,X2,...,XN)函数是否正确
-- 该函数将ASCII码值转换为字符
-- 测试单个参数
SELECT char(65); -- 应该返回大写字母'A'
-- 测试多个参数
SELECT char(72, 101, 108, 108, 111); -- 应该返回字符串'Hello'
-- 测试混合参数
SELECT char(72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100); -- 应该返回字符串'Hello World'
-- 测试空参数
SELECT char(); -- 应该返回空字符串
coalesce(X,Y,…)
-- 创建一个测试数据
INSERT INTO products (name, description, price, stock) VALUES
('Product A', NULL, 100.00, 10),
('Product B', 'Description B', NULL, 5),
('Product C', 'Description C', 150.00, 20),
('Product D', NULL, NULL, 0);
-- 使用COALESCE函数查询价格和描述,如果价格或描述为空,则返回默认值
SELECT
name,
COALESCE(description, 'No description available') AS description,
COALESCE(price, 0.00) AS price
FROM
products;
concat(X,…)
-- 测试concat函数的语法是否正确
-- 创建一个测试表
CREATE TABLE test_table (
id INTEGER PRIMARY KEY,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL
);
-- 向测试表中插入一些数据
INSERT INTO test_table (first_name, last_name) VALUES ('John', 'Doe');
INSERT INTO test_table (first_name, last_name) VALUES ('Jane', 'Doe');
INSERT INTO test_table (first_name, last_name) VALUES ('Alice', 'Smith');
-- 使用concat函数将first_name和last_name拼接成full_name
SELECT id, concat(first_name, ' ', last_name) AS full_name FROM test_table;
concat_ws(SEP,X,…)
-- 在用户表中插入几条记录
INSERT INTO users (username, password, email) VALUES
('user1', 'password1', 'user1@example.com'),
('user2', 'password2', 'user2@example.com'),
('user3', 'password3', 'user3@example.com');
-- 测试concat_ws()函数
-- 将用户名和邮箱用逗号分隔合并成一个字符串
SELECT concat_ws(',', username, email) AS user_info FROM users;
-- 将用户名、邮箱和密码用“-”分隔合并成一个字符串
SELECT concat_ws('-', username, email, password) AS user_credentials FROM users;
format(FORMAT,…)
-- 测试 FORMAT 函数是否正确格式化字符串
SELECT FORMAT('Hello, %s!', 'world'); -- 预期结果: 'Hello, world!'
-- 测试带有多个参数的格式化
SELECT FORMAT('Product: %s, Price: %.2f', 'Laptop', 1500.50); -- 预期结果: 'Product: Laptop, Price: 1500.50'
-- 测试使用数字和字符串作为参数
SELECT FORMAT('User ID: %d, Username: %s', 1, 'john_doe'); -- 预期结果: 'User ID: 1, Username: john_doe'
-- 测试日期格式化
SELECT FORMAT('Today is %Y-%m-%d', DATE('now')); -- 预期结果: 'Today is <当前日期>'
-- 测试无效的格式字符串(缺少参数)
SELECT FORMAT('This is a test: %s'); -- 预期结果: 报错,缺少参数
-- 测试无效的格式字符串(多余参数)
SELECT FORMAT('This is a test: %s', 'extra', 'extra2'); -- 预期结果: 'This is a test: extra',多余参数被忽略
glob(X,Y)
-- 测试 glob(X,Y) 函数的语法和功能
-- 创建一个用于测试的用户表
CREATE TABLE test_users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE
);
-- 插入一些测试数据
INSERT INTO test_users (username) VALUES ('john_doe');
INSERT INTO test_users (username) VALUES ('jane_smith');
INSERT INTO test_users (username) VALUES ('james_brown');
INSERT INTO test_users (username) VALUES ('bob_dylan');
INSERT INTO test_users (username) VALUES ('alice_wonderland');
-- 测试1: 使用 glob 进行模式匹配,查找以 'j' 开头的用户名
SELECT * FROM test_users WHERE username GLOB 'j*';
-- 预期结果: 返回 'john_doe', 'jane_smith', 'james_brown'
-- 测试2: 使用 glob 进行模式匹配,查找以 'doe' 结尾的用户名
SELECT * FROM test_users WHERE username GLOB '*doe';
-- 预期结果: 返回 'john_doe'
-- 测试3: 使用 glob 进行模式匹配,查找包含 'w' 的用户名
SELECT * FROM test_users WHERE username GLOB '*w*';
-- 预期结果: 返回 'james_brown', 'bob_dylan', 'alice_wonderland'
-- 测试4: 使用 glob 进行模式匹配,查找以 'j' 开头和 'e' 结尾的用户名
SELECT * FROM test_users WHERE username GLOB 'j*e';
-- 预期结果: 返回 'jane_smith'
-- 测试5: 使用 glob 进行模式匹配,查找只有两个字符的用户名
SELECT * FROM test_users WHERE username GLOB '__';
-- 预期结果: 返回 'jw'
-- 清理测试数据
DROP TABLE IF EXISTS test_users;
hex(X)
-- 测试hex(X)函数是否正确
-- 在这个测试中,我们将对用户表中的用户名进行转换并进行比较
-- 创建一个测试用户
INSERT INTO users (username, password, email) VALUES ('test_user', 'testpassword', 'test@example.com');
-- 使用hex(X)函数将用户名转换为十六进制
SELECT hex(username) AS hex_username FROM users WHERE username = 'test_user';
ifnull(X,Y)
-- 测试 IFNULL(X, Y) 函数是否能正确处理 NULL 值
-- 插入一条记录到 users 表,其中 email 字段为空
INSERT INTO users (username, password, email) VALUES ('jane_doe', 'securepassword', NULL);
-- 使用 IFNULL 函数来选择用户名和电子邮件地址,如果电子邮件地址为 NULL,则替换为字符串 'Email not provided'
SELECT
username,
IFNULL(email, 'Email not provided') AS email
FROM
users;
-- 插入一条记录到 users 表,其中 email 字段不为空
INSERT INTO users (username, password, email) VALUES ('bob_smith', 'password123', 'bob@example.com');
-- 使用 IFNULL 函数来选择用户名和电子邮件地址,如果电子邮件地址为 NULL,则替换为字符串 'Email not provided'
SELECT
username,
IFNULL(email, 'Email not provided') AS email
FROM
users;
iif(X,Y,Z)
-- 测试IIF函数语法是否正确
-- 创建一个临时测试表
CREATE TEMPORARY TABLE test_table (
id INTEGER PRIMARY KEY,
condition INTEGER NOT NULL,
value_true TEXT NOT NULL,
value_false TEXT NOT NULL
);
-- 向测试表中插入一些测试数据
INSERT INTO test_table (condition, value_true, value_false) VALUES
(1, 'True Value', 'False Value'),
(0, 'True Value', 'False Value');
-- 使用IIF函数进行测试
-- 当条件为真时返回 value_true,否则返回 value_false
SELECT
condition,
IIF(condition = 1, value_true, value_false) AS result
FROM
test_table;
instr(X,Y)
-- 测试INSTR(X, Y)函数是否正确工作
-- 创建一个测试表
CREATE TABLE test_strings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
main_string TEXT NOT NULL,
sub_string TEXT NOT NULL
);
-- 插入测试数据
INSERT INTO test_strings (main_string, sub_string) VALUES ('hello world', 'hello');
INSERT INTO test_strings (main_string, sub_string) VALUES ('hello world', 'world');
INSERT INTO test_strings (main_string, sub_string) VALUES ('hello world', 'foo');
-- 测试INSTR函数返回正确的位置
SELECT
main_string,
sub_string,
INSTR(main_string, sub_string) AS position
FROM
test_strings;
last_insert_rowid()
-- 插入一条用户数据
INSERT INTO users (username, password, email) VALUES ('test_user', 'test_password', 'test@example.com');
-- 获取最后插入行的ID
SELECT last_insert_rowid() AS last_insert_id;
-- 验证最后插入行的ID是否正确
SELECT id FROM users WHERE id = last_insert_rowid();
length(X)
-- 测试长度为0的字符串
SELECT length('') AS length_empty_string;
-- 测试长度为非零的字符串
SELECT length('Hello') AS length_hello_string;
-- 测试包含空格的字符串的长度
SELECT length('Hello World') AS length_hello_world_string;
-- 测试包含特殊字符的字符串的长度
SELECT length('特殊字符') AS length_special_characters_string;
-- 测试NULL值的长度
SELECT length(NULL) AS length_null_value;
like(X,Y)、like(X,Y,Z)
-- 测试 like(X,Y) 语法
SELECT * FROM users WHERE username LIKE 'joh%'; -- 匹配以 'joh' 开头的所有用户名
-- 测试 like(X,Y,Z) 语法
SELECT * FROM products WHERE name LIKE '%laptop%'; -- 匹配名称中包含 'laptop' 的所有产品
likelihood(X,Y)
-- 假设我们想测试 likelihood(X,Y) 语法,表示在事件 X 发生的情况下事件 Y 也发生的概率
-- 例如,我们想测试在用户下订单的情况下,订单中包含产品的概率
-- 首先,我们需要计算事件 X 和事件 Y 同时发生的次数,然后除以事件 X 发生的总次数,即 likelihood(X,Y) = count(X and Y) / count(X)
-- 计算事件 X 发生的总次数(用户下订单的次数)
SELECT COUNT(*) AS x_count FROM orders;
-- 计算事件 X 和事件 Y 同时发生的次数(用户下订单并且订单中包含产品的次数)
SELECT COUNT(*) AS xy_count
FROM orders
JOIN order_items ON orders.id = order_items.order_id;
-- 最终计算 likelihood(X,Y)
SELECT (CAST((SELECT COUNT(*) FROM orders) AS REAL) / (SELECT COUNT(*) FROM orders)) AS likelihood_xy;
likely(X)
-- 在用户表中插入一些测试数据
INSERT INTO users (username, password, email) VALUES
('user1', 'password123', 'user1@example.com'),
('user2', 'P@ssw0rd', 'user2@example.com'),
('user3', 'securepassword', 'user3@example.com');
-- 查询密码强度,使用LIKELY(X)函数判断
SELECT
username,
password,
LIKELY(password LIKE '%[0-9]%') AS contains_number,
LIKELY(password LIKE '%[A-Z]%') AS contains_uppercase,
LIKELY(password LIKE '%[a-z]%') AS contains_lowercase,
LIKELY(LENGTH(password) >= 8) AS is_long_enough
FROM
users;
load_extension(X)、load_extension(X,Y)
-- 测试 load_extension(X):加载扩展模块
-- 假设我们有一个名为 "my_extension.so" 的扩展模块文件
-- 尝试加载扩展模块
SELECT load_extension('my_extension.so');
-- 如果加载成功,则将返回 1,如果失败则返回 0
-- 请注意:在SQLite中,默认情况下,load_extension() 函数是被禁用的。要启用它,必须在编译SQLite时启用 SQLITE_ENABLE_LOAD_EXTENSION 宏,并且在运行时设置 allow_load_extension pragma。
-- 测试 load_extension(X, Y):加载扩展模块并指定入口点函数名
-- 假设我们有一个名为 "my_extension2.so" 的扩展模块文件,其中包含一个名为 "custom_function" 的自定义函数
-- 尝试加载扩展模块并指定入口点函数名
SELECT load_extension('my_extension2.so', 'custom_function');
-- 如果加载成功并且自定义函数存在,则不会有任何输出
-- 如果加载成功但自定义函数不存在,则会出现错误消息,指示找不到指定的函数
-- 如果加载失败,则会出现错误消息
-- 在测试前,请确保 "my_extension.so" 和 "my_extension2.so" 这两个文件存在,并且具有相应的扩展模块和自定义函数。
lower(X)
-- 测试 lower(X) 函数
-- 在这个示例中,我们将对不同列使用 lower(X) 函数,以测试它的功能和正确性
-- 测试 lower(X) 在 SELECT 查询中的应用
SELECT
lower(username) AS lowercase_username,
lower(email) AS lowercase_email
FROM
users;
-- 测试 lower(X) 在 INSERT 语句中的应用
-- 在插入数据时,将 username 和 email 转换为小写
INSERT INTO users (username, password, email) VALUES (lower('John_Doe'), 'securepassword', lower('john@example.com'));
-- 测试 lower(X) 在 UPDATE 语句中的应用
-- 在更新数据时,将 username 和 email 转换为小写
UPDATE users SET username = lower(username), email = lower(email) WHERE id = 1;
-- 测试 lower(X) 在 WHERE 条件中的应用
-- 在 WHERE 条件中使用 lower(X) 函数来进行大小写不敏感的匹配
SELECT * FROM users WHERE lower(username) = lower('John_Doe');
-- 测试 lower(X) 在 ORDER BY 子句中的应用
-- 在 ORDER BY 子句中使用 lower(X) 函数进行大小写不敏感的排序
SELECT * FROM users ORDER BY lower(username);
ltrim(X)、、 ltrim(X,Y)
-- 测试 ltrim(X)
-- 创建一个测试表
CREATE TABLE test_table (
id INTEGER PRIMARY KEY,
text_column TEXT
);
-- 插入测试数据
INSERT INTO test_table (text_column) VALUES
(' Hello'),
(' World'),
(' SQLite'),
(' is'),
(' awesome ');
-- 使用 ltrim(X) 函数删除开头的空格
SELECT text_column, ltrim(text_column) AS trimmed_text FROM test_table;
-- 结果应该是去除了开头空格的字符串
-- 测试 ltrim(X,Y)
-- 创建一个包含特殊字符的测试表
CREATE TABLE special_chars (
id INTEGER PRIMARY KEY,
text_column TEXT
);
-- 插入测试数据
INSERT INTO special_chars (text_column) VALUES
('!@#Hello'),
('!@#World'),
('!@#SQLite'),
('!@#is'),
('!@#awesome!@#');
-- 使用 ltrim(X,Y) 函数删除开头的特殊字符
SELECT text_column, ltrim(text_column, '!@#') AS trimmed_text FROM special_chars;
-- 结果应该是去除了开头特殊字符的字符串
max(X,Y,…)、min(X,Y,…)
-- 测试 max() 和 min() 函数
-- 最大整数值
SELECT MAX(id) AS max_id FROM users;
-- 最小整数值
SELECT MIN(id) AS min_id FROM users;
-- 最大实数值
SELECT MAX(price) AS max_price FROM products;
-- 最小实数值
SELECT MIN(price) AS min_price FROM products;
-- 最大字符串值
SELECT MAX(name) AS max_name FROM products;
-- 最小字符串值
SELECT MIN(name) AS min_name FROM products;
nullif(X,Y)
-- 创建测试数据
INSERT INTO users (username, email) VALUES ('user1', 'user1@example.com');
INSERT INTO users (username, email) VALUES ('user2', NULL);
INSERT INTO users (username, email) VALUES (NULL, 'user3@example.com');
INSERT INTO users (username, email) VALUES (NULL, NULL);
-- 测试 NULLIF(X, Y) 函数
-- Case 1: X 和 Y 都为非 NULL 值,且 X ≠ Y,返回 X
SELECT NULLIF('apple', 'banana') AS result; -- 应返回 'apple'
-- Case 2: X 和 Y 都为非 NULL 值,且 X = Y,返回 NULL
SELECT NULLIF('apple', 'apple') AS result; -- 应返回 NULL
-- Case 3: X 和 Y 都为 NULL,返回 NULL
SELECT NULLIF(NULL, NULL) AS result; -- 应返回 NULL
-- Case 4: X 为非 NULL,Y 为 NULL,返回 X
SELECT NULLIF('apple', NULL) AS result; -- 应返回 'apple'
-- Case 5: X 为 NULL,Y 为非 NULL,返回 NULL
SELECT NULLIF(NULL, 'banana') AS result; -- 应返回 NULL
-- Case 6: X 和 Y 均为列引用,测试在实际数据中的应用
SELECT username, NULLIF(email, '') AS cleaned_email FROM users;
-- 应返回:
-- username | cleaned_email
-- user1 | user1@example.com
-- user2 | NULL
-- NULL | user3@example.com
-- NULL | NULL
octet_length(X)
-- 测试 octet_length(X) 函数的使用情况
-- 创建测试数据
INSERT INTO users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
INSERT INTO users (username, password, email) VALUES ('user2', 'password2', 'user2@example.com');
INSERT INTO products (name, description, price, stock) VALUES ('Product 1', 'Description 1', 100.00, 10);
INSERT INTO products (name, description, price, stock) VALUES ('Product 2', 'Description 2', 200.00, 20);
-- 测试 octet_length(X) 函数对字符串的计算
SELECT
username,
octet_length(username) AS username_length,
email,
octet_length(email) AS email_length
FROM
users;
-- 期望输出:
-- username | username_length | email | email_length
-- ---------|-----------------|-------------------|--------------
-- user1 | 5 | user1@example.com| 16
-- user2 | 5 | user2@example.com| 16
-- 测试 octet_length(X) 函数对列的计算
SELECT
name,
octet_length(name) AS name_length,
description,
octet_length(description) AS description_length
FROM
products;
-- 期望输出:
-- name | name_length | description | description_length
-- ----------|-------------|--------------|-------------------
-- Product 1 | 9 | Description 1| 12
-- Product 2 | 9 | Description 2| 12
printf(FORMAT,…)
-- 测试printf函数的使用
-- 1. 测试基本的格式化输出
SELECT printf('Hello, %s!', 'world');
-- 2. 测试整数格式化
SELECT printf('The number is: %d', 123);
-- 3. 测试浮点数格式化
SELECT printf('The value is: %.2f', 3.14159);
-- 4. 测试多个参数
SELECT printf('Name: %s, Age: %d', 'John', 30);
-- 5. 测试使用变量
WITH vars AS (SELECT 'Alice' AS name, 25 AS age)
SELECT printf('Name: %s, Age: %d', name, age) FROM vars;
quote(X)
-- 测试 quote(X) 函数
-- 创建一个临时表用于测试
CREATE TEMPORARY TABLE temp_test (
id INTEGER PRIMARY KEY,
value TEXT
);
-- 插入一些测试数据
INSERT INTO temp_test (value) VALUES ('John');
INSERT INTO temp_test (value) VALUES ('Tom''s Pizza');
INSERT INTO temp_test (value) VALUES ('Alice "Smith"');
INSERT INTO temp_test (value) VALUES ('100');
INSERT INTO temp_test (value) VALUES ('This is a test');
-- 查询原始数据
SELECT * FROM temp_test;
-- 测试 quote(X) 函数
-- 将字符串值带引号插入到另一个表中
CREATE TEMPORARY TABLE quoted_values (
id INTEGER PRIMARY KEY,
quoted_value TEXT
);
-- 使用 quote(X) 函数将值带引号插入到新表中
INSERT INTO quoted_values (quoted_value) SELECT quote(value) FROM temp_test;
-- 查询带引号的值
SELECT * FROM quoted_values;
-- 清理临时表
DROP TABLE temp_test;
DROP TABLE quoted_values;
random()、randomblob(N)
-- 测试SQLite的random()和randomblob(N)函数
-- 插入一些示例数据以供测试
INSERT INTO users (username, password, email) VALUES ('user1', 'pass1', 'user1@example.com');
INSERT INTO users (username, password, email) VALUES ('user2', 'pass2', 'user2@example.com');
INSERT INTO users (username, password, email) VALUES ('user3', 'pass3', 'user3@example.com');
-- 使用random()函数生成随机数
SELECT random() AS random_number;
-- 使用random()函数生成指定范围的随机数(0到1之间)
SELECT random() * 100 AS random_between_0_and_100;
-- 使用randomblob(N)函数生成长度为N的随机字节串
SELECT randomblob(10) AS random_bytes;
-- 使用randomblob(N)函数生成随机的用户名
SELECT
substr(hex(randomblob(4)), 1, 8) AS random_username
FROM
users
LIMIT 1;
-- 使用randomblob(N)函数生成随机密码
SELECT
substr(hex(randomblob(4)), 1, 8) AS random_password
FROM
users
LIMIT 1;
-- 使用randomblob(N)函数生成随机邮箱
SELECT
substr(hex(randomblob(4)), 1, 8) || '@example.com' AS random_email
FROM
users
LIMIT 1;
hex(randomblob(16))
-- 创建一个测试表
CREATE TABLE test_table (
id INTEGER PRIMARY KEY,
random_data BLOB, -- 用于存储随机数据
hex_data TEXT -- 用于存储随机数据的十六进制表示
);
-- 插入一条记录,同时使用hex(randomblob(16))生成随机数据,并存储其十六进制表示
INSERT INTO test_table (random_data, hex_data) VALUES (randomblob(16), hex(randomblob(16)));
-- 查询表中的数据,验证随机数据是否被正确转换为十六进制表示
SELECT * FROM test_table;
-- 更新表中的记录,将随机数据字段更新为新的随机值,并更新对应的十六进制表示
UPDATE test_table SET random_data = randomblob(16), hex_data = hex(random_data);
-- 再次查询表中的数据,确认更新后的数据是否正确
SELECT * FROM test_table;
-- 删除表中的所有记录
DELETE FROM test_table;
-- 重新插入一些记录,以备后续测试使用
INSERT INTO test_table (random_data, hex_data) VALUES (randomblob(16), hex(randomblob(16)));
INSERT INTO test_table (random_data, hex_data) VALUES (randomblob(16), hex(randomblob(16)));
INSERT INTO test_table (random_data, hex_data) VALUES (randomblob(16), hex(randomblob(16)));
-- 使用带条件的查询,查找特定的十六进制表示对应的记录
SELECT * FROM test_table WHERE hex_data = 'your_hex_value_here';
-- 使用子查询,查找随机数据长度大于某个值的记录
SELECT * FROM test_table WHERE LENGTH(random_data) > 8;
-- 删除表,清理测试数据
DROP TABLE test_table;
lower(hex(randomblob(16)))
-- 创建测试表
CREATE TABLE test_table (
id INTEGER PRIMARY KEY,
random_hash TEXT
);
-- 插入随机数据
INSERT INTO test_table (random_hash) VALUES (lower(hex(randomblob(16))));
INSERT INTO test_table (random_hash) VALUES (lower(hex(randomblob(16))));
INSERT INTO test_table (random_hash) VALUES (lower(hex(randomblob(16))));
-- 查看插入的数据
SELECT * FROM test_table;
replace(X,Y,Z)
-- 在 users 表中将名为 'john_doe' 的用户的邮箱替换为 'john@example.com'
UPDATE users SET email = REPLACE(email, 'john_doe', 'john@example.com') WHERE username = 'john_doe';
-- 在 products 表中将所有产品描述中的 'high-end' 替换为 'premium'
UPDATE products SET description = REPLACE(description, 'high-end', 'premium');
-- 在 orders 表中将所有订单中的总金额中的 '$' 替换为空字符串
UPDATE orders SET total = REPLACE(total, '$', '');
-- 在 order_items 表中将所有订单项目的价格中的小数点 '.' 替换为逗号 ','
UPDATE order_items SET price = REPLACE(price, '.', ',');
-- 将 users 表中的所有用户名的首字母替换为大写字母
UPDATE users SET username = REPLACE(username, SUBSTR(username, 1, 1), UPPER(SUBSTR(username, 1, 1)));
-- 将 products 表中所有产品名称中的 'Laptop' 替换为 'Desktop'
UPDATE products SET name = REPLACE(name, 'Laptop', 'Desktop');
-- 将 orders 表中创建时间为 '2023' 年的订单的创建时间替换为当前时间
UPDATE orders SET created_at = REPLACE(created_at, '2023', CURRENT_TIMESTAMP);
-- 将 order_items 表中所有订单项目的数量中的 '1' 替换为 '10'
UPDATE order_items SET quantity = REPLACE(quantity, '1', '10');
round(X)、 round(X,Y)
-- 测试 round(X) 函数,对数字 X 进行四舍五入
SELECT ROUND(123.456); -- 结果应为 123
SELECT ROUND(123.567); -- 结果应为 124
SELECT ROUND(-123.456); -- 结果应为 -123
SELECT ROUND(-123.567); -- 结果应为 -124
-- 测试 round(X, Y) 函数,对数字 X 进行保留 Y 位小数的四舍五入
SELECT ROUND(123.456, 2); -- 结果应为 123.46
SELECT ROUND(123.567, 2); -- 结果应为 123.57
SELECT ROUND(-123.456, 2); -- 结果应为 -123.46
SELECT ROUND(-123.567, 2); -- 结果应为 -123.57
-- 测试 round(X, Y) 函数,当 Y 为负数时,对 X 的 Y 位数左侧进行四舍五入
SELECT ROUND(123.456, -1); -- 结果应为 120
SELECT ROUND(123.567, -1); -- 结果应为 120
SELECT ROUND(-123.456, -1); -- 结果应为 -120
SELECT ROUND(-123.567, -1); -- 结果应为 -120
-- 测试 round(X, Y) 函数,当 Y 为零时,对 X 进行四舍五入到整数
SELECT ROUND(123.456, 0); -- 结果应为 123
SELECT ROUND(123.567, 0); -- 结果应为 124
SELECT ROUND(-123.456, 0); -- 结果应为 -123
SELECT ROUND(-123.567, 0); -- 结果应为 -124
rtrim(X)、 rtrim(X,Y)
-- 测试 rtrim(X) 函数
-- 在 users 表的 username 列上测试 rtrim(X)
SELECT username, rtrim(username) AS trimmed_username
FROM users;
-- 测试 rtrim(X,Y) 函数
-- 在 products 表的 description 列上测试 rtrim(X,Y),Y 是指定的要删除的字符
SELECT description, rtrim(description, '.') AS trimmed_description
FROM products;
-- 添加包含空格的测试数据
INSERT INTO users (username, password, email) VALUES (' user1 ', 'password1', 'user1@example.com');
INSERT INTO products (name, description, price, stock) VALUES ('Product1', 'Description with trailing spaces. ', 100.00, 5);
-- 再次测试 rtrim(X) 函数以处理空格
SELECT username, rtrim(username) AS trimmed_username
FROM users;
-- 再次测试 rtrim(X,Y) 函数以处理空格
SELECT description, rtrim(description, ' ') AS trimmed_description
FROM products;
sign(X)
-- 创建测试表
CREATE TABLE sign_test (
id INTEGER PRIMARY KEY AUTOINCREMENT,
value REAL
);
-- 插入测试数据
INSERT INTO sign_test (value) VALUES (10); -- 正数
INSERT INTO sign_test (value) VALUES (-5); -- 负数
INSERT INTO sign_test (value) VALUES (0); -- 零
INSERT INTO sign_test (value) VALUES (NULL); -- 空值
INSERT INTO sign_test (value) VALUES (3.14); -- 正浮点数
INSERT INTO sign_test (value) VALUES (-2.71);-- 负浮点数
-- 使用 sign(X) 函数进行查询并显示结果
SELECT
id,
value,
sign(value) AS sign_value
FROM
sign_test;
soundex(X)
-- 插入测试数据
INSERT INTO users (username, password, email) VALUES
('john_doe', 'securepassword', 'john@example.com'),
('jane_doe', 'anotherpassword', 'jane@example.com'),
('jack_doe', 'yetanotherpassword', 'jack@example.com');
INSERT INTO products (name, description, price, stock) VALUES
('Laptop', 'A high-end gaming laptop', 1500.00, 10),
('Lamp', 'A desk lamp', 20.00, 100),
('Lamb', 'A delicious lamb meat', 12.00, 50);
-- 测试SOUNDEX函数
-- SOUNDEX函数用于返回字符串的语音编码值,可以用于比较发音相似的字符串
-- 测试SOUNDEX用于查找发音相似的用户名
SELECT username, SOUNDEX(username) AS soundex_code
FROM users;
-- 查找发音类似于 'john_doe' 的用户名
SELECT username
FROM users
WHERE SOUNDEX(username) = SOUNDEX('john_doe');
-- 测试SOUNDEX用于查找发音相似的产品名
SELECT name, SOUNDEX(name) AS soundex_code
FROM products;
-- 查找发音类似于 'Laptop' 的产品名
SELECT name
FROM products
WHERE SOUNDEX(name) = SOUNDEX('Laptop');
-- 查找发音类似于 'Lamp' 的产品名
SELECT name
FROM products
WHERE SOUNDEX(name) = SOUNDEX('Lamp');
-- 查找发音类似于 'Lamb' 的产品名
SELECT name
FROM products
WHERE SOUNDEX(name) = SOUNDEX('Lamb');
sqlite_compileoption_get(N)、sqlite_compileoption_used(X)、sqlite_offset(X)
-- 插入测试数据
INSERT INTO users (username, password, email) VALUES ('john_doe', 'securepassword', 'john@example.com');
INSERT INTO products (name, description, price, stock) VALUES ('Laptop', 'A high-end gaming laptop', 1500.00, 10);
INSERT INTO orders (user_id, total) VALUES (1, 1500.00);
INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (1, 1, 1, 1500.00);
-- 获取编译选项 (sqlite_compileoption_get)
-- 这将返回编译时定义的选项,如果存在则返回选项名,不存在则返回NULL
SELECT sqlite_compileoption_get(0); -- 获取第一个编译选项
SELECT sqlite_compileoption_get(1); -- 获取第二个编译选项
-- 可以继续增加索引直到返回NULL为止
-- 检查特定编译选项是否使用 (sqlite_compileoption_used)
-- 如果选项被使用则返回1,否则返回0
SELECT sqlite_compileoption_used('ENABLE_LOAD_EXTENSION'); -- 检查是否启用加载扩展
SELECT sqlite_compileoption_used('THREADSAFE'); -- 检查线程安全选项是否启用
-- 查询偏移量 (sqlite_offset)
-- 使用OFFSET功能进行分页查询
-- 查询订单表中的数据并获取偏移量
SELECT id, total FROM orders LIMIT 1 OFFSET 0; -- 获取第1条记录
SELECT id, total FROM orders LIMIT 1 OFFSET 1; -- 获取第2条记录
-- 注意: 由于sqlite_offset是一个虚函数,实际用例可能会不同,这里只是作为参考
-- 检查OFFSET是否正确应用,通过检查返回的行数
SELECT COUNT(*) FROM (SELECT id FROM orders LIMIT 1 OFFSET 1); -- 确认返回1行
sqlite_offset(X)、sqlite_source_id()、sqlite_version()
-- 使用 sqlite_version() 获取 SQLite 版本号
-- 该函数返回当前 SQLite 库的版本号
SELECT sqlite_version() AS current_version;
-- 测试 sqlite_offset(X) 函数
-- sqlite_offset(X) 返回与 BLOB 的第一个字节相对于其所在页的偏移量
-- 该函数通常用于内部调试或高级查询优化
-- 在我们的测试环境中创建一个包含 BLOB 数据的表
CREATE TABLE blob_test (
id INTEGER PRIMARY KEY AUTOINCREMENT,
data BLOB
);
-- 插入示例 BLOB 数据
INSERT INTO blob_test (data) VALUES (zeroblob(100)); -- 创建一个长度为100字节的BLOB数据
-- 查询 BLOB 数据的偏移量
SELECT id, sqlite_offset(data) AS blob_offset FROM blob_test;
-- 使用 sqlite_source_id() 获取 SQLite 源代码版本的唯一标识符
-- 该函数返回一个标识当前源代码版本的字符串
SELECT sqlite_source_id() AS source_id;
-- 清理测试数据
DROP TABLE blob_test;
substr(X,Y,Z)、substr(X,Y)、substring(X,Y,Z)、substring(X,Y)
-- 创建测试表
CREATE TABLE string_test (
id INTEGER PRIMARY KEY AUTOINCREMENT,
test_string TEXT NOT NULL
);
-- 插入测试数据
INSERT INTO string_test (test_string) VALUES ('Hello, World!');
-- 测试 substr(X, Y, Z)
-- 预期输出:'ello' (从第2个字符开始,取4个字符)
SELECT id, test_string, substr(test_string, 2, 4) AS substr_test FROM string_test;
-- 测试 substr(X, Y)
-- 预期输出:'World!' (从第8个字符开始到结束)
SELECT id, test_string, substr(test_string, 8) AS substr_test FROM string_test;
-- 测试 substring(X, Y, Z)
-- 由于SQLite不支持substring函数,此查询会失败
-- 预期输出:错误,提示函数不存在
-- SELECT id, test_string, substring(test_string, 2, 4) AS substring_test FROM string_test;
-- 测试 substring(X, Y)
-- 由于SQLite不支持substring函数,此查询会失败
-- 预期输出:错误,提示函数不存在
-- SELECT id, test_string, substring(test_string, 8) AS substring_test FROM string_test;
-- 为了验证,尝试执行并捕获错误信息
BEGIN TRANSACTION;
SAVEPOINT test_savepoint;
-- 尝试执行 substring(X, Y, Z)
SELECT id, test_string,
CASE
WHEN (SELECT count(*) FROM pragma_function_list WHERE name='substring') = 0 THEN 'Function not supported'
ELSE substring(test_string, 2, 4)
END AS substring_test
FROM string_test;
-- 尝试执行 substring(X, Y)
SELECT id, test_string,
CASE
WHEN (SELECT count(*) FROM pragma_function_list WHERE name='substring') = 0 THEN 'Function not supported'
ELSE substring(test_string, 8)
END AS substring_test
FROM string_test;
ROLLBACK TO test_savepoint;
RELEASE test_savepoint;
total_changes()
-- 测试插入操作
INSERT INTO users (username, password, email) VALUES ('john_doe', 'securepassword', 'john@example.com');
INSERT INTO users (username, password, email) VALUES ('jane_doe', 'securepassword', 'jane@example.com');
-- 应该返回 2,因为插入了2行
SELECT total_changes();
-- 测试更新操作
UPDATE users SET email = 'john_new@example.com' WHERE username = 'john_doe';
-- 应该返回 1,因为更新了1行
SELECT total_changes();
-- 测试删除操作
DELETE FROM users WHERE username = 'jane_doe';
-- 应该返回 1,因为删除了1行
SELECT total_changes();
-- 测试多次操作后的total_changes
INSERT INTO products (name, description, price, stock) VALUES ('Laptop', 'A high-end gaming laptop', 1500.00, 10);
INSERT INTO products (name, description, price, stock) VALUES ('Mouse', 'Wireless mouse', 25.00, 100);
UPDATE products SET stock = 20 WHERE name = 'Laptop';
DELETE FROM products WHERE name = 'Mouse';
-- 应该返回 4,因为插入了2行,更新了1行,删除了1行
SELECT total_changes();
trim(X)、trim(X,Y)
-- 创建测试数据
CREATE TABLE test_trim (
id INTEGER PRIMARY KEY,
text_value TEXT NOT NULL
);
INSERT INTO test_trim (text_value) VALUES
(' Hello '),
(' World '),
(' Greetings ');
-- 测试 TRIM(X)
SELECT
text_value AS original_text,
TRIM(text_value) AS trimmed_text
FROM
test_trim;
-- 期望结果:去除每个文本值的前导和尾随空格
-- 测试 TRIM(X,Y)
SELECT
text_value AS original_text,
TRIM(text_value, 'lHe') AS trimmed_text
FROM
test_trim;
-- 期望结果:去除每个文本值的前导和尾随包含在指定字符串 'lHe' 中的字符
typeof(X)
-- 测试 typeof(X) 函数是否正确
-- 检查 users 表中各列的数据类型
SELECT
typeof(id) AS id_type,
typeof(username) AS username_type,
typeof(password) AS password_type,
typeof(email) AS email_type,
typeof(created_at) AS created_at_type
FROM
users;
-- 检查 products 表中各列的数据类型
SELECT
typeof(id) AS id_type,
typeof(name) AS name_type,
typeof(description) AS description_type,
typeof(price) AS price_type,
typeof(stock) AS stock_type,
typeof(created_at) AS created_at_type
FROM
products;
-- 检查 orders 表中各列的数据类型
SELECT
typeof(id) AS id_type,
typeof(user_id) AS user_id_type,
typeof(total) AS total_type,
typeof(created_at) AS created_at_type
FROM
orders;
-- 检查 order_items 表中各列的数据类型
SELECT
typeof(order_id) AS order_id_type,
typeof(product_id) AS product_id_type,
typeof(quantity) AS quantity_type,
typeof(price) AS price_type
FROM
order_items;
unhex(X)、unhex(X,Y)
-- 测试unhex(X)函数
-- unhex(X)函数用于将十六进制字符串转换为二进制数据
-- 测试用例1: 将十六进制字符串转换为二进制数据
SELECT unhex('48656C6C6F'); -- 预期结果: 'Hello'
-- 测试用例2: 将包含空格的十六进制字符串转换为二进制数据
SELECT unhex('4865 6C6C6F'); -- 预期结果: 'Hello'
-- 测试用例3: 将包含特殊字符的十六进制字符串转换为二进制数据
SELECT unhex('4865_6C6C6F'); -- 预期结果: 'Hello'
-- 测试用例4: 将空的十六进制字符串转换为二进制数据
SELECT unhex(''); -- 预期结果: ''
-- 测试用例5: 将非法的十六进制字符串转换为二进制数据
-- 这里使用的十六进制字符串中包含非法字符'G',unhex函数应该返回空值或错误
SELECT unhex('48656G6C6C6F'); -- 预期结果: NULL 或 错误
-- 测试unhex(X,Y)函数
-- unhex(X,Y)函数用于将十六进制字符串转换为二进制数据,并将其放入指定的二进制数据缓冲区中
-- 测试用例6: 将十六进制字符串转换为二进制数据,并存入指定的二进制数据缓冲区
-- 在这个测试用例中,我们将 'Hello' 转换为二进制数据并将其放入名为 'buffer' 的二进制数据缓冲区
SELECT unhex('48656C6C6F', 'buffer');
-- 查看 'buffer' 缓冲区的内容
SELECT buffer; -- 预期结果: 'Hello'
-- 测试用例7: 使用unhex(X,Y)函数时指定的缓冲区不存在
-- 在这个测试用例中,我们尝试将数据存入名为 'non_existing_buffer' 的不存在的二进制数据缓冲区
-- unhex函数应该返回空值或错误
SELECT unhex('48656C6C6F', 'non_existing_buffer'); -- 预期结果: NULL 或 错误
unicode(X)
-- 测试SQLite的unicode(X)函数
-- 插入一些测试数据
INSERT INTO users (username, password, email) VALUES ('user1', 'pass1', 'user1@example.com');
INSERT INTO products (name, description, price, stock) VALUES ('Product 1', 'Description 1', 100.00, 50);
INSERT INTO orders (user_id, total) VALUES (1, 100.00);
INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (1, 1, 1, 100.00);
-- 测试字符串的unicode码点值
-- 字符串:Hello World!
SELECT unicode('Hello World!');
-- 字符串:你好世界!
SELECT unicode('你好世界!');
-- 字符串:😊
SELECT unicode('😊');
-- 获取用户名为'user1'的用户的用户名和密码的unicode码点值
SELECT
username,
unicode(username) AS username_unicode,
unicode(password) AS password_unicode
FROM
users
WHERE
username = 'user1';
-- 获取产品名为'Product 1'的产品的名称和描述的unicode码点值
SELECT
name,
unicode(name) AS name_unicode,
unicode(description) AS description_unicode
FROM
products
WHERE
name = 'Product 1';
-- 获取订单ID为1的订单总额的unicode码点值
SELECT
id,
unicode(total) AS total_unicode
FROM
orders
WHERE
id = 1;
-- 获取订单项目的数量和价格的unicode码点值
SELECT
quantity,
unicode(price) AS price_unicode
FROM
order_items
WHERE
order_id = 1;
unlikely(X)
-- 在users表中进行unlikely函数的测试
-- 测试目的:确保unlikely函数对于不太可能出现的值提供了正确的优化提示
-- 插入一条用户记录,ID设为100,这样后面的查询中ID不等于100的记录就很少
INSERT INTO users (id, username, password, email) VALUES (100, 'testuser', 'testpassword', 'test@example.com');
-- 查询ID不等于100的用户,使用unlikely函数提示ID为100的记录很少出现
EXPLAIN QUERY PLAN SELECT * FROM users WHERE unlikely(id=100);
-- 查询ID等于100的用户,使用unlikely函数提示ID为100的记录很少出现
EXPLAIN QUERY PLAN SELECT * FROM users WHERE unlikely(id=100);
-- 查询username等于'testuser'的用户,使用unlikely函数提示这是一种不太可能的情况
EXPLAIN QUERY PLAN SELECT * FROM users WHERE unlikely(username='testuser');
-- 查询username不等于'testuser'的用户,使用unlikely函数提示这是一种不太可能的情况
EXPLAIN QUERY PLAN SELECT * FROM users WHERE unlikely(username='testuser');
-- 查询email等于'test@example.com'的用户,使用unlikely函数提示这是一种不太可能的情况
EXPLAIN QUERY PLAN SELECT * FROM users WHERE unlikely(email='test@example.com');
-- 查询email不等于'test@example.com'的用户,使用unlikely函数提示这是一种不太可能的情况
EXPLAIN QUERY PLAN SELECT * FROM users WHERE unlikely(email='test@example.com');
upper(X)
-- 创建一个测试表
CREATE TABLE test_table (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
-- 插入一些测试数据
INSERT INTO test_table (name) VALUES ('john'), ('Doe'), ('Mary'), ('Smith');
-- 查询原始数据
SELECT * FROM test_table;
-- 使用 UPPER(X) 函数将 name 字段转换为大写
SELECT id, UPPER(name) AS uppercase_name FROM test_table;
-- 清理测试数据
DROP TABLE test_table;
zeroblob(N)
-- 创建一个包含 BLOB 字段的表
CREATE TABLE files (
id INTEGER PRIMARY KEY AUTOINCREMENT,
filename TEXT NOT NULL,
filedata BLOB
);
-- 插入带有 zeroblob 数据的记录
INSERT INTO files (filename, filedata) VALUES ('test_file_1', zeroblob(1024)); -- 1 KB 的零值 BLOB
INSERT INTO files (filename, filedata) VALUES ('test_file_2', zeroblob(2048)); -- 2 KB 的零值 BLOB
-- 验证插入的数据
SELECT id, filename, length(filedata) as blob_length FROM files;
-- 更新记录中的 BLOB 数据
UPDATE files SET filedata = zeroblob(512) WHERE filename = 'test_file_1'; -- 更新为 512 字节的零值 BLOB
-- 验证更新的数据
SELECT id, filename, length(filedata) as blob_length FROM files;
-- 插入带有 NULL BLOB 数据的记录作为对比
INSERT INTO files (filename, filedata) VALUES ('test_file_3', NULL);
-- 验证所有数据
SELECT id, filename, length(filedata) as blob_length FROM files;
CREATE INDEX
-- 为 users 表的 username 字段创建唯一索引
CREATE UNIQUE INDEX idx_username ON users (username);
-- 为 products 表的 price 字段创建普通索引,以便快速按价格查询商品
CREATE INDEX idx_price ON products (price);
-- 为 orders 表的 user_id 字段创建索引,以便快速按用户 ID 查询订单
CREATE INDEX idx_user_id ON orders (user_id);
-- 为 order_items 表的 order_id 字段创建联合索引,以便快速按订单 ID 查询订单项目
CREATE INDEX idx_order_id ON order_items (order_id);
-- 为 order_items 表的 product_id 字段创建联合索引,以便快速按产品 ID 查询订单项目
CREATE INDEX idx_product_id ON order_items (product_id);
create table
官网链接:创建表 (sqlite.org)
“CREATE TABLE”命令用于在 SQLite 中创建新表 数据库。CREATE TABLE命令指定 新表:
- 新表的名称。
- 在其中创建新表的数据库。表可以是 在主数据库、临时数据库或任何附加数据库中创建 数据库。
- 表中每列的名称。
- 表中每列的声明类型。
- 表中每列的默认值或表达式。
- 用于每列的默认排序规则序列。
- (可选)表的 PRIMARY KEY。单列和 支持复合(多列)主键。
- 每个表的一组 SQL 约束。SQLite 支持 UNIQUE,而不是 NULL、CHECK 和 FOREIGN KEY 约束。
- (可选)生成的列约束。
- 该表是否为 WITHOUT ROWID 表。
- 表是否经过严格的类型检查。
每个 CREATE TABLE 语句都必须指定新表的名称。 以“sqlite_”开头的表名保留供内部使用。它 是尝试创建名称以 “sqlite_”。
CREATE TRIGGER
-- 创建一个触发器,每当在用户表中插入新行时,自动向订单表中插入一条默认订单
CREATE TRIGGER insert_default_order AFTER INSERT ON users
BEGIN
INSERT INTO orders (user_id, total) VALUES (NEW.id, 0.00);
END;
-- 创建一个触发器,每当在订单表中插入新行时,更新该订单的总金额
CREATE TRIGGER update_order_total AFTER INSERT ON order_items
BEGIN
UPDATE orders
SET total = (
SELECT SUM(oi.quantity * p.price)
FROM order_items oi
JOIN products p ON oi.product_id = p.id
WHERE oi.order_id = NEW.order_id
)
WHERE id = NEW.order_id;
END;
CREATE VIEW
官网原文:创建视图 (sqlite.org)
CREATE VIEW 命令为预打包的 SELECT 语句指定一个名称。 创建视图后,可以在 FROM 子句中使用它 另一个 SELECT 来代替表名。
如果“TEMP”或“TEMPORARY”关键字出现在“CREATE”之间 和“VIEW”,则创建的视图仅对创建它的数据库连接可见,并在以下情况下自动删除 数据库连接已关闭。
如果指定了架构名称,则视图 在指定的数据库中创建。 在 VIEW 上同时指定 schema-name 和 TEMP 关键字是错误的,除非 schema-name 为“temp”。 如果未指定架构名称,并且 TEMP 关键字不存在, VIEW 是在主数据库中创建的。
不能删除、插入或更新视图。视图是只读的 在 SQLite 中。但是,在许多情况下,您可以在视图上使用 INSTEAD OF 触发器来完成 同样的事情。视图被移除 使用 DROP VIEW 命令。
CREATE VIRTUAL TABLE
官网原文:Date And Time Functions (sqlite.org)
虚拟表是外部存储或计算的接口 看似表但实际上不存储信息的引擎 在数据库文件中。
通常,可以使用虚拟表执行任何可以执行的操作 使用普通表,但不能在 虚拟表。某些虚拟表实现可能会施加额外的 限制。例如,许多虚拟表是只读的。
使用普通的 DROP TABLE语句销毁虚拟表。没有 DROP VIRTUAL TABLE 语句。
-- 测试基于现有表的虚拟表创建语法
-- 创建一个基于已有表users的虚拟表
CREATE VIRTUAL TABLE users_fts USING fts4(content='users', username, email);
-- 创建一个基于已有表products的虚拟表
CREATE VIRTUAL TABLE products_fts USING fts4(content='products', name, description);
-- 创建一个基于多个表的虚拟表,以实现全文搜索
CREATE VIRTUAL TABLE search_fts USING fts4(content='users, products', username, email, name, description);
-- 创建一个基于现有表orders的虚拟表,使用外部内容提供者
CREATE VIRTUAL TABLE orders_external USING external_module(content='orders', user_id, total);
-- 创建一个基于自定义外部内容提供者的虚拟表
CREATE VIRTUAL TABLE custom_external USING external_module(content='custom_provider', column1, column2);
Date And Time Functions
SQLite 支持七种标量日期和时间函数,如下所示:
- date(time-value, modifier, modifier, …)
- time(time-value, modifier, modifier, …)
- datetime(time-value, modifier, modifier, …)
- julianday(time-value, modifier, modifier, …)
- unixepoch(time-value, modifier, modifier, …)
- strftime(format, time-value, modifier, modifier, …)
- timediff(time-value, time-value)
前 6 个日期和时间函数采用可选的时间值作为参数,后跟 由零个或多个修饰符组成。 strftime() 函数还采用格式字符串作为其第一个参数。 timediff() 函数正好接受两个参数,这两个参数都是时间值。
-- 使用SQLite日期和时间函数进行测试
-- 1. date()
-- 返回指定时间值的日期部分
SELECT date('2024-05-21 12:34:56');
-- 2. time()
-- 返回指定时间值的时间部分
SELECT time('2024-05-21 12:34:56');
-- 3. datetime()
-- 将日期和时间值以特定格式组合起来
SELECT datetime('2024-05-21 12:34:56', '+1 day', '-2 hours');
-- 4. julianday()
-- 返回指定时间值的朱利安日数
SELECT julianday('2024-05-21 12:34:56');
-- 5. unixepoch()
-- 将Unix时间戳转换为日期和时间值
SELECT datetime(1590044486, 'unixepoch');
-- 6. strftime()
-- 格式化日期和时间值
SELECT strftime('%Y-%m-%d %H:%M:%S', 'now');
-- 7. timediff()
-- 计算两个时间值之间的时间差
SELECT timediff('2024-05-21 12:34:56', '2024-05-20 10:00:00');
DELETE
DELETE 命令从由 qualified-table-name 标识的表中删除记录。
如果 WHERE 子句不存在,则将删除表中的所有记录。 如果提供了 WHERE 子句,则仅针对 删除 WHERE 子句布尔表达式为 true。 表达式为 false 或 NULL 的行将保留。
以下限制适用于在 CREATE TRIGGER 语句的正文:
- 指定为 DELETE 语句 触发体必须是不合格的。换言之,不允许在表名上加上 schema-name.前缀 在触发器内。如果触发器附加到的表是 不在临时数据库中,则在触发器中执行 DELETE 语句 body 必须对与其位于同一数据库中的表进行操作。如果表 触发器附加到的是在 TEMP 数据库中,然后 要删除的表的非限定名称的解析方式与以下方式相同 它用于顶级语句(通过首先搜索 TEMP 数据库,然后搜索 主数据库,然后是按顺序排列的任何其他数据库 附后)。
- DELETE 上不允许使用 INDEXED BY 和 NOT INDEXED 子句 触发器中的语句。
- 不支持 LIMIT 和 ORDER BY 子句(如下所述) 触发器中的 DELETE 语句。
- 触发器不支持 RETURNING 子句。
如果 DELETE 语句具有 LIMIT 子句,则 将删除是通过评估随附的表达式和强制转换找到的 它设置为整数值。如果 LIMIT 子句的评估结果 不能无损转换为整数值,这是一个错误。一个 负 LIMIT 值被解释为“无限制”。如果 DELETE 语句 也有一个 OFFSET 子句,然后它被类似地计算并转换为 整数值。同样,如果值不能无损,则为错误 转换为整数。如果没有 OFFSET 子句,或者计算出 整数值为负数,有效偏移值为零。
如果 DELETE 语句具有 ORDER BY 子句,则所有 在没有 LIMIT 子句的情况下删除,根据 订购依据。前 M 行,其中 M 是 计算 OFFSET 子句表达式,并删除以下 N,其中 N 是 LIMIT 表达式的值。 如果采用 OFFSET 子句后剩余的行数少于 N 行 考虑在内,或者如果 LIMIT 子句的计算结果为负值,则全部 其余行将被删除。
如果 DELETE 语句没有 ORDER BY 子句,则所有行 在没有 LIMIT 子句的情况下将被删除,这些子句被组装在一个 在应用 LIMIT 和 OFFSET 子句确定之前的任意顺序 实际删除的子集。
DELETE 语句上的 ORDER BY 子句仅用于确定哪个 行落在 LIMIT 范围内。删除行的顺序是任意的 并且不受 ORDER BY 子句的影响。 这意味着,如果存在 RETURNING 子句,则返回的行 该语句可能不会按照 ORDER BY 子句。
-- 删除指定条件的数据
-- 删除用户名为 'john_doe' 的用户
DELETE FROM users WHERE username = 'john_doe';
-- 删除某个订单及其关联的订单项目
-- 删除订单号为 1 的订单及其相关订单项目
DELETE FROM orders WHERE id = 1;
-- 删除所有库存不足的产品
-- 删除库存小于等于 0 的产品
DELETE FROM products WHERE stock <= 0;
-- 删除所有用户
DELETE FROM users;
-- 删除所有订单项目
DELETE FROM order_items;
-- 删除所有订单
DELETE FROM orders;
-- 删除所有数据(清空所有表)
DELETE FROM users;
DELETE FROM products;
DELETE FROM order_items;
DELETE FROM orders;
DETACH
-- 创建第二个数据库以供测试
ATTACH DATABASE 'second_database.db' AS second_db;
-- 创建第二个数据库的表
CREATE TABLE second_db.test_table (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
-- 插入数据到第二个数据库的表
INSERT INTO second_db.test_table (name) VALUES ('Test Data');
-- 查询第二个数据库的表
SELECT * FROM second_db.test_table;
-- 从第二个数据库中分离
DETACH DATABASE second_db;
-- 尝试查询已分离的数据库的表,应该会报错
-- SELECT * FROM second_db.test_table;
DROP
-- 测试DROP INDEX语法
-- 创建一个索引
CREATE INDEX idx_username ON users (username);
-- 尝试删除存在的索引
DROP INDEX IF EXISTS idx_username;
-- 尝试删除不存在的索引
DROP INDEX IF EXISTS idx_nonexistent_index;
-- 测试DROP TABLE语法
-- 尝试删除存在的表
DROP TABLE IF EXISTS users;
-- 尝试删除不存在的表
DROP TABLE IF EXISTS nonexistent_table;
-- 测试DROP TRIGGER语法
-- 创建一个触发器
CREATE TRIGGER my_trigger
AFTER INSERT ON users
BEGIN
-- 触发器逻辑
END;
-- 尝试删除存在的触发器
DROP TRIGGER IF EXISTS my_trigger;
-- 尝试删除不存在的触发器
DROP TRIGGER IF EXISTS nonexistent_trigger;
-- 测试DROP VIEW语法
-- 创建一个视图
CREATE VIEW user_details AS
SELECT id, username, email FROM users;
-- 尝试删除存在的视图
DROP VIEW IF EXISTS user_details;
-- 尝试删除不存在的视图
DROP VIEW IF EXISTS nonexistent_view;
END TRANSACTION
END TRANSACTION 是 COMMIT 的别名。
-- 开始一个事务
BEGIN TRANSACTION;
-- 在 users 表中插入一条新记录
INSERT INTO users (username, password, email) VALUES ('test_user', 'testpassword', 'test@example.com');
-- 查询 users 表,确认新记录已插入(事务尚未提交)
SELECT * FROM users WHERE username = 'test_user';
-- 提交事务
END TRANSACTION;
-- 查询 users 表,确认提交成功
SELECT * FROM users WHERE username = 'test_user';
EXPLAIN
SQL 语句前面可以有关键字“EXPLAIN”或 通过短语“解释查询计划”。
EXPLAIN 和 EXPLAIN QUERY PLAN 的输出适用于 仅限交互式分析和故障排除。详细信息 输出格式可能会从一个版本更改为下一个版本。 应用程序不应使用 EXPLAIN 或 EXPLAIN QUERY PLAN,因为 它们的确切行为是可变的,并且仅部分记录在案。
EXPLAIN 在运行时运行,而不是在准备时运行
-- 查询用户表的执行计划
EXPLAIN QUERY PLAN SELECT * FROM users;
-- 带条件的选择查询的执行计划
EXPLAIN QUERY PLAN SELECT * FROM users WHERE username = 'john_doe';
-- 多表联接查询的执行计划
EXPLAIN QUERY PLAN
SELECT
orders.id AS order_id,
users.username,
products.name AS product_name,
order_items.quantity,
order_items.price
FROM
orders
JOIN
users ON orders.user_id = users.id
JOIN
order_items ON orders.id = order_items.order_id
JOIN
products ON order_items.product_id = products.id;
-- 分组查询的执行计划
EXPLAIN QUERY PLAN
SELECT
user_id,
COUNT(*) AS order_count,
SUM(total) AS total_spent
FROM
orders
GROUP BY
user_id;
-- 排序查询的执行计划
EXPLAIN QUERY PLAN SELECT * FROM products ORDER BY price DESC;
-- 子查询的执行计划
EXPLAIN QUERY PLAN
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE total > 1000);
Expressions
LIKE, GLOB, REGEXP, MATCH, and extract operators
-- LIKE操作符测试
-- 搜索用户名以 'john' 开头的用户
SELECT * FROM users WHERE username LIKE 'john%';
-- 搜索邮箱以 'example.com' 结尾的用户
SELECT * FROM users WHERE email LIKE '%@example.com';
-- GLOB操作符测试
-- 搜索用户名以 'J' 或 'j' 开头的用户
SELECT * FROM users WHERE username GLOB '[Jj]*';
-- 搜索用户名为三个字符长,且以 'j' 开头的用户
SELECT * FROM users WHERE username GLOB 'j??';
-- REGEXP操作符测试
-- 搜索用户名以 'john' 开头的用户
SELECT * FROM users WHERE username REGEXP '^john';
-- 搜索用户名包含至少一个数字的用户
SELECT * FROM users WHERE username REGEXP '[0-9]';
-- MATCH操作符测试(仅在全文搜索虚拟表上有效)
-- 假设我们有一个全文搜索虚拟表名为 'products_fts'
-- 搜索产品描述中包含 'high-end' 的产品
SELECT * FROM products_fts WHERE products_fts MATCH 'high-end';
-- extract操作符测试
-- 提取用户电子邮件地址中的域名部分
SELECT email, SUBSTR(email, INSTR(email, '@') + 1) AS domain FROM users;
BETWEEN AND
-- 测试BETWEEN操作符
-- 1. 测试数字范围
SELECT * FROM products WHERE price BETWEEN 1000 AND 2000;
-- 2. 测试日期范围
SELECT * FROM orders WHERE created_at BETWEEN '2024-01-01' AND '2024-01-31';
-- 3. 测试字符串范围(按字母顺序)
SELECT * FROM users WHERE username BETWEEN 'a' AND 'm';
-- 4. 测试NULL值范围
SELECT * FROM users WHERE email BETWEEN 'a@example.com' AND 'm@example.com';
-- 5. 测试排除边界值
SELECT * FROM products WHERE price BETWEEN 1000 AND 2000 AND price NOT BETWEEN 1500 AND 1800;
-- 6. 测试反向范围
SELECT * FROM orders WHERE created_at BETWEEN '2024-01-31' AND '2024-01-01';
-- 7. 测试非数值、非日期、非字符串类型的列
-- 假设有一列存储用户积分
SELECT * FROM users WHERE points BETWEEN 100 AND 200;
-- 8. 测试包含NULL的范围
SELECT * FROM users WHERE email BETWEEN 'a@example.com' AND 'z@example.com';
-- 9. 测试混合数据类型的范围(SQLite 在比较不同类型时会进行自动转换)
-- 假设有一列存储用户年龄
SELECT * FROM users WHERE age BETWEEN 18 AND '30';
-- 10. 测试范围包含边界值的情况
SELECT * FROM products WHERE price BETWEEN 1000 AND 2000 AND (price = 1000 OR price = 2000);
CASE
-- 测试 CASE 语法
-- 简单 CASE 表达式
SELECT
id,
CASE
WHEN price > 1000 THEN 'Expensive'
WHEN price <= 1000 THEN 'Affordable'
ELSE 'Unknown'
END AS price_category
FROM
products;
-- CASE 表达式中的聚合函数
SELECT
user_id,
COUNT(*) AS order_count,
CASE
WHEN COUNT(*) > 5 THEN 'Frequent'
WHEN COUNT(*) <= 5 THEN 'Occasional'
ELSE 'Unknown'
END AS order_frequency
FROM
orders
GROUP BY
user_id;
-- CASE 表达式中的子查询
SELECT
id,
(CASE
WHEN (SELECT COUNT(*) FROM order_items WHERE order_id = orders.id) > 0 THEN 'Has Items'
ELSE 'No Items'
END) AS order_status
FROM
orders;
-- CASE 表达式中的多条件
SELECT
id,
CASE
WHEN stock > 10 AND price < 1000 THEN 'In Stock and Affordable'
WHEN stock > 10 AND price >= 1000 THEN 'In Stock but Expensive'
WHEN stock <= 10 THEN 'Low Stock'
ELSE 'Unknown'
END AS product_status
FROM
products;
-- CASE 表达式中的嵌套
SELECT
id,
CASE
WHEN stock > 10 THEN
CASE
WHEN price < 1000 THEN 'Affordable'
WHEN price >= 1000 THEN 'Expensive'
ELSE 'Unknown'
END
ELSE 'Low Stock'
END AS product_status
FROM
products;
IN and NOT IN
-- 测试IN语法:查询用户名为'alice'和'bob'的用户
SELECT * FROM users WHERE username IN ('alice', 'bob');
-- 测试NOT IN语法:查询用户名不为'alice'和'bob'的用户
SELECT * FROM users WHERE username NOT IN ('alice', 'bob');
-- 测试IN语法:查询购买了产品1和产品2的订单
SELECT * FROM orders WHERE id IN (SELECT order_id FROM order_items WHERE product_id IN (1, 2));
-- 测试NOT IN语法:查询未购买产品1和产品2的订单
SELECT * FROM orders WHERE id NOT IN (SELECT order_id FROM order_items WHERE product_id IN (1, 2));
-- 测试IN语法:查询购买了产品名称为'Laptop'和'Mouse'的订单
SELECT * FROM orders WHERE id IN (SELECT order_id FROM order_items WHERE product_id IN (SELECT id FROM products WHERE name IN ('Laptop', 'Mouse')));
-- 测试NOT IN语法:查询未购买产品名称为'Laptop'和'Mouse'的订单
SELECT * FROM orders WHERE id NOT IN (SELECT order_id FROM order_items WHERE product_id IN (SELECT id FROM products WHERE name IN ('Laptop', 'Mouse')));
表列名
-- 测试常规列名
SELECT id, username, email FROM users;
-- 测试特殊列名"ROWID"
SELECT ROWID, username, email FROM users;
-- 测试特殊列名"OID"
-- 在SQLite中,"OID"是"ROWID"的别名,因此使用"ROWID"和"OID"是等价的
SELECT OID, username, email FROM users;
-- 测试特殊列名"_ROWID_"
-- "_ROWID_"是"ROWID"的别名,用于WITHOUT ROWID表中
-- 但由于我们的表没有使用WITHOUT ROWID,所以在这里使用"ROWID"即可
SELECT _ROWID_, username, email FROM users;
EXISTS
-- 检查是否存在符合条件的记录
SELECT
id,
username
FROM
users
WHERE
EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id);
-- 检查是否存在符合条件的记录,并且用户名不是 "admin"
SELECT
id,
username
FROM
users
WHERE
EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id)
AND username != 'admin';
-- 检查是否存在符合条件的记录,并且订单金额大于 1000
SELECT
id,
username
FROM
users
WHERE
EXISTS (
SELECT 1
FROM orders
WHERE orders.user_id = users.id
AND orders.total > 1000
);
-- 检查是否存在用户没有下过订单的情况
SELECT
id,
username
FROM
users
WHERE
NOT EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id);
-- 检查是否存在用户没有下过订单的情况,并且用户名不是 "guest"
SELECT
id,
username
FROM
users
WHERE
NOT EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id)
AND username != 'guest';
子查询
-- 子查询测试
-- 1. 简单的子查询,选择用户名和订单数
SELECT
username,
(SELECT COUNT(*) FROM orders WHERE orders.user_id = users.id) AS order_count
FROM
users;
-- 2. 带条件的子查询,选择用户名和该用户最近的订单总额
SELECT
username,
(SELECT SUM(total) FROM orders WHERE orders.user_id = users.id ORDER BY created_at DESC LIMIT 1) AS latest_order_total
FROM
users;
-- 3. 子查询与多表联接结合,选择用户名、产品名称和该用户购买过的产品数
SELECT
u.username,
p.name AS product_name,
(SELECT COUNT(*)
FROM order_items
WHERE order_items.product_id = p.id
AND order_items.order_id IN (SELECT id FROM orders WHERE orders.user_id = u.id)
) AS product_count
FROM
users u
JOIN
orders o ON u.id = o.user_id
JOIN
order_items oi ON o.id = oi.order_id
JOIN
products p ON oi.product_id = p.id;
-- 4. 子查询中使用聚合函数,选择用户名和该用户平均每笔订单的总额
SELECT
username,
(SELECT AVG(total) FROM orders WHERE orders.user_id = users.id) AS avg_order_total
FROM
users;
-- 5. 子查询中的条件查询,选择购买过产品ID为1的用户列表
SELECT
username
FROM
users
WHERE
EXISTS (SELECT 1 FROM order_items WHERE order_items.product_id = 1 AND order_items.order_id IN (SELECT id FROM orders WHERE orders.user_id = users.id));
关联子查询
-- 查询所有用户的用户名及其最近一次订单的订单号和总金额
SELECT
u.username,
o.id AS latest_order_id,
o.total AS latest_order_total
FROM
users u
LEFT JOIN
(
SELECT
user_id,
id,
total
FROM
orders
WHERE
id = (
SELECT
MAX(id)
FROM
orders
WHERE
user_id = u.id
)
) AS o ON u.id = o.user_id;
-- 查询每个产品的名称以及被购买过的总次数
SELECT
p.name,
(
SELECT
COUNT(*)
FROM
order_items oi
WHERE
oi.product_id = p.id
) AS purchase_count
FROM
products p;
-- 查询每个用户的用户名以及其购买的产品总数量
SELECT
u.username,
(
SELECT
SUM(oi.quantity)
FROM
orders o
JOIN
order_items oi ON o.id = oi.order_id
WHERE
o.user_id = u.id
) AS total_quantity
FROM
users u;
CAST 表达式
-- 测试 CAST 表达式语法
-- 创建一个用于测试的临时表
CREATE TEMP TABLE test_cast (
id INTEGER PRIMARY KEY,
value TEXT
);
-- 插入一些测试数据
INSERT INTO test_cast (value) VALUES ('123'), ('3.14'), ('abc'), ('2022-05-21');
-- 测试 NONE CAST
SELECT value, CAST(value AS NONE) AS casted_value_none FROM test_cast;
-- 测试 TEXT CAST
SELECT value, CAST(value AS TEXT) AS casted_value_text FROM test_cast;
-- 测试 REAL CAST
SELECT value, CAST(value AS REAL) AS casted_value_real FROM test_cast;
-- 测试 INTEGER CAST
SELECT value, CAST(value AS INTEGER) AS casted_value_integer FROM test_cast;
-- 测试 NUMERIC CAST
SELECT value, CAST(value AS NUMERIC) AS casted_value_numeric FROM test_cast;
-- 删除临时表
DROP TABLE test_cast;
BOOL 表达式
SQL语言有几个上下文,在这些上下文中计算表达式并将结果转换为布尔值(true或false)。这些上下文是:
- SELECT、UPDATE或DELETE语句的WHERE子句,
- SELECT语句中联接的ON或USING子句,
- SELECT语句的HAVING子句,
- SQL触发器的WHEN子句,
- 以及
WHEN子句或某些CASE表达式的子句。
-- WHERE clause of a SELECT statement
SELECT * FROM users WHERE id = 1;
-- WHERE clause of an UPDATE statement
UPDATE products SET stock = stock - 1 WHERE id = 5;
-- WHERE clause of a DELETE statement
DELETE FROM orders WHERE total > 1000;
-- ON clause of a join in a SELECT statement
SELECT *
FROM orders
JOIN users ON orders.user_id = users.id;
-- USING clause of a join in a SELECT statement
SELECT *
FROM orders
JOIN users USING (user_id);
-- HAVING clause of a SELECT statement
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
HAVING COUNT(*) > 2;
-- WHEN clause of an SQL trigger
-- Assuming a trigger exists
CREATE TRIGGER update_stock_trigger
AFTER INSERT ON order_items
BEGIN
UPDATE products SET stock = stock - NEW.quantity WHERE id = NEW.product_id;
END;
-- WHEN clause of a CASE expression
SELECT id, name,
CASE
WHEN price > 1000 THEN 'Expensive'
WHEN price > 500 THEN 'Moderate'
ELSE 'Cheap'
END AS price_category
FROM products;
函数
-- 1. 测试字符串函数
-- 字符串连接函数 CONCAT
SELECT CONCAT(username, '@example.com') AS email FROM users;
-- 字符串长度函数 LENGTH
SELECT username, LENGTH(username) AS username_length FROM users;
-- 子字符串函数 SUBSTR
SELECT username, SUBSTR(username, 1, 3) AS username_substr FROM users;
-- 2. 测试数值函数
-- 求和函数 SUM
SELECT SUM(price) AS total_price FROM products;
-- 平均值函数 AVG
SELECT AVG(price) AS avg_price FROM products;
-- 最大值函数 MAX
SELECT MAX(price) AS max_price FROM products;
-- 最小值函数 MIN
SELECT MIN(price) AS min_price FROM products;
-- 绝对值函数 ABS
SELECT ABS(-10) AS absolute_value;
-- 3. 测试日期时间函数
-- 当前日期时间函数 CURRENT_TIMESTAMP
SELECT CURRENT_TIMESTAMP AS current_time;
-- 日期时间格式化函数 STRFTIME
SELECT STRFTIME('%Y-%m-%d %H:%M:%S', 'now') AS formatted_time;
-- 日期时间计算函数 DATE
SELECT DATE('now') AS current_date;
-- 日期时间差函数 DATEDIFF
SELECT DATEDIFF('2024-05-21', '2024-05-01') AS days_diff;
-- 4. 测试条件函数
-- CASE 表达式
SELECT
CASE
WHEN price > 1000 THEN 'Expensive'
ELSE 'Affordable'
END AS price_category
FROM
products;
-- 5. 测试聚合函数
-- COUNT 函数
SELECT COUNT(*) AS user_count FROM users;
-- GROUP_CONCAT 函数
SELECT user_id, GROUP_CONCAT(product_id) AS ordered_products FROM orders GROUP BY user_id;
INDEXED BY
INDEXED BY 短语强制 SQLite 查询规划器使用 DELETE、SELECT 或 UPDATE 语句上的特定命名索引。 INDEXED BY 短语是一个 SQLite 扩展,并且 不能移植到其他 SQL 数据库引擎。
-- 创建索引
CREATE INDEX idx_users_username ON users (username);
CREATE INDEX idx_products_name ON products (name);
-- 使用索引进行选择查询
SELECT * FROM users INDEXED BY idx_users_username WHERE username = 'john_doe';
-- 使用索引进行排序查询
SELECT * FROM products INDEXED BY idx_products_name ORDER BY name ASC;
-- 使用联合索引进行条件查询
SELECT * FROM order_items INDEXED BY order_items_order_id_product_id WHERE order_id = 1 AND product_id = 1;
-- 使用索引进行连接查询
SELECT
orders.id AS order_id,
users.username,
products.name AS product_name,
order_items.quantity,
order_items.price
FROM
orders INDEXED BY orders_user_id
JOIN
users ON orders.user_id = users.id
JOIN
order_items ON orders.id = order_items.order_id
JOIN
products ON order_items.product_id = products.id;
-- 使用索引进行分组查询
SELECT
user_id,
COUNT(*) AS order_count,
SUM(total) AS total_spent
FROM
orders INDEXED BY orders_user_id
GROUP BY
user_id;
-- 使用索引进行子查询
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders INDEXED BY orders_user_id WHERE total > 1000);
INSERT
INSERT 语句有三种基本形式。
INSERT INTO table VALUES(…);
INSERT INTO table SELECT …;
INSERT INTO* table DEFAULT VALUES;
-- 测试第一种形式:INSERT INTO table VALUES(...);
-- 向 users 表插入一条用户记录
INSERT INTO users VALUES (1, 'john_doe', 'securepassword', 'john@example.com', '2024-05-21 12:00:00');
-- 测试第二种形式:INSERT INTO table SELECT ...;
-- 向 products 表插入来自另一个表的数据
INSERT INTO products SELECT * FROM products_backup;
-- 测试第三种形式:INSERT INTO table DEFAULT VALUES;
-- 向 orders 表插入一条默认值记录
INSERT INTO orders DEFAULT VALUES;
Keywords
SQL标准规定了大量的关键字,这些关键字不能用作表、索引、列、数据库、用户定义函数、排序规则、虚拟表模块或任何其他命名对象的名称。
如果要将关键字用作名称,您需要对其进行引用。SQLite有四种引用关键字的方法:
- ‘keyword’ 单引号中的关键字是字符串字面值。
- “keyword” 双引号中的关键字是标识符。
- [keyword] 方括号中的关键字是标识符。这不是标准SQL。这种引用机制由MS Access和SQL Server使用,并包含在SQLite中以确保兼容性。
keyword
用反引号(ASCII代码96)括起来的关键字是标识符。这不是标准SQL。这种引用机制由MySQL使用,并包含在SQLite中以确保兼容性。为了在面对历史SQL语句时具有弹性,SQLite有时会放宽上述引用规则:
- 如果在允许标识符但不允许字符串字面值的上下文中使用单引号中的关键字(例如:‘key’或’glob’),那么该标记将被理解为标识符而不是字符串字面值。
- 如果在无法解析为标识符但允许字符串字面值的上下文中使用双引号中的关键字(例如:“key"或"glob”),那么该标记将被理解为字符串字面值而不是标识符。
程序员被告诫不要使用前述两个例外情况。我们强调它们仅存在是为了确保旧的和格式不正确的SQL语句能够正确运行。未来的SQLite版本可能会引发错误,而不是接受上述例外情况覆盖的格式不正确的语句。
下面列出了任何SQLite版本中可能使用的所有关键字,而不考虑编译时选项。大多数合理的配置使用大多数或所有这些关键字,但在禁用SQL语言功能时可能会省略某些关键字。无论编译时配置如何,任何不在以下147个元素列表中的标识符都不是SQLite中SQL解析器的关键字:
The ON CONFLICT Clause
ON CONFLICT 子句适用于 UNIQUE、NOT NULL、CHECK 和 PRIMARY KEY 约束。 ON CONFLICT 算法没有 应用于 FOREIGN KEY 约束。
-- 在 users 表中创建一个具有唯一约束的示例表
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL
);
-- 尝试插入一个已存在的用户名,预期结果:事务回滚,没有新行被插入
INSERT INTO users (username, password) VALUES ('john_doe', 'password1');
INSERT INTO users (username, password) VALUES ('john_doe', 'password2') ON CONFLICT(ROLLBACK);
-- 尝试插入一个已存在的用户名,预期结果:事务回滚,没有新行被插入
INSERT INTO users (username, password) VALUES ('jane_doe', 'password3');
INSERT INTO users (username, password) VALUES ('jane_doe', 'password4') ON CONFLICT(ABORT);
-- 尝试插入一个已存在的用户名,预期结果:INSERT 操作失败,返回错误信息
INSERT INTO users (username, password) VALUES ('mary_smith', 'password5');
INSERT INTO users (username, password) VALUES ('mary_smith', 'password6') ON CONFLICT(FAIL);
-- 尝试插入一个已存在的用户名,预期结果:忽略插入冲突,不触发错误
INSERT INTO users (username, password) VALUES ('mark_johnson', 'password7');
INSERT INTO users (username, password) VALUES ('mark_johnson', 'password8') ON CONFLICT(IGNORE);
-- 尝试插入一个已存在的用户名,预期结果:替换已存在的行
INSERT INTO users (username, password) VALUES ('sarah_williams', 'password9');
INSERT INTO users (username, password) VALUES ('sarah_williams', 'password10') ON CONFLICT(REPLACE);
-- 显示 users 表中的内容,以验证上述操作的结果
SELECT * FROM users;
PRAGMA
PRAGMA 语句是 SQLite 特有的 SQL 扩展,用于修改 SQLite 库的操作或查询 SQLite 库的内部(非表格)数据。PRAGMA 语句使用与其他 SQLite 命令(例如 SELECT、INSERT)相同的接口发出,但在以下重要方面有所不同:
- PRAGMA 命令特定于 SQLite,与任何其他 SQL 数据库引擎不兼容。
- 未来的 SQLite 版本可能会删除特定的 PRAGMA 语句并添加其他语句。不能保证向后兼容性。
- 如果发出未知的 PRAGMA,不会生成错误消息。未知的 PRAGMA 会被简单地忽略。这意味着如果 PRAGMA 语句中有拼写错误,库不会向用户提示这一事实。
- 一些 PRAGMA 在 SQL 编译阶段生效,而不是执行阶段。这意味着如果使用 C 语言的 sqlite3_prepare()、sqlite3_step()、sqlite3_finalize() API(或类似的封装接口),PRAGMA 可能会在 sqlite3_prepare() 调用期间运行,而不是在 sqlite3_step() 调用期间像普通的 SQL 语句那样。或者 PRAGMA 可能会在 sqlite3_step() 期间运行,就像普通的 SQL 语句一样。PRAGMA 是否在 sqlite3_prepare() 或 sqlite3_step() 期间运行取决于 PRAGMA 和特定的 SQLite 发行版。
- SQL 语句的 EXPLAIN 和 EXPLAIN QUERY PLAN 前缀只影响 sqlite3_step() 期间语句的行为。这意味着在 sqlite3_prepare() 期间生效的 PRAGMA 语句无论是否以 “EXPLAIN” 为前缀都会表现相同。
- SQLite 的 C 语言 API 提供了 SQLITE_FCNTL_PRAGMA 文件控制,该控制允许 VFS 实现添加新的 PRAGMA 语句或覆盖内置 PRAGMA 语句的含义。
-- 创建一个测试表
CREATE TABLE test_table (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
-- PRAGMA语句测试
-- 打开数据库检查的PRAGMA设置
PRAGMA foreign_keys;
-- 设置PRAGMA,使得插入自动分配ID
PRAGMA autoincrement = 1;
-- 查看数据库版本信息
PRAGMA user_version;
-- 修改数据库版本信息
PRAGMA user_version = 2;
-- 查看数据库的schema
PRAGMA table_info(test_table);
-- 查看数据库中所有表的列表
PRAGMA table_info;
-- 检查数据库是否为可写状态
PRAGMA writable_schema;
-- 查看数据库中所有索引的列表
PRAGMA index_list(test_table);
-- 查看特定索引的详细信息
PRAGMA index_info(index_name);
-- 查看表的存储信息
PRAGMA page_count;
-- 查看表的空闲页数
PRAGMA freelist_count;
-- 查看表的最大页数
PRAGMA max_page_count;
-- 查看SQLite的配置参数
PRAGMA compile_options;
-- 查看SQLite的限制信息
PRAGMA limits;
-- 查看数据库中索引的状态
PRAGMA index_xinfo(index_name);
-- 查看数据库中各表的详细信息
PRAGMA database_list;
-- 查看数据库中的设置
PRAGMA database_list;
-- 查看数据库的自动VACUUM模式
PRAGMA auto_vacuum;
-- 设置数据库的自动VACUUM模式
PRAGMA auto_vacuum = FULL;
-- 查看数据库的递增计数器
PRAGMA autoincrement;
-- 查看数据库的缓存大小
PRAGMA cache_size;
-- 设置数据库的缓存大小
PRAGMA cache_size = 10000;
-- 查看数据库的缓存使用情况
PRAGMA cache_spill;
-- 查看数据库的编码
PRAGMA encoding;
-- 设置数据库的编码
PRAGMA encoding = 'UTF-8';
-- 查看数据库的递增计数器
PRAGMA foreign_key_check;
-- 开始数据库的递增计数器
PRAGMA foreign_key_check = ON;
-- 查看数据库的全局锁状态
PRAGMA locking_mode;
-- 设置数据库的全局锁状态
PRAGMA locking_mode = EXCLUSIVE;
-- 查看数据库的日志模式
PRAGMA journal_mode;
-- 设置数据库的日志模式
PRAGMA journal_mode = WAL;
-- 查看数据库的主页大小
PRAGMA page_size;
-- 设置数据库的主页大小
PRAGMA page_size = 4096;
-- 查看数据库的预留字节
PRAGMA reserved_bytes;
-- 设置数据库的预留字节
PRAGMA reserved_bytes = 1000;
-- 查看数据库的主键
PRAGMA primary_key;
-- 设置数据库的主键
PRAGMA primary_key = ON;
-- 查看数据库的同步状态
PRAGMA synchronous;
-- 设置数据库的同步状态
PRAGMA synchronous = NORMAL;
-- 查看数据库的表空间名称
PRAGMA tablespace_name;
-- 查看数据库的WAL模式
PRAGMA wal_checkpoint;
-- 设置数据库的WAL模式
PRAGMA wal_checkpoint(FULL);
REINDEX
- REINDEX命令的用途:REINDEX命令用于从头开始删除并重新创建索引。当排序序列的定义发生变化,或者涉及到函数的表达式的索引的定义发生变化时,这非常有用。
- REINDEX的使用方式:
- 如果REINDEX关键字后面没有排序序列或数据库对象标识符,则会重建所有附加数据库中的所有索引。
- 如果REINDEX关键字后面跟着一个排序序列名称,那么会重新创建所有使用该命名排序序列的所有附加数据库中的索引。
- 或者,如果REINDEX关键字后面跟着标识特定数据库表的参数,那么会重建附加到该数据库表的所有索引。如果标识特定数据库索引,则仅重新创建该索引。
- 对于形式为"REINDEX name"的命令,与排序名称的匹配优先于与索引名称或表名称的匹配。通过在重新索引特定表或索引时始终指定模式名称,可以避免语法上的这种歧义。
-- 创建一个索引以便后续测试
CREATE INDEX idx_username ON users(username);
-- 检查索引是否存在
PRAGMA index_list('users');
-- 重新构建索引
REINDEX idx_username;
-- 检查是否重新构建成功
PRAGMA index_info('idx_username');
RELEASE SAVEPOINT
SAVEPOINTs是一种创建事务的方法,类似于BEGIN和COMMIT,不同之处在于SAVEPOINT和RELEASE命令具有名称且可以嵌套使用。
- SAVEPOINT命令以名称开始一个新的事务。事务名称不必唯一。SAVEPOINT可以在BEGIN…COMMIT内部或外部开始。当SAVEPOINT是最外层保存点且不在BEGIN…COMMIT内时,行为与BEGIN DEFERRED TRANSACTION相同。
- ROLLBACK TO命令将数据库状态恢复到与相应SAVEPOINT刚创建时相同的状态。与普通的ROLLBACK命令(不带TO关键字)不同,ROLLBACK TO命令不会取消事务。相反,ROLLBACK TO命令会重新启动事务。但是,所有介于SAVEPOINT和ROLLBACK TO之间的SAVEPOINT将被取消。
- RELEASE命令类似于SAVEPOINT的COMMIT。RELEASE命令将导致所有保存点(包括最新的具有匹配名称的保存点)及之前的保存点从事务栈中删除。释放内部事务不会向数据库文件写入任何更改;它只是从事务堆栈中删除保存点,使得无法ROLLBACK TO这些保存点。如果RELEASE命令释放了最外层的保存点,使得事务堆栈为空,则RELEASE等同于COMMIT。即使事务最初是由SAVEPOINT命令而不是BEGIN命令开始的,COMMIT命令也可以用于释放所有保存点并提交事务。
- 如果RELEASE命令中的保存点名称与当前事务堆栈中的任何保存点都不匹配,则不会释放任何保存点,数据库保持不变,RELEASE命令返回错误。
- RELEASE命令的几种理解方式:
- 一些人认为RELEASE等同于SAVEPOINT的COMMIT。只要记住内部事务提交的更改可能会在外部事务中回滚即可。
- 另一种理解是RELEASE将命名事务合并到其父事务中,使得命名事务和其父事务成为同一个事务。RELEASE后,命名事务和其父事务将一起提交或回滚。
- 还可以将保存点视为事务时间轴上的“标记”。在这种观点下,SAVEPOINT命令创建一个新的标记,ROLLBACK TO命令将时间轴倒回到命名标记之后的一个点,RELEASE命令从时间轴中擦除标记,而不实际对数据库进行任何更改。
-- 创建一个 SAVEPOINT
SAVEPOINT sp1;
-- 在 SAVEPOINT 下执行一些插入操作
INSERT INTO users (username, password, email) VALUES ('alice', 'password123', 'alice@example.com');
INSERT INTO products (name, price, stock) VALUES ('Phone', 599.99, 20);
-- 查看保存点下的数据是否已经插入
SELECT * FROM users WHERE username = 'alice';
SELECT * FROM products WHERE name = 'Phone';
-- 如果需要回滚到保存点,则执行以下语句
-- ROLLBACK TO SAVEPOINT sp1;
-- 如果保存点下的操作全部成功,提交事务
-- COMMIT;
REPLACE
REPLACE
命令是INSERT OR REPLACE
的别名,用于在插入数据时,如果存在唯一约束或主键冲突,则替换已有记录。
-- 创建一个临时表用于测试
CREATE TEMP TABLE temp_users (
id INTEGER PRIMARY KEY,
username TEXT NOT NULL UNIQUE,
email TEXT NOT NULL UNIQUE
);
-- 插入一条数据
INSERT INTO temp_users (username, email) VALUES ('user1', 'user1@example.com');
-- 尝试插入一条相同的数据,但是email与现有记录冲突
INSERT INTO temp_users (username, email) VALUES ('user2', 'user1@example.com');
-- 查询已有数据,应该只有一条记录
SELECT * FROM temp_users;
-- 使用REPLACE语法插入相同的数据
REPLACE INTO temp_users (username, email) VALUES ('user2', 'user1@example.com');
-- 再次查询已有数据,现在应该只有一条记录,但是username应该被更新为'user2'
SELECT * FROM temp_users;
-- 清理测试用的临时表
DROP TABLE temp_users;
RETURNING clause
-- 在用户表中插入一条新记录,并返回插入的id和用户名
INSERT INTO users (username, password, email)
VALUES ('test_user', 'testpassword', 'test@example.com')
RETURNING id, username;
-- 更新产品表中的库存,并返回更新后的库存数量
UPDATE products
SET stock = stock - 1
WHERE id = 1
RETURNING stock;
-- 删除订单表中的一条记录,并返回被删除的订单ID和总金额
DELETE FROM orders
WHERE id = 1
RETURNING id, total;
-- 通过插入一条订单记录和订单项目记录来演示多条返回值
BEGIN TRANSACTION;
INSERT INTO orders (user_id, total)
VALUES (1, 1500.00);
INSERT INTO order_items (order_id, product_id, quantity, price)
VALUES (LAST_INSERT_ROWID(), 1, 1, 1500.00);
COMMIT;
SELECT o.id AS order_id, oi.id AS order_item_id
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
WHERE o.id = LAST_INSERT_ROWID();
UPSERT
UPSERT是在INSERT语句中添加的一个子句,如果插入操作违反了唯一性约束,则会使INSERT操作行为像UPDATE或不执行任何操作。UPSERT不是标准SQL。在SQLite中,UPSERT遵循PostgreSQL建立的语法,但有一些泛化。
UPSERT是一个普通的INSERT语句,后面跟着一个或多个ON CONFLICT子句,如上面的语法图中所示。
在"ON CONFLICT"和"DO"关键字之间的语法称为"冲突目标"。冲突目标指定了将触发upsert的唯一性约束。在INSERT语句中的最后一个ON CONFLICT子句中可以省略冲突目标,但对于所有其他ON CONFLICT子句都是必需的。
如果插入操作会导致冲突目标的唯一性约束失败,则插入会被省略,相应的DO NOTHING或DO UPDATE操作将代替执行。ON CONFLICT子句按指定的顺序检查。如果最后一个ON CONFLICT子句省略了冲突目标,则它将在任何前面的ON CONFLICT子句未捕获的唯一性约束失败时触发。
对于INSERT的每一行,只会执行一个ON CONFLICT子句,具体来说是与冲突目标匹配的第一个ON CONFLICT子句。当ON CONFLICT子句触发时,该行之后的所有ON CONFLICT子句都将被忽略。
对于多行插入,upsert决策是针对每一行单独进行的。
- UPSERT处理仅适用于唯一性约束。"唯一性约束"是在CREATE TABLE语句中明确指定的UNIQUE或PRIMARY KEY约束,或者是唯一索引。UPSERT不会干预失败的NOT NULL、CHECK或外键约束,也不会干预使用触发器实现的约束。
- DO UPDATE中表达式中的列名是插入前列的原始未更改的值。要使用如果约束未失败时将要插入的值,请将特殊的"excluded."表限定符添加到列名中。
-- 插入一条新记录,如果唯一索引或主键已存在则更新
INSERT INTO users (username, password, email)
VALUES ('john_doe', 'securepassword', 'john@example.com')
ON CONFLICT(username) DO UPDATE SET
password = 'newpassword', email = 'updated_john@example.com';
-- 期望结果:如果 'john_doe' 用户已存在,则更新密码和电子邮件
-- 插入一条新记录,如果唯一索引或主键已存在则忽略
INSERT OR IGNORE INTO users (username, password, email)
VALUES ('jane_doe', 'securepassword', 'jane@example.com');
-- 期望结果:如果 'jane_doe' 用户已存在,则忽略插入操作
-- 插入一条新记录,如果唯一索引或主键已存在则不执行任何操作
INSERT OR REPLACE INTO users (username, password, email)
VALUES ('jane_doe', 'newpassword', 'updated_jane@example.com');
-- 期望结果:如果 'jane_doe' 用户已存在,则替换现有记录的密码和电子邮件
-- 在插入新记录时,同时更新相关表的数据
INSERT INTO orders (user_id, total)
VALUES (
(SELECT id FROM users WHERE username = 'john_doe'),
1500.00
)
ON CONFLICT(user_id) DO NOTHING;
-- 期望结果:如果与 'john_doe' 相关的订单已存在,则不执行任何操作
-- 在插入新记录时,同时更新相关表的数据
INSERT INTO orders (user_id, total)
VALUES (
(SELECT id FROM users WHERE username = 'john_doe'),
1500.00
)
ON CONFLICT(user_id) DO UPDATE SET
total = total + 1500.00;
-- 期望结果:如果与 'john_doe' 相关的订单已存在,则更新订单总额
-- 在插入新记录时,如果用户已存在则更新密码和电子邮件,否则插入新用户
INSERT INTO users (username, password, email)
VALUES ('john_doe', 'securepassword', 'john@example.com')
ON CONFLICT(username) DO UPDATE SET
password = 'newpassword', email = 'updated_john@example.com'
WHERE username = 'john_doe';
-- 期望结果:如果 'john_doe' 用户已存在,则更新密码和电子邮件,否则插入新用户
-- 插入一条新记录,如果唯一索引或主键已存在则忽略
INSERT INTO users (username, password, email)
VALUES ('john_doe', 'securepassword', 'john@example.com')
ON CONFLICT(username) DO NOTHING;
-- 期望结果:如果 'john_doe' 用户已存在,则忽略插入操作
VACUUM
VACUUM命令重新构建数据库文件,将其压缩到最小的磁盘空间中。
-- 创建一个测试数据库(假设数据库文件名为test.db)
-- 这个数据库文件包含了之前定义的表结构
-- 打开测试数据库
ATTACH DATABASE 'test.db' AS test_db;
-- 插入一些数据以确保数据库文件有一定的大小
INSERT INTO users (username, password, email) VALUES ('test_user1', 'password1', 'test1@example.com');
INSERT INTO products (name, description, price, stock) VALUES ('Test Product 1', 'Test description', 10.99, 100);
INSERT INTO orders (user_id, total) VALUES (1, 10.99);
INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (1, 1, 1, 10.99);
-- 关闭数据库连接,确保数据被写入磁盘
DETACH DATABASE test_db;
-- 打开测试数据库
ATTACH DATABASE 'test.db' AS test_db;
-- 查询数据库文件大小(单位为字节)
SELECT 'Before VACUUM: ', page_count * page_size AS size_before_vacuum FROM pragma_page_count(), pragma_page_size();
-- 执行VACUUM命令来优化数据库文件大小
VACUUM;
-- 查询优化后的数据库文件大小
SELECT 'After VACUUM: ', page_count * page_size AS size_after_vacuum FROM pragma_page_count(), pragma_page_size();
-- 关闭数据库连接
DETACH DATABASE test_db;
####The WITH Clause
-- 创建测试数据
INSERT INTO users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
INSERT INTO users (username, password, email) VALUES ('user2', 'password2', 'user2@example.com');
INSERT INTO products (name, description, price, stock) VALUES ('Product 1', 'Description 1', 10.0, 100);
INSERT INTO products (name, description, price, stock) VALUES ('Product 2', 'Description 2', 20.0, 50);
INSERT INTO orders (user_id, total) VALUES (1, 30.0);
INSERT INTO orders (user_id, total) VALUES (2, 25.0);
INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (1, 1, 3, 30.0);
INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (2, 2, 1, 25.0);
-- 测试WITH子句
WITH total_spent AS (
SELECT user_id, SUM(total) AS total_spent FROM orders GROUP BY user_id
)
SELECT users.username, total_spent.total_spent
FROM users
JOIN total_spent ON users.id = total_spent.user_id;
- 或者,如果REINDEX关键字后面跟着标识特定数据库表的参数,那么会重建附加到该数据库表的所有索引。如果标识特定数据库索引,则仅重新创建该索引。
- 对于形式为"REINDEX name"的命令,与排序名称的匹配优先于与索引名称或表名称的匹配。通过在重新索引特定表或索引时始终指定模式名称,可以避免语法上的这种歧义。
-- 创建一个索引以便后续测试
CREATE INDEX idx_username ON users(username);
-- 检查索引是否存在
PRAGMA index_list('users');
-- 重新构建索引
REINDEX idx_username;
-- 检查是否重新构建成功
PRAGMA index_info('idx_username');
RELEASE SAVEPOINT
SAVEPOINTs是一种创建事务的方法,类似于BEGIN和COMMIT,不同之处在于SAVEPOINT和RELEASE命令具有名称且可以嵌套使用。
- SAVEPOINT命令以名称开始一个新的事务。事务名称不必唯一。SAVEPOINT可以在BEGIN…COMMIT内部或外部开始。当SAVEPOINT是最外层保存点且不在BEGIN…COMMIT内时,行为与BEGIN DEFERRED TRANSACTION相同。
- ROLLBACK TO命令将数据库状态恢复到与相应SAVEPOINT刚创建时相同的状态。与普通的ROLLBACK命令(不带TO关键字)不同,ROLLBACK TO命令不会取消事务。相反,ROLLBACK TO命令会重新启动事务。但是,所有介于SAVEPOINT和ROLLBACK TO之间的SAVEPOINT将被取消。
- RELEASE命令类似于SAVEPOINT的COMMIT。RELEASE命令将导致所有保存点(包括最新的具有匹配名称的保存点)及之前的保存点从事务栈中删除。释放内部事务不会向数据库文件写入任何更改;它只是从事务堆栈中删除保存点,使得无法ROLLBACK TO这些保存点。如果RELEASE命令释放了最外层的保存点,使得事务堆栈为空,则RELEASE等同于COMMIT。即使事务最初是由SAVEPOINT命令而不是BEGIN命令开始的,COMMIT命令也可以用于释放所有保存点并提交事务。
- 如果RELEASE命令中的保存点名称与当前事务堆栈中的任何保存点都不匹配,则不会释放任何保存点,数据库保持不变,RELEASE命令返回错误。
- RELEASE命令的几种理解方式:
- 一些人认为RELEASE等同于SAVEPOINT的COMMIT。只要记住内部事务提交的更改可能会在外部事务中回滚即可。
- 另一种理解是RELEASE将命名事务合并到其父事务中,使得命名事务和其父事务成为同一个事务。RELEASE后,命名事务和其父事务将一起提交或回滚。
- 还可以将保存点视为事务时间轴上的“标记”。在这种观点下,SAVEPOINT命令创建一个新的标记,ROLLBACK TO命令将时间轴倒回到命名标记之后的一个点,RELEASE命令从时间轴中擦除标记,而不实际对数据库进行任何更改。
-- 创建一个 SAVEPOINT
SAVEPOINT sp1;
-- 在 SAVEPOINT 下执行一些插入操作
INSERT INTO users (username, password, email) VALUES ('alice', 'password123', 'alice@example.com');
INSERT INTO products (name, price, stock) VALUES ('Phone', 599.99, 20);
-- 查看保存点下的数据是否已经插入
SELECT * FROM users WHERE username = 'alice';
SELECT * FROM products WHERE name = 'Phone';
-- 如果需要回滚到保存点,则执行以下语句
-- ROLLBACK TO SAVEPOINT sp1;
-- 如果保存点下的操作全部成功,提交事务
-- COMMIT;
REPLACE
REPLACE
命令是INSERT OR REPLACE
的别名,用于在插入数据时,如果存在唯一约束或主键冲突,则替换已有记录。
-- 创建一个临时表用于测试
CREATE TEMP TABLE temp_users (
id INTEGER PRIMARY KEY,
username TEXT NOT NULL UNIQUE,
email TEXT NOT NULL UNIQUE
);
-- 插入一条数据
INSERT INTO temp_users (username, email) VALUES ('user1', 'user1@example.com');
-- 尝试插入一条相同的数据,但是email与现有记录冲突
INSERT INTO temp_users (username, email) VALUES ('user2', 'user1@example.com');
-- 查询已有数据,应该只有一条记录
SELECT * FROM temp_users;
-- 使用REPLACE语法插入相同的数据
REPLACE INTO temp_users (username, email) VALUES ('user2', 'user1@example.com');
-- 再次查询已有数据,现在应该只有一条记录,但是username应该被更新为'user2'
SELECT * FROM temp_users;
-- 清理测试用的临时表
DROP TABLE temp_users;
RETURNING clause
-- 在用户表中插入一条新记录,并返回插入的id和用户名
INSERT INTO users (username, password, email)
VALUES ('test_user', 'testpassword', 'test@example.com')
RETURNING id, username;
-- 更新产品表中的库存,并返回更新后的库存数量
UPDATE products
SET stock = stock - 1
WHERE id = 1
RETURNING stock;
-- 删除订单表中的一条记录,并返回被删除的订单ID和总金额
DELETE FROM orders
WHERE id = 1
RETURNING id, total;
-- 通过插入一条订单记录和订单项目记录来演示多条返回值
BEGIN TRANSACTION;
INSERT INTO orders (user_id, total)
VALUES (1, 1500.00);
INSERT INTO order_items (order_id, product_id, quantity, price)
VALUES (LAST_INSERT_ROWID(), 1, 1, 1500.00);
COMMIT;
SELECT o.id AS order_id, oi.id AS order_item_id
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
WHERE o.id = LAST_INSERT_ROWID();
UPSERT
UPSERT是在INSERT语句中添加的一个子句,如果插入操作违反了唯一性约束,则会使INSERT操作行为像UPDATE或不执行任何操作。UPSERT不是标准SQL。在SQLite中,UPSERT遵循PostgreSQL建立的语法,但有一些泛化。
UPSERT是一个普通的INSERT语句,后面跟着一个或多个ON CONFLICT子句,如上面的语法图中所示。
在"ON CONFLICT"和"DO"关键字之间的语法称为"冲突目标"。冲突目标指定了将触发upsert的唯一性约束。在INSERT语句中的最后一个ON CONFLICT子句中可以省略冲突目标,但对于所有其他ON CONFLICT子句都是必需的。
如果插入操作会导致冲突目标的唯一性约束失败,则插入会被省略,相应的DO NOTHING或DO UPDATE操作将代替执行。ON CONFLICT子句按指定的顺序检查。如果最后一个ON CONFLICT子句省略了冲突目标,则它将在任何前面的ON CONFLICT子句未捕获的唯一性约束失败时触发。
对于INSERT的每一行,只会执行一个ON CONFLICT子句,具体来说是与冲突目标匹配的第一个ON CONFLICT子句。当ON CONFLICT子句触发时,该行之后的所有ON CONFLICT子句都将被忽略。
对于多行插入,upsert决策是针对每一行单独进行的。
- UPSERT处理仅适用于唯一性约束。"唯一性约束"是在CREATE TABLE语句中明确指定的UNIQUE或PRIMARY KEY约束,或者是唯一索引。UPSERT不会干预失败的NOT NULL、CHECK或外键约束,也不会干预使用触发器实现的约束。
- DO UPDATE中表达式中的列名是插入前列的原始未更改的值。要使用如果约束未失败时将要插入的值,请将特殊的"excluded."表限定符添加到列名中。
-- 插入一条新记录,如果唯一索引或主键已存在则更新
INSERT INTO users (username, password, email)
VALUES ('john_doe', 'securepassword', 'john@example.com')
ON CONFLICT(username) DO UPDATE SET
password = 'newpassword', email = 'updated_john@example.com';
-- 期望结果:如果 'john_doe' 用户已存在,则更新密码和电子邮件
-- 插入一条新记录,如果唯一索引或主键已存在则忽略
INSERT OR IGNORE INTO users (username, password, email)
VALUES ('jane_doe', 'securepassword', 'jane@example.com');
-- 期望结果:如果 'jane_doe' 用户已存在,则忽略插入操作
-- 插入一条新记录,如果唯一索引或主键已存在则不执行任何操作
INSERT OR REPLACE INTO users (username, password, email)
VALUES ('jane_doe', 'newpassword', 'updated_jane@example.com');
-- 期望结果:如果 'jane_doe' 用户已存在,则替换现有记录的密码和电子邮件
-- 在插入新记录时,同时更新相关表的数据
INSERT INTO orders (user_id, total)
VALUES (
(SELECT id FROM users WHERE username = 'john_doe'),
1500.00
)
ON CONFLICT(user_id) DO NOTHING;
-- 期望结果:如果与 'john_doe' 相关的订单已存在,则不执行任何操作
-- 在插入新记录时,同时更新相关表的数据
INSERT INTO orders (user_id, total)
VALUES (
(SELECT id FROM users WHERE username = 'john_doe'),
1500.00
)
ON CONFLICT(user_id) DO UPDATE SET
total = total + 1500.00;
-- 期望结果:如果与 'john_doe' 相关的订单已存在,则更新订单总额
-- 在插入新记录时,如果用户已存在则更新密码和电子邮件,否则插入新用户
INSERT INTO users (username, password, email)
VALUES ('john_doe', 'securepassword', 'john@example.com')
ON CONFLICT(username) DO UPDATE SET
password = 'newpassword', email = 'updated_john@example.com'
WHERE username = 'john_doe';
-- 期望结果:如果 'john_doe' 用户已存在,则更新密码和电子邮件,否则插入新用户
-- 插入一条新记录,如果唯一索引或主键已存在则忽略
INSERT INTO users (username, password, email)
VALUES ('john_doe', 'securepassword', 'john@example.com')
ON CONFLICT(username) DO NOTHING;
-- 期望结果:如果 'john_doe' 用户已存在,则忽略插入操作
VACUUM
VACUUM命令重新构建数据库文件,将其压缩到最小的磁盘空间中。
-- 创建一个测试数据库(假设数据库文件名为test.db)
-- 这个数据库文件包含了之前定义的表结构
-- 打开测试数据库
ATTACH DATABASE 'test.db' AS test_db;
-- 插入一些数据以确保数据库文件有一定的大小
INSERT INTO users (username, password, email) VALUES ('test_user1', 'password1', 'test1@example.com');
INSERT INTO products (name, description, price, stock) VALUES ('Test Product 1', 'Test description', 10.99, 100);
INSERT INTO orders (user_id, total) VALUES (1, 10.99);
INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (1, 1, 1, 10.99);
-- 关闭数据库连接,确保数据被写入磁盘
DETACH DATABASE test_db;
-- 打开测试数据库
ATTACH DATABASE 'test.db' AS test_db;
-- 查询数据库文件大小(单位为字节)
SELECT 'Before VACUUM: ', page_count * page_size AS size_before_vacuum FROM pragma_page_count(), pragma_page_size();
-- 执行VACUUM命令来优化数据库文件大小
VACUUM;
-- 查询优化后的数据库文件大小
SELECT 'After VACUUM: ', page_count * page_size AS size_after_vacuum FROM pragma_page_count(), pragma_page_size();
-- 关闭数据库连接
DETACH DATABASE test_db;
####The WITH Clause
-- 创建测试数据
INSERT INTO users (username, password, email) VALUES ('user1', 'password1', 'user1@example.com');
INSERT INTO users (username, password, email) VALUES ('user2', 'password2', 'user2@example.com');
INSERT INTO products (name, description, price, stock) VALUES ('Product 1', 'Description 1', 10.0, 100);
INSERT INTO products (name, description, price, stock) VALUES ('Product 2', 'Description 2', 20.0, 50);
INSERT INTO orders (user_id, total) VALUES (1, 30.0);
INSERT INTO orders (user_id, total) VALUES (2, 25.0);
INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (1, 1, 3, 30.0);
INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (2, 2, 1, 25.0);
-- 测试WITH子句
WITH total_spent AS (
SELECT user_id, SUM(total) AS total_spent FROM orders GROUP BY user_id
)
SELECT users.username, total_spent.total_spent
FROM users
JOIN total_spent ON users.id = total_spent.user_id;