MySQL查询语句(续)

第4章:MySQL查询语句(续)

4.3 高级功能

MySQL提供了许多高级功能,可以增强数据库的性能、安全性和功能性。本节将详细介绍索引、视图、存储过程、触发器、事务等高级功能。

4.3.1 索引

索引是提高数据库查询性能的关键工具。它们类似于书籍的目录,允许数据库系统快速定位所需的数据,而无需扫描整个表。

索引的类型

MySQL支持多种类型的索引:

  1. B-Tree索引:最常用的索引类型,适用于全键值、键值范围和键前缀查询
  2. 哈希索引:只适用于等值比较,主要用于MEMORY存储引擎
  3. 全文索引:用于全文搜索
  4. 空间索引:用于地理空间数据类型
  5. 前缀索引:对字符串列的前几个字符建立索引
  6. 复合索引:包含多个列的索引
创建索引

可以在创建表时或之后创建索引:

sql 复制代码
-- 在创建表时创建索引
CREATE TABLE employees (
    employee_id INT PRIMARY KEY,
    first_name VARCHAR(50),
    last_name VARCHAR(50),
    email VARCHAR(100),
    hire_date DATE,
    INDEX idx_name (first_name, last_name),
    UNIQUE INDEX idx_email (email)
);

-- 在现有表上创建索引
CREATE INDEX idx_hire_date ON employees (hire_date);
CREATE UNIQUE INDEX idx_emp_email ON employees (email);

-- 使用ALTER TABLE创建索引
ALTER TABLE employees ADD INDEX idx_last_name (last_name);
创建前缀索引

对于长字符串列,可以只索引前几个字符:

sql 复制代码
CREATE INDEX idx_description ON products (description(50));

这将为description列的前50个字符创建索引。

创建全文索引

全文索引用于全文搜索:

sql 复制代码
-- 在创建表时创建全文索引
CREATE TABLE articles (
    id INT PRIMARY KEY,
    title VARCHAR(200),
    content TEXT,
    FULLTEXT INDEX idx_content (content)
);

-- 在现有表上创建全文索引
CREATE FULLTEXT INDEX idx_title_content ON articles (title, content);

使用全文索引进行搜索:

sql 复制代码
-- 自然语言模式
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('database optimization');

-- 布尔模式
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('+database -optimization' IN BOOLEAN MODE);
查看索引

可以使用以下命令查看表的索引:

sql 复制代码
-- 方法1
SHOW INDEX FROM employees;

-- 方法2
SHOW INDEXES FROM employees;

-- 方法3
SHOW KEYS FROM employees;

也可以从INFORMATION_SCHEMA查询索引信息:

sql 复制代码
SELECT index_name, column_name, non_unique
FROM INFORMATION_SCHEMA.STATISTICS
WHERE table_schema = 'your_database' AND table_name = 'employees';
删除索引

可以使用以下命令删除索引:

sql 复制代码
-- 方法1
DROP INDEX idx_name ON employees;

-- 方法2
ALTER TABLE employees DROP INDEX idx_name;
索引的使用场景

索引适用于以下场景:

  1. WHERE子句中频繁使用的列
  2. JOIN操作中的连接列
  3. ORDER BY或GROUP BY子句中的列
  4. 包含大量唯一值的列
索引的注意事项

虽然索引可以提高查询性能,但也有一些注意事项:

  1. 索引会占用额外的存储空间
  2. 索引会降低写操作(INSERT、UPDATE、DELETE)的性能
  3. 不是所有查询都能使用索引
  4. 过多的索引可能导致优化器选择次优的执行计划
索引最佳实践

以下是一些索引使用的最佳实践:

  1. 为经常在WHERE子句中使用的列创建索引
  2. 为经常在ORDER BY和GROUP BY子句中使用的列创建索引
  3. 为外键列创建索引
  4. 考虑列的基数(唯一值的数量)
  5. 避免对经常更新的列创建过多索引
  6. 使用EXPLAIN分析查询,确保索引被正确使用
  7. 定期检查和优化索引

4.3.2 视图

视图是基于SQL查询的虚拟表,可以像表一样使用。视图不存储数据,而是在查询时动态生成结果。

创建视图

使用CREATE VIEW语句创建视图:

sql 复制代码
CREATE [OR REPLACE] VIEW view_name AS
SELECT column1, column2, ...
FROM table_name
WHERE condition;

示例:

sql 复制代码
-- 创建一个简单的视图
CREATE VIEW employee_details AS
SELECT e.employee_id, e.first_name, e.last_name, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;

