用户与权限管理

1. 用户创建

-- 创建用户(第三章 3.3节)
CREATE USER 'wulian'@'sakila' IDENTIFIED BY '1234';
-- 创建只能在'sakila'主机上登录的用户wulian,密码为1234

2. 授予权限

-- 授予权限(第三章 3.3节)
GRANT SELECT, UPDATE, DELETE ON sakila.* TO wulian@sakila;
-- 授予wulian用户在sakila数据库所有表上的SELECT、UPDATE、DELETE权限

3. 撤销权限

-- 撤销权限(第三章 3.3节)
REVOKE SELECT, DELETE ON sakila.* FROM 'wulian'@'sakila';
-- 撤销wulian用户在sakila数据库所有表上的SELECT、DELETE权限

基本查看指令

1. 查看数据库信息

-- 查看所有数据库(第三章 3.3节)
SHOW DATABASES;

-- 查看特定数据库中的表(第三章 3.3节)
SHOW TABLES;

-- 查看表的创建语句(第三章 3.3节)
SHOW CREATE TABLE actor;

-- 查看当前字符集设置(第三章 3.3节)
SHOW VARIABLES LIKE 'character%';

2. 查看表结构

-- 查看表结构:两种等价写法(第四章 4.2节)
DESCRIBE student;  或  DESC student;
-- 查看student表的字段名、类型、约束等信息

3. 查看视图

-- 查看视图结构(第四章 4.4节)
DESC stu_view;

创建表与视图

1. 创建表(含完整约束)

-- 创建带有各种约束的学生表(第四章 4.1-4.2节)
CREATE TABLE student (
    -- 自增主键(表级约束也可在最后定义)
    snum INT AUTO_INCREMENT PRIMARY KEY,
    
    -- 学号:非空且唯一
    s_no CHAR(12) NOT NULL UNIQUE,
    
    -- 姓名:非空约束
    s_name VARCHAR(8) NOT NULL,
    
    -- 性别:枚举类型(隐含检查约束)
    gender ENUM('男', '女'),
    
    -- 出生日期
    DOB DATE,
    
    -- 入学成绩:检查约束(必须>500)
    score INT CHECK (score > 500),
    
    -- 电话:唯一约束
    phone VARCHAR(15) UNIQUE,
    
    -- 邮箱
    mail VARCHAR(20),
    
    -- 表级约束示例(为约束命名)
    CONSTRAINT ck_score CHECK (score > 500),
    CONSTRAINT uk_sno UNIQUE (s_no),
    CONSTRAINT uk_phone UNIQUE (phone)
);

-- 简化写法,将约束放在列定义中
CREATE TABLE student(
    snum INT AUTO_INCREMENT PRIMARY KEY,
    s_no CHAR(12) NOT NULL UNIQUE,
    s_name VARCHAR(8) NOT NULL,
    gender ENUM('男', '女'),
    DOB DATE,
    score INT CHECK(score>500),
    phone VARCHAR(15) UNIQUE,
    mail VARCHAR(20)
);

2. 创建外键约束

-- 创建课程表(第四章 4.1节) 
CREATE TABLE course(
    course_id CHAR(10) PRIMARY KEY,
    course_name VARCHAR(50),
    teacher_id INT,
    -- 表级外键约束(只能定义为表级约束)
    CONSTRAINT fk_teacher FOREIGN KEY (teacher_id) 
    REFERENCES teacher(teacher_id)
);

3. 创建视图

-- 创建简单的行列子集视图(可更新)(第四章 4.4节)
CREATE VIEW stu_view AS 
SELECT s_name, s_no, gender 
FROM student;

-- 创建带条件的视图(第四章 4.4节)
CREATE VIEW male_stu AS
SELECT s_name, s_no 
FROM student 
WHERE gender = '男';

-- 使用WITH CHECK OPTION(第四章 4.4节)
CREATE VIEW high_score_stu AS
SELECT s_no, s_name, score
FROM student
WHERE score > 600
WITH CHECK OPTION;  -- 保证插入/更新的数据必须满足score>600

SELECT查询(复杂示例)

1. 基础查询与虚列

-- 查询学号、姓名和年龄(虚列)(第五章 5.1节)
SELECT s_no, s_name, 
       YEAR(CURDATE()) - YEAR(DOB) AS age  -- 年龄是计算列
FROM student;