-- 创建带列名的视图
CREATE VIEW employee_salaries (id, name, salary, department) AS
SELECT e.employee_id, CONCAT(e.first_name, ' ', e.last_name), e.salary, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;
查询视图

视图可以像表一样查询:

sql 复制代码
SELECT * FROM employee_details;

SELECT id, name, department
FROM employee_salaries
WHERE salary > 10000;
修改视图

可以使用ALTER VIEW或CREATE OR REPLACE VIEW修改现有视图:

sql 复制代码
-- 方法1
ALTER VIEW employee_details AS
SELECT e.employee_id, e.first_name, e.last_name, e.email, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;

-- 方法2
CREATE OR REPLACE VIEW employee_details AS
SELECT e.employee_id, e.first_name, e.last_name, e.email, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;
删除视图

使用DROP VIEW语句删除视图:

sql 复制代码
DROP VIEW [IF EXISTS] employee_details;
可更新视图

在某些情况下,视图是可更新的,这意味着可以通过视图执行INSERT、UPDATE和DELETE操作:

sql 复制代码
-- 创建可更新视图
CREATE VIEW active_employees AS
SELECT employee_id, first_name, last_name, email, department_id
FROM employees
WHERE active = 1;

-- 通过视图更新数据
UPDATE active_employees
SET department_id = 80
WHERE employee_id = 207;

视图可更新的条件:

  1. 视图必须只基于一个表
  2. 视图不能包含以下元素:
    • 聚合函数(SUM、COUNT等)
    • DISTINCT
    • GROUP BY
    • HAVING
    • UNION
    • 子查询
    • 某些连接操作
  3. 视图必须包含基表的所有NOT NULL且没有默认值的列
带检查选项的视图

可以使用WITH CHECK OPTION子句确保通过视图进行的修改符合视图的定义条件:

sql 复制代码
CREATE VIEW active_employees AS
SELECT employee_id, first_name, last_name, email, active
FROM employees
WHERE active = 1
WITH CHECK OPTION;

使用此选项后,以下更新将失败,因为它违反了视图的WHERE条件:

sql 复制代码
UPDATE active_employees
SET active = 0
WHERE employee_id = 207;
视图的优势

视图提供以下优势:

  1. 简化复杂查询:将复杂查询封装为简单视图
  2. 提供数据抽象:隐藏底层表结构的复杂性
  3. 实现行级和列级安全:限制用户只能看到特定的行或列
  4. 提供向后兼容性:当底层表结构变化时,保持应用程序接口稳定

4.3.3 存储过程

存储过程是存储在数据库中的一组SQL语句,可以像函数一样调用。它们可以接受参数、执行操作并返回结果。

创建存储过程

使用CREATE PROCEDURE语句创建存储过程:

sql 复制代码
DELIMITER //
CREATE PROCEDURE procedure_name(parameter_list)
BEGIN
    -- SQL语句
END //
DELIMITER ;

参数可以是IN(输入)、OUT(输出)或INOUT(输入输出)类型:

sql 复制代码
DELIMITER //
CREATE PROCEDURE get_employee_details(
    IN emp_id INT,
    OUT emp_name VARCHAR(100),
    OUT emp_salary DECIMAL(10,2)
)
BEGIN
    SELECT CONCAT(first_name, ' ', last_name), salary
    INTO emp_name, emp_salary
    FROM employees
    WHERE employee_id = emp_id;
END //
DELIMITER ;
调用存储过程

使用CALL语句调用存储过程:

sql 复制代码
-- 调用不带参数的存储过程
CALL update_statistics();

-- 调用带IN参数的存储过程
CALL increase_salaries(60, 10);

-- 调用带OUT参数的存储过程
SET @name = '';
SET @salary = 0;
CALL get_employee_details(207, @name, @salary);
SELECT @name, @salary;
存储过程中的变量

可以在存储过程中声明和使用变量:

sql 复制代码
DELIMITER //
CREATE PROCEDURE calculate_bonus(IN emp_id INT)
BEGIN
    DECLARE salary DECIMAL(10,2);
    DECLARE years_of_service INT;
    DECLARE bonus DECIMAL(10,2);
    
    -- 获取员工工资和服务年限
    SELECT salary, FLOOR(DATEDIFF(CURDATE(), hire_date) / 365)
    INTO salary, years_of_service
    FROM employees
    WHERE employee_id = emp_id;
    
    -- 计算奖金
    IF years_of_service < 5 THEN
        SET bonus = salary * 0.05;
    ELSEIF years_of_service < 10 THEN
        SET bonus = salary * 0.10;
    ELSE
        SET bonus = salary * 0.15;
    END IF;
    
    -- 返回结果
    SELECT emp_id, salary, years_of_service, bonus;
END //
DELIMITER ;
存储过程中的流程控制

MySQL存储过程支持多种流程控制语句:

  1. IF语句
sql 复制代码
IF condition THEN
    -- statements
ELSEIF another_condition THEN
    -- statements
ELSE
    -- statements
END IF;
  1. CASE语句
sql 复制代码
CASE expression
    WHEN value1 THEN
        -- statements
    WHEN value2 THEN
        -- statements
    ELSE
        -- statements
END CASE;

-- 或

CASE
    WHEN condition1 THEN
        -- statements
    WHEN condition2 THEN
        -- statements
    ELSE
        -- statements
END CASE;
  1. 循环语句
sql 复制代码
-- WHILE循环
WHILE condition DO
    -- statements
END WHILE;

-- REPEAT循环(至少执行一次)
REPEAT
    -- statements
UNTIL condition
END REPEAT;

-- LOOP循环(需要显式退出)
loop_label: LOOP
    -- statements
    IF condition THEN
        LEAVE loop_label;
    END IF;
END LOOP;
存储过程中的错误处理

可以使用DECLARE HANDLER语句处理存储过程中的错误:

sql 复制代码
DELIMITER //
CREATE PROCEDURE safe_update_salary(IN emp_id INT, IN new_salary DECIMAL(10,2))
BEGIN
    -- 声明错误处理器
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SELECT 'Error occurred. Transaction rolled back.' AS message;
    END;
    
    -- 开始事务
    START TRANSACTION;
    
    -- 更新工资
    UPDATE employees
    SET salary = new_salary
    WHERE employee_id = emp_id;
    
    -- 检查是否找到员工
    IF ROW_COUNT() = 0 THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'Employee not found';
    END IF;
    
    -- 提交事务
    COMMIT;
    
    SELECT 'Salary updated successfully.' AS message;
END //
DELIMITER ;
查看存储过程

可以使用以下命令查看存储过程:

sql 复制代码
-- 查看所有存储过程
SHOW PROCEDURE STATUS WHERE db = 'your_database';

-- 查看特定存储过程的定义
SHOW CREATE PROCEDURE procedure_name;

也可以从INFORMATION_SCHEMA查询存储过程信息:

sql 复制代码
SELECT routine_name, routine_definition
FROM INFORMATION_SCHEMA.ROUTINES
WHERE routine_schema = 'your_database' AND routine_type = 'PROCEDURE';
修改和删除存储过程

使用ALTER PROCEDURE修改存储过程的特性(但不能修改其内容):

sql 复制代码
ALTER PROCEDURE procedure_name
COMMENT 'New comment'
SQL SECURITY INVOKER;

要修改存储过程的内容,需要先删除再重新创建。

使用DROP PROCEDURE删除存储过程:

sql 复制代码
DROP PROCEDURE [IF EXISTS] procedure_name;

4.3.4 函数

MySQL函数类似于存储过程,但它们返回单个值,可以在SQL语句中使用。

创建函数

使用CREATE FUNCTION语句创建函数:

sql 复制代码
DELIMITER //
CREATE FUNCTION function_name(parameter_list)
RETURNS data_type
[characteristic ...]
BEGIN
    -- SQL语句
    RETURN value;
END //
DELIMITER ;

示例:

sql 复制代码
DELIMITER //
CREATE FUNCTION calculate_age(birth_date DATE)
RETURNS INT
DETERMINISTIC
BEGIN
    RETURN FLOOR(DATEDIFF(CURDATE(), birth_date) / 365);
END //
DELIMITER ;

函数特性(characteristic)包括:

  • DETERMINISTIC:函数对于相同的输入总是返回相同的结果
  • NOT DETERMINISTIC:函数结果可能因外部因素而变化(默认)
  • CONTAINS SQL:函数包含SQL语句,但不读取或修改数据
  • NO SQL:函数不包含SQL语句
  • READS SQL DATA:函数包含读取数据的语句,但不修改数据
  • MODIFIES SQL DATA:函数包含修改数据的语句
使用函数

函数可以在SQL语句中使用:

sql 复制代码
-- 在SELECT语句中使用
SELECT employee_id, first_name, last_name, birth_date, calculate_age(birth_date) AS age
FROM employees;

-- 在WHERE子句中使用
SELECT * FROM employees
WHERE calculate_age(birth_date) > 40;

-- 直接调用函数
SELECT calculate_age('1980-01-15');
查看、修改和删除函数

查看函数:

sql 复制代码
-- 查看所有函数
SHOW FUNCTION STATUS WHERE db = 'your_database';