-- 使用DISTINCT去重(第五章 5.1节)
SELECT DISTINCT gender FROM student;

-- 排序和限制结果数量(第五章 5.1节)
SELECT s_no, s_name, score
FROM student
ORDER BY score DESC  -- 按成绩降序排列
LIMIT 5;             -- 只显示前5名

2. 分组与聚合函数

-- 按性别分组,统计人数和平均成绩(第五章 5.1节)
SELECT gender, 
       COUNT(*) AS stu_count, 
       AVG(score) AS avg_score
FROM student
GROUP BY gender
HAVING AVG(score) > 550;  -- 只显示平均分>550的组

3. 多表连接查询

-- 内连接(第五章 5.2节)
SELECT stu.s_name, stu.s_no, sc.course_name, sc.score
FROM student stu
INNER JOIN stu_score sc ON stu.s_no = sc.s_no
WHERE sc.course_name = '自动控制原理';

-- 左外连接(第五章 5.2节)
SELECT stu.s_name, sc.course_name, sc.score
FROM student stu
LEFT JOIN stu_score sc ON stu.s_no = sc.s_no;
-- 显示所有学生,即使他们没有成绩记录

-- 自然连接(自动匹配相同列名)
SELECT * FROM student NATURAL JOIN stu_score;

4. 嵌套查询

-- 使用IN的子查询(第五章 5.3节)
SELECT s_name, s_no
FROM student
WHERE s_no IN (
    SELECT s_no 
    FROM stu_score 
    WHERE score > 90
);

-- 使用比较运算符的子查询(第五章 5.3节)
SELECT s_name, score
FROM student
WHERE score > (
    SELECT AVG(score) 
    FROM student
);

-- 相关子查询(使用EXISTS)(第五章 5.3节)
SELECT s_name
FROM student stu
WHERE EXISTS (
    SELECT 1 
    FROM stu_score sc 
    WHERE sc.s_no = stu.s_no 
    AND sc.score > 85
);

5. 集合查询

-- 并集(UNION)(第五章 5.4节)
SELECT s_no FROM student WHERE gender = '男'
UNION
SELECT s_no FROM student WHERE score > 600;

表中数据的增删改

1. 插入数据

-- 标准插入(第三章 3.1节)
INSERT INTO student (s_no, s_name, gender, score)
VALUES ('202101100011', '张三', '男', 620);

-- 省略列名插入(需提供所有列值)
INSERT INTO student 
VALUES (NULL, '202101100012', '李四', '女', '2003-05-30', 580, '13800138000', NULL);

-- 使用SET插入(第四章 4.3节)
INSERT INTO student 
SET s_no = '202101100013', s_name = '王五', gender = '男', score = 650;

-- 批量从文件导入(第四章 4.3节)
LOAD DATA LOCAL INFILE 'D:\\testdata\\stu.txt' 
INTO TABLE student
FIELDS TERMINATED BY ',' 
LINES TERMINATED BY ';'
(s_no, s_name, gender, DOB);

2. 更新数据

-- 更新单条记录(必须使用主键或唯一标识)
UPDATE student 
SET score = score + 10, phone = '13900139000'
WHERE s_no = '202101100011';  -- 基于主键条件

-- 批量更新符合条件的记录
UPDATE student 
SET score = score * 1.05 
WHERE score < 550;

-- 更新时使用子查询
UPDATE stu_score sc
SET sc.score = sc.score + 5
WHERE sc.s_no IN (
    SELECT s_no 
    FROM student 
    WHERE gender = '女'
);

3. 删除数据

-- 删除单条记录(第三章 3.1节)
DELETE FROM student 
WHERE s_no = '202101100011';

-- 删除所有记录
DELETE FROM student;

-- 删除多个符合条件的记录
DELETE FROM student 
WHERE score < 500 OR phone IS NULL;

-- 删除与其他表相关的记录(使用子查询)
DELETE FROM student 
WHERE s_no IN (
    SELECT s_no 
    FROM stu_score 
    WHERE course_name = '已取消课程'
);

视图中数据的修改

-- 注意:只有行列子集视图才可以更新数据!

-- 1. 更新视图数据(实际上更新原表)
UPDATE stu_view 
SET gender = '女' 
WHERE s_no = '202101100011';

-- 2. 向视图插入数据(第四章 4.4节)
INSERT INTO male_stu (s_no, s_name)
VALUES ('202101100014', '赵六');
-- 注意:如果原表有gender字段,这里未指定,可能会插入NULL