-- 查看特定函数的定义
SHOW CREATE FUNCTION function_name;

修改函数特性(不能修改内容):

sql 复制代码
ALTER FUNCTION function_name
COMMENT 'New comment'
SQL SECURITY INVOKER;

删除函数:

sql 复制代码
DROP FUNCTION [IF EXISTS] function_name;

4.3.5 触发器

触发器是在表上执行特定操作(INSERT、UPDATE或DELETE)时自动执行的特殊存储过程。

创建触发器

使用CREATE TRIGGER语句创建触发器:

sql 复制代码
DELIMITER //
CREATE TRIGGER trigger_name
{BEFORE | AFTER} {INSERT | UPDATE | DELETE} ON table_name
FOR EACH ROW
BEGIN
    -- SQL语句
END //
DELIMITER ;

示例:

sql 复制代码
-- 创建BEFORE INSERT触发器
DELIMITER //
CREATE TRIGGER before_employee_insert
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
    -- 设置默认值
    IF NEW.hire_date IS NULL THEN
        SET NEW.hire_date = CURDATE();
    END IF;
END //
DELIMITER ;

-- 创建AFTER UPDATE触发器
DELIMITER //
CREATE TRIGGER after_salary_update
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
    -- 记录工资变更
    IF OLD.salary <> NEW.salary THEN
        INSERT INTO salary_changes (employee_id, old_salary, new_salary, change_date)
        VALUES (NEW.employee_id, OLD.salary, NEW.salary, NOW());
    END IF;
END //
DELIMITER ;

在触发器中,可以使用OLD和NEW关键字引用行的旧值和新值:

  • INSERT触发器只能使用NEW
  • DELETE触发器只能使用OLD
  • UPDATE触发器可以使用OLD和NEW
查看触发器

可以使用以下命令查看触发器:

sql 复制代码
-- 查看所有触发器
SHOW TRIGGERS;

-- 查看特定表的触发器
SHOW TRIGGERS WHERE `table` = 'employees';

也可以从INFORMATION_SCHEMA查询触发器信息:

sql 复制代码
SELECT trigger_name, event_manipulation, action_statement
FROM INFORMATION_SCHEMA.TRIGGERS
WHERE event_object_table = 'employees';
删除触发器

使用DROP TRIGGER语句删除触发器:

sql 复制代码
DROP TRIGGER [IF EXISTS] trigger_name;
触发器的使用场景

触发器适用于以下场景:

  1. 自动更新时间戳:记录行的最后修改时间
  2. 数据验证:确保插入或更新的数据符合业务规则
  3. 维护冗余数据:自动更新相关表中的冗余数据
  4. 审计日志:记录数据变更
  5. 实现复杂的业务规则:如级联更新或删除
触发器的注意事项

使用触发器时应注意以下几点:

  1. 触发器可能影响性能:特别是在大量数据操作时
  2. 触发器可能导致复杂的调试问题:因为它们是自动执行的
  3. 触发器不应包含复杂的业务逻辑:复杂逻辑应放在应用程序或存储过程中
  4. 触发器不能调用存储过程:但可以调用函数
  5. 触发器不能使用显式或隐式的事务控制语句:如COMMIT或ROLLBACK

4.3.6 事件调度器

MySQL事件调度器允许自动执行计划任务,类似于Unix的cron作业。

启用事件调度器

首先,确保事件调度器已启用:

sql 复制代码
-- 检查事件调度器状态
SHOW VARIABLES LIKE 'event_scheduler';

-- 启用事件调度器
SET GLOBAL event_scheduler = ON;

也可以在MySQL配置文件中启用:

ini 复制代码
[mysqld]
event_scheduler = ON
创建事件

使用CREATE EVENT语句创建事件:

sql 复制代码
DELIMITER //
CREATE EVENT event_name
ON SCHEDULE schedule
[ON COMPLETION [NOT] PRESERVE]
[ENABLE | DISABLE | DISABLE ON SLAVE]
[COMMENT 'comment']
DO
BEGIN
    -- SQL语句
END //
DELIMITER ;

schedule可以是以下之一:

  • AT timestamp:在指定时间执行一次
  • EVERY interval:按指定间隔重复执行

示例:

sql 复制代码
-- 创建一次性事件
DELIMITER //
CREATE EVENT one_time_event
ON SCHEDULE AT '2023-12-31 23:59:59'
DO
BEGIN
    INSERT INTO year_end_summary (year, total_sales)
    SELECT YEAR(order_date), SUM(amount)
    FROM orders
    WHERE YEAR(order_date) = 2023;
END //
DELIMITER ;

-- 创建重复事件
DELIMITER //
CREATE EVENT daily_cleanup
ON SCHEDULE EVERY 1 DAY
STARTS '2023-01-01 01:00:00'
DO
BEGIN
    DELETE FROM temp_logs
    WHERE created_at < DATE_SUB(NOW(), INTERVAL 7 DAY);
END //
DELIMITER ;
查看事件

可以使用以下命令查看事件:

sql 复制代码
-- 查看所有事件
SHOW EVENTS;

-- 查看特定数据库的事件
SHOW EVENTS FROM your_database;

也可以从INFORMATION_SCHEMA查询事件信息:

sql 复制代码
SELECT event_name, event_definition, interval_value, interval_field, status
FROM INFORMATION_SCHEMA.EVENTS
WHERE event_schema = 'your_database';
修改事件

使用ALTER EVENT语句修改事件:

sql 复制代码
-- 修改事件执行计划
ALTER EVENT daily_cleanup
ON SCHEDULE EVERY 12 HOUR;

-- 禁用事件
ALTER EVENT daily_cleanup
DISABLE;

-- 启用事件
ALTER EVENT daily_cleanup
ENABLE;
删除事件

使用DROP EVENT语句删除事件:

sql 复制代码
DROP EVENT [IF EXISTS] event_name;
事件的使用场景

事件调度器适用于以下场景:

  1. 定期数据清理:删除旧日志或临时数据
  2. 定期数据聚合:计算和存储统计信息
  3. 定期维护任务:优化表、更新统计信息
  4. 生成定期报告:每日、每周或每月报告
  5. 数据归档:将旧数据移动到归档表

4.3.7 分区

分区是将大表分成更小、更易管理的部分的技术。MySQL支持多种分区类型。

分区类型

MySQL支持以下分区类型:

  1. RANGE分区:基于连续范围的分区
  2. LIST分区:基于离散值列表的分区
  3. HASH分区:基于哈希函数的分区
  4. KEY分区:类似于HASH,但使用MySQL的内部哈希函数
创建分区表

使用PARTITION BY子句创建分区表:

sql 复制代码
-- RANGE分区
CREATE TABLE sales (
    id INT NOT NULL,
    sale_date DATE NOT NULL,
    amount DECIMAL(10,2),
    PRIMARY KEY (id, sale_date)  -- 包含分区键
)
PARTITION BY RANGE (YEAR(sale_date)) (
    PARTITION p0 VALUES LESS THAN (2020),
    PARTITION p1 VALUES LESS THAN (2021),
    PARTITION p2 VALUES LESS THAN (2022),
    PARTITION p3 VALUES LESS THAN (2023),
    PARTITION p4 VALUES LESS THAN MAXVALUE
);

-- LIST分区
CREATE TABLE employees (
    id INT NOT NULL,
    name VARCHAR(50),
    department VARCHAR(20),
    PRIMARY KEY (id, department)  -- 包含分区键
)
PARTITION BY LIST (department) (
    PARTITION p_sales VALUES IN ('Sales', 'Marketing'),
    PARTITION p_tech VALUES IN ('IT', 'Engineering'),
    PARTITION p_admin VALUES IN ('HR', 'Finance', 'Admin')
);

-- HASH分区
CREATE TABLE orders (
    id INT NOT NULL,
    customer_id INT,
    order_date DATE,
    PRIMARY KEY (id)
)
PARTITION BY HASH (id)
PARTITIONS 4;

-- KEY分区
CREATE TABLE customers (
    id INT NOT NULL,
    name VARCHAR(50),
    region VARCHAR(50),
    PRIMARY KEY (id)
)
PARTITION BY KEY (id)
PARTITIONS 4;
子分区

可以进一步将分区分成子分区:

sql 复制代码
CREATE TABLE sales (
    id INT NOT NULL,
    sale_date DATE NOT NULL,
    amount DECIMAL(10,2),
    region VARCHAR(10),
    PRIMARY KEY (id, sale_date, region)
)
PARTITION BY RANGE (YEAR(sale_date))
SUBPARTITION BY KEY (region)
SUBPARTITIONS 2 (
    PARTITION p0 VALUES LESS THAN (2020),
    PARTITION p1 VALUES LESS THAN (2021),
    PARTITION p2 VALUES LESS THAN (2022)
);
管理分区

可以使用ALTER TABLE语句管理分区:

sql 复制代码
-- 添加分区
ALTER TABLE sales
ADD PARTITION (PARTITION p5 VALUES LESS THAN (2024));

-- 删除分区
ALTER TABLE sales
DROP PARTITION p0;