-- 3. WITH CHECK OPTION的效果(第四章 4.4节)
-- 假设有视图 high_score_stu (score > 600 WITH CHECK OPTION)
INSERT INTO high_score_stu VALUES ('202101100015', '孙七', 590);
-- 这里会失败!因为590不满足score>600的条件

-- 4. 删除视图中的数据
DELETE FROM stu_view 
WHERE s_name = '张三';
-- 这会从原表student中删除对应的记录

自定义函数与存储过程

1. 创建并调用函数

-- 计算长方形面积的函数(第六章 6.2节)
DELIMITER $$
CREATE FUNCTION calc_area(length INT, width INT)
RETURNS INT
BEGIN
    RETURN length * width;
END$$
DELIMITER ;

-- 调用函数
SELECT calc_area(3, 4) AS area;  -- 返回12

-- 带条件判断的函数(第六章 6.3节)
DELIMITER $$
CREATE FUNCTION t_score(score INT) 
RETURNS CHAR(15)
BEGIN
    DECLARE grade CHAR(15);
    IF score >= 85 THEN 
        SET grade = 'EXCELLENT';
    ELSE 
        SET grade = 'PASS';
    END IF;
    RETURN grade;
END$$
DELIMITER ;

SELECT s_name, t_score(score) AS grade FROM student;

2. 创建并调用存储过程

-- 简单的存储过程(第七章 7.1节)
DELIMITER $$
CREATE PROCEDURE get_student_info(IN stu_no CHAR(12))
BEGIN
    SELECT s_name, gender, score 
    FROM student 
    WHERE s_no = stu_no;
END$$
DELIMITER ;

-- 调用存储过程
CALL get_student_info('202101100011');

-- 带输入输出参数的存储过程(第七章 7.1节)[4](@context-ref?id=0)
DELIMITER $$
CREATE PROCEDURE count_by_grade(
    IN grade_year INT, 
    OUT stu_count INT
)
BEGIN
    SELECT COUNT(*) INTO stu_count
    FROM student
    WHERE s_no LIKE CONCAT(grade_year, '%');
END$$
DELIMITER ;

-- 调用并获取输出参数
CALL count_by_grade(2021, @count);
SELECT @count AS '2021级学生数';

触发器

1. 创建触发器

-- 删除数据时触发的触发器(第七章 7.2节)
DELIMITER $$
CREATE TRIGGER del_trigger
BEFORE DELETE ON student
FOR EACH ROW
BEGIN
    -- 在删除前将被删信息记录到日志表
    INSERT INTO delete_log(deleted_s_no, deleted_at)
    VALUES (OLD.s_no, NOW());
END$$
DELIMITER ;

-- 插入数据时触发的触发器(第七章 7.2节)
DELIMITER $$
CREATE TRIGGER insert_trigger
AFTER INSERT ON student
FOR EACH ROW
BEGIN
    -- 自动初始化相关表中的记录
    INSERT INTO stu_score(s_no) VALUES (NEW.s_no);
END$$
DELIMITER ;

-- 带条件检查的UPDATE触发器(第七章 7.2节)
DELIMITER $$
CREATE TRIGGER update_score_trigger
BEFORE UPDATE ON student
FOR EACH ROW
BEGIN
    IF NEW.score < 0 THEN
        SIGNAL SQLSTATE '45000' 
        SET MESSAGE_TEXT = '成绩不能为负数!';
    END IF;
END$$
DELIMITER ;

2. 查看和管理触发器

-- 查看所有触发器
SHOW TRIGGERS;

-- 删除触发器
DROP TRIGGER del_trigger;

计划事件

1. 创建计划事件

-- 一次性事件:5秒后执行(第七章 7.3节)
CREATE EVENT one_time_event
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 5 SECOND
DO
    INSERT INTO event_log(event_name, executed_at) 
    VALUES ('一次性事件', NOW());

-- 周期性事件:每天凌晨1点执行(第七章 7.3节)
CREATE EVENT daily_cleanup
ON SCHEDULE EVERY 1 DAY
STARTS CURRENT_DATE + INTERVAL 1 HOUR
DO
    -- 清理临时数据
    DELETE FROM temp_table WHERE created_at < DATE_SUB(NOW(), INTERVAL 7 DAY);