-- 重组分区
ALTER TABLE sales
REORGANIZE PARTITION p1, p2 INTO (
    PARTITION p12 VALUES LESS THAN (2022)
);

-- 拆分分区
ALTER TABLE sales
REORGANIZE PARTITION p4 INTO (
    PARTITION p4 VALUES LESS THAN (2023),
    PARTITION p5 VALUES LESS THAN MAXVALUE
);
分区修剪

分区修剪是MySQL优化器的一个功能,它只扫描包含查询数据的分区,而不是整个表:

sql 复制代码
-- 这个查询只会扫描p1分区
SELECT * FROM sales
WHERE sale_date BETWEEN '2020-01-01' AND '2020-12-31';
查看分区信息

可以使用以下命令查看分区信息:

sql 复制代码
-- 查看表的分区信息
SHOW CREATE TABLE sales;

-- 查看分区详细信息
SELECT partition_name, partition_method, partition_expression
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE table_name = 'sales';

-- 查看每个分区的行数
SELECT partition_name, table_rows
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE table_name = 'sales';
分区的优势

分区提供以下优势:

  1. 提高查询性能:通过分区修剪减少扫描的数据量
  2. 简化数据管理:可以单独管理每个分区
  3. 优化维护操作:可以对单个分区执行维护操作
  4. 提高可用性:一个分区的问题不会影响其他分区
分区的限制

分区也有一些限制:

  1. 分区键必须是主键的一部分
  2. 不支持外键约束
  3. 某些存储引擎不支持分区
  4. 分区数量有限制(最多1024个分区)

4.3.8 全文搜索

MySQL支持全文搜索,允许基于文本内容的复杂搜索。

创建全文索引

可以在创建表时或之后创建全文索引:

sql 复制代码
-- 在创建表时创建全文索引
CREATE TABLE articles (
    id INT PRIMARY KEY,
    title VARCHAR(200),
    content TEXT,
    FULLTEXT INDEX idx_content (content)
);

-- 在现有表上创建全文索引
CREATE FULLTEXT INDEX idx_title_content ON articles (title, content);
全文搜索模式

MySQL支持三种全文搜索模式:

  1. 自然语言模式:搜索包含任何指定词的文档
  2. 布尔模式:使用特殊运算符进行更复杂的搜索
  3. 查询扩展模式:基于初始搜索结果扩展搜索
自然语言模式

自然语言模式是默认模式,它搜索包含任何指定词的文档:

sql 复制代码
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('database optimization');

这将返回包含"database"或"optimization"的文章,按相关性排序。

布尔模式

布尔模式允许使用特殊运算符进行更复杂的搜索:

sql 复制代码
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('database optimization' IN BOOLEAN MODE);

布尔模式运算符:

  • +:词必须存在
  • -:词不能存在
  • *:通配符
  • ":精确短语
  • ():分组
  • >:增加相关性
  • <:减少相关性

示例:

sql 复制代码
-- 必须包含"database",可能包含"optimization"
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('+database optimization' IN BOOLEAN MODE);

-- 必须包含"database",不能包含"mysql"
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('+database -mysql' IN BOOLEAN MODE);

-- 必须包含精确短语"database optimization"
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('"database optimization"' IN BOOLEAN MODE);

-- 包含以"data"开头的词
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('data*' IN BOOLEAN MODE);
查询扩展模式

查询扩展模式执行两次搜索:第一次使用原始搜索词,第二次使用第一次搜索中找到的相关词:

sql 复制代码
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('database' WITH QUERY EXPANSION);

这可能会返回不包含"database"但包含相关词(如"MySQL"、"SQL"等)的文章。

全文搜索配置

可以配置全文搜索的行为:

sql 复制代码
-- 最小词长度(默认为3)
SET GLOBAL innodb_ft_min_token_size = 2;

-- 停用词(不索引的常见词)
SET GLOBAL innodb_ft_server_stopword_table = 'database/stopwords';

注意:修改这些设置后,需要重建全文索引才能生效。

4.3.9 JSON支持

MySQL 5.7.8及以上版本支持原生JSON数据类型,提供了存储和操作JSON数据的功能。

创建JSON列

可以在创建表时定义JSON列:

sql 复制代码
CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    attributes JSON
);
插入JSON数据

可以使用JSON字面量或JSON_OBJECT()函数插入JSON数据:

sql 复制代码
-- 使用JSON字面量
INSERT INTO products VALUES (1, 'Laptop', '{"color": "black", "weight": 1.5, "features": ["backlit keyboard", "touch screen"]}');

-- 使用JSON_OBJECT()函数
INSERT INTO products VALUES (2, 'Phone', JSON_OBJECT('color', 'white', 'weight', 0.3, 'features', JSON_ARRAY('water resistant', 'dual camera')));
查询JSON数据

可以使用JSON函数和运算符查询JSON数据:

sql 复制代码
-- 使用->运算符(返回JSON值)
SELECT id, name, attributes->'$.color' AS color FROM products;

-- 使用->>运算符(返回字符串值)
SELECT id, name, attributes->>'$.color' AS color FROM products;

-- 访问数组元素
SELECT id, name, attributes->'$.features[0]' AS first_feature FROM products;

-- 使用JSON_EXTRACT()函数
SELECT id, name, JSON_EXTRACT(attributes, '$.color') AS color FROM products;
修改JSON数据

可以使用JSON函数修改JSON数据:

sql 复制代码
-- 更新整个JSON值
UPDATE products
SET attributes = '{"color": "silver", "weight": 1.5, "features": ["backlit keyboard", "touch screen", "fingerprint reader"]}'
WHERE id = 1;

-- 更新特定路径
UPDATE products
SET attributes = JSON_SET(attributes, '$.color', 'silver', '$.features[2]', 'fingerprint reader')
WHERE id = 1;

-- 插入新属性
UPDATE products
SET attributes = JSON_INSERT(attributes, '$.price', 999.99)
WHERE id = 1;

-- 替换属性
UPDATE products
SET attributes = JSON_REPLACE(attributes, '$.weight', 1.6)
WHERE id = 1;

-- 删除属性
UPDATE products
SET attributes = JSON_REMOVE(attributes, '$.weight')
WHERE id = 1;
JSON函数

MySQL提供了许多JSON函数:

  1. 构造函数

    • JSON_ARRAY():创建JSON数组
    • JSON_OBJECT():创建JSON对象
    • JSON_QUOTE():将字符串引用为JSON字符串
  2. 查询函数

    • JSON_EXTRACT():提取JSON值
    • JSON_CONTAINS():检查JSON文档是否包含特定值
    • JSON_CONTAINS_PATH():检查JSON文档是否包含特定路径
    • JSON_KEYS():返回JSON对象的键
    • JSON_SEARCH():查找JSON值中的字符串
  3. 修改函数

    • JSON_INSERT():插入值(如果路径不存在)
    • JSON_SET():插入或更新值
    • JSON_REPLACE():更新值(如果路径存在)
    • JSON_REMOVE():删除值
    • JSON_ARRAY_APPEND():将值追加到JSON数组
    • JSON_ARRAY_INSERT():将值插入JSON数组
  4. 实用函数

    • JSON_TYPE():返回JSON值的类型
    • JSON_LENGTH():返回JSON文档的长度
    • JSON_VALID():检查值是否为有效的JSON
    • JSON_PRETTY():格式化JSON文档以提高可读性
JSON索引

虽然不能直接索引JSON列,但可以创建虚拟列并索引它们:

sql 复制代码
-- 添加虚拟列
ALTER TABLE products
ADD COLUMN color VARCHAR(50) GENERATED ALWAYS AS (attributes->>'$.color') VIRTUAL,
ADD COLUMN weight DECIMAL(5,2) GENERATED ALWAYS AS (attributes->>'$.weight') VIRTUAL;

-- 创建索引
CREATE INDEX idx_color ON products(color);
CREATE INDEX idx_weight ON products(weight);

然后可以使用这些索引进行高效查询:

sql 复制代码
SELECT * FROM products
WHERE color = 'black' AND weight < 1.0;

4.3.10 性能优化

MySQL性能优化是一个广泛的主题,涉及多个方面。以下是一些关键的优化技术:

查询优化
  1. 使用EXPLAIN分析查询
sql 复制代码
EXPLAIN SELECT * FROM employees
WHERE department_id = 60 AND salary > 10000;

EXPLAIN输出包含以下重要信息:

  • select_type:查询类型(SIMPLE、PRIMARY、SUBQUERY等)
  • table:表名
  • type:连接类型(system、const、eq_ref、ref、range、index、ALL等)
  • possible_keys:可能使用的索引
  • key:实际使用的索引
  • key_len:索引键的长度
  • ref:与索引比较的列
  • rows:估计要检查的行数
  • filtered:按表条件过滤的行百分比
  • Extra:附加信息
  1. 优化索引使用

    • 为WHERE、JOIN、ORDER BY和GROUP BY子句中的列创建索引
    • 使用复合索引时,考虑列的顺序
    • 避免在索引列上使用函数
  2. 优化JOIN操作

    • 确保JOIN条件有索引
    • 使用适当的JOIN类型(INNER、LEFT、RIGHT)
    • 考虑表的连接顺序
  3. 优化子查询

    • 考虑使用JOIN替代某些子查询
    • 使用EXISTS而不是IN(对于大型子查询)
  4. 使用LIMIT限制结果集大小