-- 指定结束时间的周期性事件(第七章 7.3节)
CREATE EVENT weekly_report
ON SCHEDULE EVERY 1 WEEK
STARTS CURDATE() + INTERVAL 1 WEEK
ENDS '2026-08-31 12:00:00'
DO
    -- 生成周报
    CALL generate_weekly_report();

2. 带语句块的事件

DELIMITER $$
CREATE EVENT batch_process
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 10 SECOND
DO
BEGIN
    DECLARE done INT DEFAULT 0;
    DECLARE stu_no CHAR(12);
    DECLARE cur CURSOR FOR SELECT s_no FROM student WHERE score IS NULL;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
    
    OPEN cur;
    read_loop: LOOP
        FETCH cur INTO stu_no;
        IF done THEN
            LEAVE read_loop;
        END IF;
        -- 为成绩为空的学生设置默认成绩
        UPDATE student SET score = 500 WHERE s_no = stu_no;
    END LOOP;
    CLOSE cur;
END$$
DELIMITER ;

3. 管理事件

-- 查看所有事件
SHOW EVENTS;

-- 启用/禁用事件
ALTER EVENT daily_cleanup DISABLE;
ALTER EVENT daily_cleanup ENABLE;

-- 删除事件
DROP EVENT daily_cleanup;

高级示例:综合运用

1. 使用游标的存储过程

-- 计算学生平均成绩并更新等级的存储过程(第七章 7.4节)
DELIMITER $$
CREATE PROCEDURE update_all_grades()
BEGIN
    DECLARE done INT DEFAULT 0;
    DECLARE v_s_no CHAR(12);
    DECLARE v_score INT;
    DECLARE cur CURSOR FOR SELECT s_no, score FROM student;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
    
    OPEN cur;
    
    read_loop: LOOP
        FETCH cur INTO v_s_no, v_score;
        IF done THEN
            LEAVE read_loop;
        END IF;
        
        -- 根据成绩更新等级
        IF v_score >= 90 THEN
            UPDATE student SET grade = 'A' WHERE s_no = v_s_no;
        ELSEIF v_score >= 80 THEN
            UPDATE student SET grade = 'B' WHERE s_no = v_s_no;
        ELSEIF v_score >= 70 THEN
            UPDATE student SET grade = 'C' WHERE s_no = v_s_no;
        ELSE
            UPDATE student SET grade = 'D' WHERE s_no = v_s_no;
        END IF;
    END LOOP;
    
    CLOSE cur;
END$$
DELIMITER ;

CALL update_all_grades();

2. 带错误处理的事务

-- 转账操作(原子性事务)(第九章 9.1节)
DELIMITER $$
CREATE PROCEDURE transfer_money(
    IN from_account CHAR(20),
    IN to_account CHAR(20),
    IN amount DECIMAL(10,2)
)
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '转账失败!';
    END;
    
    START TRANSACTION;
    
    -- 检查余额是否充足
    IF (SELECT balance FROM accounts WHERE account_no = from_account) < amount THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '余额不足!';
    END IF;
    
    -- 扣除转出账户余额
    UPDATE accounts SET balance = balance - amount 
    WHERE account_no = from_account;
    
    -- 增加转入账户余额
    UPDATE accounts SET balance = balance + amount 
    WHERE account_no = to_account;
    
    -- 记录交易日志
    INSERT INTO transfer_log(from_account, to_account, amount, trans_time)
    VALUES (from_account, to_account, amount, NOW());
    
    COMMIT;
END$$
DELIMITER ;

要点总结

需要特别注意的区别:

  1. 函数 vs 存储过程
    • 函数必须有返回值,存储过程可以没有
    • 函数用SELECT调用,存储过程用CALL调用
  2. 视图更新条件
    • 必须是行列子集视图(单表、原始列、无计算)
    • 不能使用聚合函数、DISTINCT、GROUP BY等
  3. 事务的类型
    • 显式事务:START TRANSACTION + COMMIT/ROLLBACK
    • 隐式事务:SET autocommit=0 + COMMIT/ROLLBACK
    • 自动提交:SET autocommit=1(默认)
  4. 锁机制
    • 表级锁:LOCK TABLE ... READ/WRITE
    • 行级锁:SELECT ... LOCK IN SHARE MODE / FOR UPDATE
  5. 约束类型
    • 行级约束:列定义时直接添加
    • 表级约束:所有列定义完成后添加
    • 外键只能定义为表级约束