服务器配置优化
  1. 内存配置

    • innodb_buffer_pool_size:InnoDB缓冲池大小(通常设置为系统内存的50-80%)
    • key_buffer_size:MyISAM键缓存大小
    • query_cache_size:查询缓存大小(MySQL 8.0已移除)
    • sort_buffer_size:排序缓冲区大小
    • join_buffer_size:连接缓冲区大小
  2. 并发配置

    • max_connections:最大连接数
    • thread_cache_size:线程缓存大小
    • innodb_thread_concurrency:InnoDB线程并发数
  3. 日志配置

    • innodb_log_file_size:InnoDB日志文件大小
    • innodb_log_buffer_size:InnoDB日志缓冲区大小
    • innodb_flush_log_at_trx_commit:事务提交时的日志刷新策略
表优化
  1. 选择适当的存储引擎

    • InnoDB:支持事务、外键、行级锁定
    • MyISAM:简单、快速、不支持事务
    • MEMORY:内存表,速度快但不持久
  2. 表结构优化

    • 使用适当的数据类型
    • 避免使用NULL(除非必要)
    • 考虑表分区
    • 使用适当的字符集和排序规则
  3. 表维护

    • 定期优化表:OPTIMIZE TABLE
    • 分析表:ANALYZE TABLE
    • 检查表:CHECK TABLE
    • 修复表:REPAIR TABLE
监控和诊断
  1. 性能模式(Performance Schema)

    • 启用性能模式:SET GLOBAL performance_schema = ON;
    • 查询性能模式表获取性能信息
  2. 慢查询日志

    • 启用慢查询日志:SET GLOBAL slow_query_log = 1;
    • 设置慢查询阈值:SET GLOBAL long_query_time = 1;
    • 分析慢查询日志:使用mysqldumpslow工具
  3. 状态变量

    • 查看全局状态:SHOW GLOBAL STATUS;
    • 查看会话状态:SHOW SESSION STATUS;
    • 查看特定变量:SHOW STATUS LIKE 'Com_%';
  4. 系统变量

    • 查看全局变量:SHOW GLOBAL VARIABLES;
    • 查看会话变量:SHOW SESSION VARIABLES;
    • 查看特定变量:SHOW VARIABLES LIKE 'innodb_%';
缓存优化
  1. 查询缓存(MySQL 8.0已移除):

    • 启用查询缓存:SET GLOBAL query_cache_type = 1;
    • 设置查询缓存大小:SET GLOBAL query_cache_size = 67108864;
  2. InnoDB缓冲池

    • 设置缓冲池大小:SET GLOBAL innodb_buffer_pool_size = 1073741824;
    • 设置缓冲池实例数:SET GLOBAL innodb_buffer_pool_instances = 8;
  3. 表缓存

    • 设置表打开缓存:SET GLOBAL table_open_cache = 2000;
    • 设置表定义缓存:SET GLOBAL table_definition_cache = 1400;

通过掌握这些高级功能和优化技术,您可以充分利用MySQL的强大功能,构建高性能、可靠的数据库应用程序。这些功能不仅可以提高数据库的性能和可靠性,还可以简化开发和维护工作。

相关推荐
.Shu.1 小时前
Mysql InnoDB 底层架构设计、功能、原理、源码系列合集【五、InnoDB 高阶机制与实战调优】
数据库·mysql
新法国菜3 小时前
MySql知识梳理之DDL语句
数据库·mysql
DarkAthena3 小时前
【GaussDB】全密态等值查询功能测试及全密态技术介绍
数据库·gaussdb
ShawnLeiLei4 小时前
2.3 Flink的核心概念解析
数据库·python·flink
石皮幼鸟5 小时前
数据完整性在所有场景下都很重要吗?
数据库·后端
大只鹅5 小时前
Centos7.9 Docker26容器化部署 MySql9.4 一主一从的同步复制部署
mysql·centos
叁沐6 小时前
MySQL 28 读写分离有哪些坑?
mysql
nightunderblackcat6 小时前
新手向:异步编程入门asyncio最佳实践
前端·数据库·python
DarkAthena6 小时前
【GaussDB】使用MySQL客户端连接到GaussDB的M-Compatibility数据库
数据库·mysql·gaussdb
livemetee7 小时前
Flink2.0学习笔记:使用HikariCP 自定义sink实现数据库连接池化
大数据·数据库·笔记·学习·flink