一、DDL(数据定义语言)详细示例
1. 数据库操作
sql
-- 创建数据库,指定字符集和排序规则
CREATE DATABASE IF NOT EXISTS company_db
CHARACTER SET utf8mb4 -- 使用UTF-8编码,支持表情符号
COLLATE utf8mb4_unicode_ci; -- 不区分大小写的Unicode排序规则
-- 查看所有数据库
SHOW DATABASES;
-- 选择要使用的数据库
USE company_db;
-- 删除数据库(危险操作,谨慎使用)
-- DROP DATABASE IF EXISTS old_db;
2. 表操作完整示例
sql
-- 创建员工表
CREATE TABLE IF NOT EXISTS employees (
-- 列定义开始
id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '员工ID,主键',
emp_no VARCHAR(20) NOT NULL UNIQUE COMMENT '员工工号,唯一',
name VARCHAR(50) NOT NULL COMMENT '员工姓名',
gender ENUM('M', 'F') NOT NULL DEFAULT 'M' COMMENT '性别:M-男,F-女',
birth_date DATE NOT NULL COMMENT '出生日期',
department_id INT UNSIGNED COMMENT '部门ID,外键',
salary DECIMAL(10, 2) NOT NULL DEFAULT 0.00 COMMENT '月薪,10位数字,2位小数',
hire_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1-在职,0-离职',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
-- 主键约束
PRIMARY KEY (id),
-- 唯一约束
UNIQUE KEY uk_emp_no (emp_no),
-- 外键约束(引用部门表)
CONSTRAINT fk_employee_department
FOREIGN KEY (department_id)
REFERENCES departments(id)
ON DELETE SET NULL -- 部门删除时设为NULL
ON UPDATE CASCADE, -- 部门ID更新时级联更新
-- 普通索引
INDEX idx_department (department_id),
INDEX idx_name (name),
INDEX idx_hire_date (hire_date),
-- 复合索引
INDEX idx_dept_status (department_id, status),
-- 检查约束(MySQL 8.0.16+)
CONSTRAINT chk_salary CHECK (salary >= 0),
CONSTRAINT chk_birth_date CHECK (birth_date < '2005-01-01')
-- 表选项
) ENGINE = InnoDB -- 使用InnoDB存储引擎
DEFAULT CHARSET = utf8mb4 -- 默认字符集
COLLATE = utf8mb4_unicode_ci -- 排序规则
AUTO_INCREMENT = 1001 -- 自增起始值
COMMENT = '员工信息表' -- 表注释
ROW_FORMAT = DYNAMIC; -- 行格式
3. 修改表结构
sql
-- 添加新列
ALTER TABLE employees
ADD COLUMN email VARCHAR(100)
AFTER name -- 在name列之后添加
COMMENT '邮箱地址';
-- 修改列定义
ALTER TABLE employees
MODIFY COLUMN salary DECIMAL(12, 2) -- 修改数据类型
NOT NULL DEFAULT 0.00
COMMENT '月薪(扩大范围)';
-- 修改列名
ALTER TABLE employees
CHANGE COLUMN birth_date birth_day DATE -- birth_date改为birth_day
COMMENT '出生日';
-- 删除列(危险操作)
-- ALTER TABLE employees DROP COLUMN temp_column;
-- 添加索引
ALTER TABLE employees
ADD INDEX idx_email (email(50)); -- 对email前50个字符建索引
-- 添加唯一索引
ALTER TABLE employees
ADD UNIQUE INDEX uk_email (email);
-- 删除索引
ALTER TABLE employees
DROP INDEX idx_email;
-- 添加外键
ALTER TABLE employees
ADD CONSTRAINT fk_employee_title
FOREIGN KEY (title_id)
REFERENCES job_titles(id);
-- 删除外键
ALTER TABLE employees
DROP FOREIGN KEY fk_employee_title;
-- 重命名表
RENAME TABLE old_employees TO archived_employees;
-- 或者使用ALTER TABLE重命名
ALTER TABLE employees RENAME TO staff;
4. 索引管理
sql
-- 创建普通索引
CREATE INDEX idx_salary ON employees(salary);
-- 创建唯一索引
CREATE UNIQUE INDEX idx_employee_email ON employees(email);
-- 创建前缀索引(节省空间)
CREATE INDEX idx_name_prefix ON employees(name(10)); -- 只索引前10个字符
-- 创建全文索引(用于文本搜索)
CREATE TABLE articles (
id INT,
title VARCHAR(200),
content TEXT,
FULLTEXT idx_fulltext (title, content) -- 全文索引
) ENGINE = InnoDB;
-- 查看表索引
SHOW INDEX FROM employees;
-- 删除索引
DROP INDEX idx_salary ON employees;
5. 视图操作
sql
-- 创建视图(虚拟表)
CREATE OR REPLACE VIEW v_active_employees AS
SELECT
id,
emp_no,
name,
department_id,
salary,
hire_date
FROM employees
WHERE status = 1 -- 只显示在职员工
AND salary > 5000 -- 薪资大于5000
WITH CHECK OPTION; -- 保证视图数据一致性
-- 使用视图查询
SELECT * FROM v_active_employees
WHERE department_id = 1;
-- 修改视图
ALTER VIEW v_active_employees AS
SELECT * FROM employees
WHERE status = 1;
-- 删除视图
DROP VIEW IF EXISTS v_active_employees;
二、DML(数据操作语言)详细示例
1. INSERT 插入数据
sql
-- 基本插入
INSERT INTO employees (emp_no, name, gender, birth_date, salary)
VALUES ('E1001', '张三', 'M', '1990-05-15', 8000.00);
-- 插入多行数据(高效方式)
INSERT INTO employees (emp_no, name, gender, birth_date, salary)
VALUES
('E1002', '李四', 'M', '1992-08-20', 7500.00),
('E1003', '王芳', 'F', '1993-03-10', 8200.00),
('E1004', '赵六', 'M', '1991-11-25', 9000.00);
-- 插入查询结果
INSERT INTO employee_archive (emp_no, name, salary, archive_date)
SELECT emp_no, name, salary, NOW()
FROM employees
WHERE status = 0; -- 插入离职员工到归档表
-- INSERT IGNORE:忽略重复键错误
INSERT IGNORE INTO employees (emp_no, name)
VALUES ('E1001', '张三重复'); -- 如果E1001已存在,则忽略
-- INSERT ... ON DUPLICATE KEY UPDATE
-- 存在则更新,不存在则插入
INSERT INTO daily_stats (stat_date, page_views, unique_visitors)
VALUES ('2024-01-15', 100, 50)
ON DUPLICATE KEY UPDATE
page_views = page_views + VALUES(page_views), -- 累加
unique_visitors = VALUES(unique_visitors); -- 替换
-- REPLACE:存在则删除后插入(注意:删除原有记录)
REPLACE INTO employees (id, emp_no, name)
VALUES (1, 'E1001', '张三新名字'); -- 如果id=1存在,先删除再插入
2. UPDATE 更新数据
sql
-- 基本更新
UPDATE employees
SET salary = salary * 1.1, -- 涨薪10%
updated_at = NOW() -- 更新修改时间
WHERE department_id = 1 -- 条件:部门ID=1
AND hire_date < '2023-01-01'; -- 且入职早于2023-01-01
-- 使用子查询更新
UPDATE employees e
SET e.salary = (
SELECT AVG(salary) * 1.2
FROM employees
WHERE department_id = e.department_id
)
WHERE e.performance_rating = 'A'; -- 绩效A的员工涨到部门平均的1.2倍
-- 多表更新(更新关联表)
UPDATE employees e
JOIN departments d ON e.department_id = d.id
SET e.salary = e.salary + 1000, -- 员工加薪
d.budget = d.budget - 1000 -- 部门预算减少
WHERE d.name = '技术部';
-- 使用CASE语句条件更新
UPDATE employees
SET salary = CASE
WHEN performance_rating = 'A' THEN salary * 1.2 -- A级涨20%
WHEN performance_rating = 'B' THEN salary * 1.1 -- B级涨10%
WHEN performance_rating = 'C' THEN salary * 1.05 -- C级涨5%
ELSE salary -- 其他不变
END,
bonus = CASE
WHEN years_of_service > 10 THEN 5000 -- 工龄>10年
WHEN years_of_service > 5 THEN 3000 -- 工龄>5年
ELSE 1000 -- 其他
END;
-- LIMIT限制更新数量(防止误操作)
UPDATE employees
SET status = 0 -- 标记为离职
WHERE last_work_date < '2023-12-01' -- 最后工作日早于
ORDER BY id -- 按ID排序
LIMIT 10; -- 只更新10条
3. DELETE 删除数据
sql
-- 基本删除(危险!一定要有条件)
DELETE FROM employees
WHERE status = 0 -- 删除离职员工
AND updated_at < '2023-01-01'; -- 且长时间未更新
-- 使用子查询删除
DELETE FROM employees
WHERE department_id IN (
SELECT id FROM departments
WHERE status = 'closed' -- 删除已关闭部门的员工
);
-- 关联删除
DELETE e FROM employees e
JOIN departments d ON e.department_id = d.id
WHERE d.location = '北京旧办公室'; -- 删除旧办公室员工
-- LIMIT限制删除数量(安全做法)
DELETE FROM audit_log
WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY) -- 90天前的日志
ORDER BY created_at ASC -- 从最早的开始删
LIMIT 1000; -- 每次最多删1000条
-- TRUNCATE vs DELETE
TRUNCATE TABLE temp_data; -- 快速清空,重置自增ID,不可回滚
-- DELETE FROM temp_data; -- 逐行删除,可回滚,保留自增ID
-- 删除重复数据
DELETE t1 FROM employees t1
INNER JOIN employees t2
WHERE t1.id < t2.id -- 保留ID大的(或小的)
AND t1.emp_no = t2.emp_no; -- emp_no相同即为重复
4. 事务处理
sql
-- 开始事务
START TRANSACTION;
-- 或者:BEGIN;
-- 银行转账示例
START TRANSACTION;
-- 第一步:从账户1扣款
UPDATE accounts
SET balance = balance - 1000,
update_time = NOW()
WHERE account_no = 'A001'
AND balance >= 1000; -- 余额检查
-- 检查是否成功
SELECT ROW_COUNT() INTO @rows_affected; -- 获取影响行数
IF @rows_affected = 1 THEN
-- 第二步:向账户2存款
UPDATE accounts
SET balance = balance + 1000,
update_time = NOW()
WHERE account_no = 'A002';
-- 第三步:记录交易流水
INSERT INTO transactions (from_account, to_account, amount, type)
VALUES ('A001', 'A002', 1000, 'TRANSFER');
-- 提交事务
COMMIT;
SELECT '转账成功' AS result;
ELSE
-- 回滚事务(余额不足)
ROLLBACK;
SELECT '转账失败:余额不足' AS result;
END IF;
-- 设置保存点(部分回滚)
START TRANSACTION;
INSERT INTO orders (order_no, amount) VALUES ('O001', 100);
SAVEPOINT sp1; -- 设置保存点
UPDATE inventory SET stock = stock - 1 WHERE product_id = 1;
-- 如果库存更新失败,可以回滚到保存点
-- ROLLBACK TO SAVEPOINT sp1; -- 只回滚库存更新,保留订单
INSERT INTO payments (order_no, amount) VALUES ('O001', 100);
COMMIT; -- 提交所有操作
-- 查看事务状态
SHOW VARIABLES LIKE 'autocommit'; -- 查看是否自动提交
SET autocommit = 0; -- 关闭自动提交(慎用)
三、DQL(数据查询语言)详细示例
1. 基础查询
sql
-- 查询所有列(生产环境慎用)
SELECT * FROM employees;
-- 查询特定列
SELECT
id,
name AS 姓名, -- 使用别名
salary AS 月薪,
salary * 12 AS 年薪 -- 计算列
FROM employees;
-- 条件查询
SELECT name, department, salary
FROM employees
WHERE department = '技术部' -- 等于
AND salary > 8000 -- 大于
AND hire_date BETWEEN '2022-01-01' AND '2023-12-31' -- 范围
AND name LIKE '张%' -- 模糊匹配:张开头
AND (bonus IS NULL OR bonus = 0) -- NULL检查
AND status IN (1, 2, 3); -- IN列表
-- 去重查询
SELECT DISTINCT department FROM employees;
-- 排序
SELECT name, salary, hire_date
FROM employees
WHERE status = 1
ORDER BY
department ASC, -- 先按部门升序
salary DESC, -- 再按薪资降序
hire_date ASC; -- 最后按入职日期升序
-- 限制结果集
SELECT name, salary
FROM employees
ORDER BY salary DESC
LIMIT 10; -- 前10条(高薪榜)
-- 分页查询
SELECT name, salary
FROM employees
ORDER BY id
LIMIT 20 OFFSET 40; -- 每页20条,第3页(跳过前40条)
-- 等价于:LIMIT 40, 20
2. 聚合函数
sql
-- 基本聚合
SELECT
COUNT(*) AS 总人数, -- 统计行数
COUNT(DISTINCT department) AS 部门数, -- 去重统计
AVG(salary) AS 平均薪资, -- 平均值
MAX(salary) AS 最高薪资, -- 最大值
MIN(salary) AS 最低薪资, -- 最小值
SUM(salary) AS 薪资总额 -- 求和
FROM employees
WHERE status = 1;
-- 分组聚合
SELECT
department AS 部门,
COUNT(*) AS 人数,
AVG(salary) AS 平均薪资,
MAX(salary) AS 部门最高薪资,
SUM(salary) AS 部门薪资总额
FROM employees
WHERE status = 1
GROUP BY department -- 按部门分组
HAVING AVG(salary) > 10000 -- 分组后过滤(平均薪资>1万)
ORDER BY AVG(salary) DESC; -- 按平均薪资降序
-- 分组聚合扩展
SELECT
department,
gender,
COUNT(*) AS 人数,
AVG(salary) AS 平均薪资
FROM employees
GROUP BY department, gender -- 多列分组
WITH ROLLUP; -- 添加小计和总计行
3. 多表连接查询
sql
-- 内连接(INNER JOIN) - 只返回匹配的行
SELECT
e.name AS 员工姓名,
d.name AS 部门名称,
p.name AS 项目名称
FROM employees e
INNER JOIN departments d ON e.department_id = d.id -- 员工-部门
INNER JOIN projects p ON e.project_id = p.id -- 员工-项目
WHERE d.status = 'active'
AND p.status = 'ongoing';
-- 左连接(LEFT JOIN) - 返回左表所有行
SELECT
e.name AS 员工姓名,
d.name AS 部门名称
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id -- 即使没部门的员工也显示
WHERE e.status = 1;
-- 右连接(RIGHT JOIN) - 返回右表所有行
SELECT
d.name AS 部门名称,
COUNT(e.id) AS 员工数
FROM departments d
RIGHT JOIN employees e ON d.id = e.department_id -- 显示所有员工,即使没对应部门
GROUP BY d.name;
-- 全外连接(MySQL通过UNION实现)
SELECT e.name, d.name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id
UNION -- 合并结果,去重
SELECT e.name, d.name
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.id
WHERE e.id IS NULL; -- 只取右表独有的
-- 自连接(同一表内连接)
SELECT
e1.name AS 员工姓名,
e2.name AS 上级领导,
e1.salary AS 员工薪资,
e2.salary AS 领导薪资
FROM employees e1
LEFT JOIN employees e2 ON e1.manager_id = e2.id -- 连接自己的经理
WHERE e1.salary > e2.salary; -- 薪资比领导高的员工
4. 子查询
sql
-- 标量子查询(返回单个值)
SELECT name, salary,
(SELECT AVG(salary) FROM employees) AS 公司平均薪资,
salary - (SELECT AVG(salary) FROM employees) AS 与平均的差距
FROM employees;
-- 列子查询(返回一列)
SELECT name, department, salary
FROM employees
WHERE department_id IN (
SELECT id FROM departments
WHERE location = '北京' -- 在北京的部门
);
-- 行子查询(返回一行)
SELECT name, salary
FROM employees
WHERE (department_id, salary) = ( -- 同时匹配部门和薪资
SELECT department_id, MAX(salary)
FROM employees
WHERE department_id = 1
);
-- 表子查询(返回多行多列)
SELECT dept_stats.*
FROM (
SELECT
department,
COUNT(*) as emp_count,
AVG(salary) as avg_salary
FROM employees
GROUP BY department
) AS dept_stats -- 必须使用别名
WHERE dept_stats.avg_salary > 10000;
-- EXISTS / NOT EXISTS
SELECT d.name AS 部门名称
FROM departments d
WHERE EXISTS ( -- 存在员工的部门
SELECT 1 FROM employees e
WHERE e.department_id = d.id
AND e.salary > 20000
);
-- 相关子查询(引用外部查询)
SELECT e1.name, e1.salary, e1.department
FROM employees e1
WHERE e1.salary > (
SELECT AVG(e2.salary)
FROM employees e2
WHERE e2.department = e1.department -- 引用外部查询的部门
);
5. 高级查询技巧
sql
-- CASE表达式
SELECT
name,
salary,
CASE
WHEN salary >= 20000 THEN '高薪'
WHEN salary >= 10000 THEN '中薪'
WHEN salary >= 5000 THEN '普薪'
ELSE '低薪'
END AS 薪资等级,
CASE department
WHEN '技术部' THEN '技术序列'
WHEN '销售部' THEN '业务序列'
WHEN '人事部' THEN '职能序列'
ELSE '其他序列'
END AS 序列类型
FROM employees;
-- 窗口函数(MySQL 8.0+)
SELECT
name,
department,
salary,
ROW_NUMBER() OVER (ORDER BY salary DESC) AS 公司排名, -- 行号
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS 部门排名, -- 排名(会跳过)
DENSE_RANK() OVER (ORDER BY salary DESC) AS 密集排名, -- 排名(不跳过)
NTILE(4) OVER (ORDER BY salary DESC) AS 薪资四分位, -- 分桶
LAG(salary, 1) OVER (ORDER BY hire_date) AS 上月薪资, -- 上一行
LEAD(salary, 1) OVER (ORDER BY hire_date) AS 下月薪资, -- 下一行
FIRST_VALUE(salary) OVER (PARTITION BY department ORDER BY hire_date) AS 部门最早薪资,
LAST_VALUE(salary) OVER (PARTITION BY department ORDER BY hire_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS 部门最新薪资,
SUM(salary) OVER (PARTITION BY department) AS 部门薪资总额, -- 累计
AVG(salary) OVER (ORDER BY hire_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS 最近三月平均 -- 滑动平均
FROM employees;
-- 公用表表达式CTE(MySQL 8.0+)
WITH RECURSIVE cte_org AS (
-- 锚点查询(起始点)
SELECT
id,
name,
parent_id,
1 AS level,
CAST(id AS CHAR(100)) AS path
FROM organization
WHERE parent_id IS NULL -- 顶层节点
UNION ALL
-- 递归查询
SELECT
o.id,
o.name,
o.parent_id,
c.level + 1,
CONCAT(c.path, '->', o.id)
FROM organization o
INNER JOIN cte_org c ON o.parent_id = c.id
)
SELECT * FROM cte_org ORDER BY path;
-- JSON函数(MySQL 5.7+)
SELECT
id,
name,
JSON_EXTRACT(profile, '$.skills') AS 技能, -- 提取JSON字段
JSON_CONTAINS(profile, '"MySQL"', '$.skills') AS 会MySQL, -- 检查包含
JSON_LENGTH(profile, '$.projects') AS 项目数量 -- JSON数组长度
FROM employees
WHERE JSON_VALID(profile) = 1; -- 验证JSON格式
6. 联合查询
sql
-- UNION:合并结果,去重
SELECT '在职' AS 类型, COUNT(*) AS 人数 FROM employees WHERE status = 1
UNION
SELECT '离职' AS 类型, COUNT(*) AS 人数 FROM employees WHERE status = 0
UNION
SELECT '总计' AS 类型, COUNT(*) AS 人数 FROM employees;
-- UNION ALL:合并结果,不去重(更快)
SELECT name, salary FROM current_employees
UNION ALL
SELECT name, salary FROM former_employees
ORDER BY salary DESC;
-- 使用UNION实现全外连接
SELECT e.name, d.name
FROM employees e LEFT JOIN departments d ON e.department_id = d.id
UNION
SELECT e.name, d.name
FROM employees e RIGHT JOIN departments d ON e.department_id = d.id
WHERE e.id IS NULL;
7. 查询优化和技巧
sql
-- 使用EXPLAIN分析查询
EXPLAIN
SELECT e.name, d.name, p.name
FROM employees e
JOIN departments d ON e.department_id = d.id
JOIN projects p ON e.project_id = p.id
WHERE e.salary > 10000
AND d.location = '北京'
ORDER BY e.hire_date;
-- 使用索引提示
SELECT /*+ INDEX(e idx_salary) */ e.name, e.salary
FROM employees e FORCE INDEX (idx_salary) -- 强制使用索引
WHERE e.salary BETWEEN 5000 AND 15000;
-- 避免SELECT *
SELECT id, name, email FROM users; -- 只查询需要的列
-- 使用覆盖索引
-- 创建索引覆盖所有查询字段
CREATE INDEX idx_covering ON employees(department_id, status, salary);
-- 然后查询时可以直接从索引获取数据
SELECT department_id, COUNT(*), AVG(salary)
FROM employees
WHERE status = 1
GROUP BY department_id; -- 可以使用覆盖索引
-- 分页优化
-- 不好的分页(偏移量大时慢)
SELECT * FROM large_table LIMIT 1000000, 20;
-- 优化的分页(使用游标)
SELECT * FROM large_table
WHERE id > 1000000 -- 记住上一页最后一条的ID
ORDER BY id
LIMIT 20;
-- 使用派生表延迟关联
SELECT *
FROM employees e
INNER JOIN (
SELECT id
FROM employees
WHERE department_id = 1
ORDER BY hire_date
LIMIT 100000, 20 -- 先在子查询中分页
) AS tmp ON e.id = tmp.id;
四、DCL(数据控制语言)详细示例
1. 用户管理
sql
-- 创建用户
CREATE USER 'app_user'@'localhost'
IDENTIFIED BY 'StrongPass123!' -- 密码
PASSWORD EXPIRE INTERVAL 90 DAY -- 90天过期
FAILED_LOGIN_ATTEMPTS 5 -- 最大失败尝试次数
PASSWORD_LOCK_TIME 1; -- 锁定1天
-- 创建用户指定认证插件
CREATE USER 'legacy_user'@'%'
IDENTIFIED WITH mysql_native_password -- 使用旧版认证
BY 'OldStylePass';
-- 修改用户
ALTER USER 'app_user'@'localhost'
IDENTIFIED BY 'NewPass456!' -- 修改密码
PASSWORD EXPIRE NEVER -- 密码永不过期
ACCOUNT LOCK; -- 锁定账号
ALTER USER 'app_user'@'localhost'
ACCOUNT UNLOCK; -- 解锁账号
-- 重命名用户
RENAME USER 'old_user'@'localhost'
TO 'new_user'@'localhost';
-- 删除用户
DROP USER IF EXISTS 'test_user'@'%';
-- 查看用户
SELECT User, Host, account_locked, password_expired
FROM mysql.user
WHERE User LIKE '%app%'; -- 查找包含app的用户
2. 权限管理
sql
-- 授予数据库级别权限
GRANT
SELECT, INSERT, UPDATE, DELETE, -- 数据操作权限
CREATE, ALTER, DROP, -- 结构操作权限
INDEX, -- 索引权限
CREATE VIEW, SHOW VIEW -- 视图权限
ON company_db.* -- 所有表
TO 'dev_user'@'localhost';
-- 授予表级别权限
GRANT
SELECT (id, name, email), -- 列级别权限(只允许查特定列)
INSERT (name, email), -- 只允许插入特定列
UPDATE (name, email) -- 只允许更新特定列
ON company_db.users -- 指定表
TO 'api_user'@'192.168.1.%'; -- 指定IP段
-- 授予存储过程权限
GRANT EXECUTE
ON PROCEDURE company_db.calculate_bonus
TO 'finance_user'@'localhost';
-- 授予代理权限
GRANT PROXY
ON 'manager'@'localhost' -- 被代理用户
TO 'assistant'@'localhost'; -- 代理用户
-- 授予WITH GRANT OPTION权限
GRANT SELECT
ON company_db.salary_data
TO 'hr_manager'@'localhost'
WITH GRANT OPTION; -- 允许该用户将此权限授予他人
-- 授予角色
CREATE ROLE 'read_only_role';
GRANT SELECT ON *.* TO 'read_only_role';
CREATE USER 'reporter1'@'localhost';
GRANT 'read_only_role' TO 'reporter1'@'localhost';
SET DEFAULT ROLE 'read_only_role' TO 'reporter1'@'localhost';
-- 撤销权限
REVOKE DELETE
ON company_db.*
FROM 'dev_user'@'localhost';
REVOKE ALL PRIVILEGES, GRANT OPTION -- 撤销所有权限和授权权限
ON *.*
FROM 'misbehaving_user'@'%';
-- 撤销角色
REVOKE 'read_only_role'
FROM 'reporter1'@'localhost';
-- 刷新权限(使更改立即生效)
FLUSH PRIVILEGES;
-- 查看权限
-- 查看当前用户权限
SHOW GRANTS;
-- 查看特定用户权限
SHOW GRANTS FOR 'app_user'@'localhost';
-- 查看用户拥有的角色
SHOW GRANTS FOR 'reporter1'@'localhost' USING 'read_only_role';
-- 查看mysql系统表中的权限
SELECT *
FROM mysql.user
WHERE user = 'app_user'\G -- \G表示垂直显示结果
3. 权限层级示例
sql
-- 1. 全局权限(所有数据库的所有表)
GRANT ALL PRIVILEGES ON *.*
TO 'global_admin'@'localhost'
WITH GRANT OPTION;
-- 2. 数据库级别权限(特定数据库的所有表)
GRANT ALL PRIVILEGES ON company_db.*
TO 'db_admin'@'localhost';
-- 3. 表级别权限(特定表)
GRANT SELECT, INSERT, UPDATE, DELETE
ON company_db.employees
TO 'hr_staff'@'localhost';
-- 4. 列级别权限(特定列)
GRANT SELECT (id, name, department)
ON company_db.employees
TO 'auditor'@'localhost';
GRANT UPDATE (name, email)
ON company_db.users
TO 'operator'@'localhost';
-- 5. 存储程序权限
GRANT EXECUTE
ON FUNCTION company_db.calculate_age
TO 'analyst'@'localhost';
GRANT EXECUTE
ON PROCEDURE company_db.monthly_report
TO 'manager'@'localhost';
-- 6. 代理权限
GRANT PROXY
ON 'admin_user'@'localhost'
TO 'assistant'@'localhost';
4. 安全最佳实践
sql
-- 1. 遵循最小权限原则
CREATE USER 'web_app'@'app_server_ip'
IDENTIFIED BY '加密密码';
-- 只授予必要权限
GRANT SELECT, INSERT, UPDATE, DELETE
ON app_db.*
TO 'web_app'@'app_server_ip';
-- 2. 使用角色管理权限
-- 创建角色
CREATE ROLE 'app_read_only';
CREATE ROLE 'app_read_write';
-- 为角色授权
GRANT SELECT ON app_db.* TO 'app_read_only';
GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app_read_write';
-- 将角色分配给用户
CREATE USER 'user1'@'%' IDENTIFIED BY 'pass1';
CREATE USER 'user2'@'%' IDENTIFIED BY 'pass2';
GRANT 'app_read_only' TO 'user1'@'%';
GRANT 'app_read_write' TO 'user2'@'%';
-- 激活角色
SET DEFAULT ROLE ALL TO 'user1'@'%';
-- 3. 定期审计权限
-- 查看所有有SUPER权限的用户
SELECT User, Host
FROM mysql.user
WHERE Super_priv = 'Y';
-- 查看有GRANT权限的用户
SELECT User, Host
FROM mysql.user
WHERE Grant_priv = 'Y';
-- 查看可以创建用户的账号
SELECT User, Host
FROM mysql.user
WHERE Create_user_priv = 'Y';
-- 4. 密码策略管理
-- 设置密码策略(MySQL 8.0+)
SET GLOBAL validate_password.policy = STRONG; -- 强密码策略
SET GLOBAL validate_password.length = 12; -- 最小长度12
SET GLOBAL validate_password.mixed_case_count = 2; -- 至少2个大小写字母
SET GLOBAL validate_password.number_count = 2; -- 至少2个数字
SET GLOBAL validate_password.special_char_count = 1; -- 至少1个特殊字符
-- 5. 清理无用账户
-- 查找长时间未使用的账户
SELECT User, Host, password_last_changed
FROM mysql.user
WHERE password_last_changed < DATE_SUB(NOW(), INTERVAL 180 DAY)
OR (account_locked = 'Y' AND User NOT IN ('root', 'mysql.sys'));
-- 删除测试账户
DROP USER IF EXISTS 'test'@'%', 'demo'@'localhost';
-- 6. 安全连接要求
-- 要求SSL连接
CREATE USER 'secure_user'@'%'
IDENTIFIED BY 'password'
REQUIRE SSL; -- 必须使用SSL
-- 要求X509证书
CREATE USER 'vip_user'@'%'
IDENTIFIED BY 'password'
REQUIRE X509; -- 需要客户端证书
-- 指定允许的IP段
CREATE USER 'internal_user'@'192.168.1.%' -- 只允许内网IP段
IDENTIFIED BY 'password';
5. 权限问题排查
sql
-- 查看当前会话权限
SELECT CURRENT_USER(); -- 当前连接用户
SHOW GRANTS; -- 当前用户权限
-- 检查是否有特定权限
SELECT
*
FROM information_schema.user_privileges
WHERE grantee LIKE '%app_user%';
-- 检查表权限
SELECT
grantee,
table_schema,
table_name,
privilege_type
FROM information_schema.table_privileges
WHERE table_name = 'employees'
AND grantee LIKE '%user%';
-- 检查列权限
SELECT
grantee,
table_schema,
table_name,
column_name,
privilege_type
FROM information_schema.column_privileges
WHERE table_name = 'employees';
-- 模拟其他用户权限
-- 首先要有PROXY权限
GRANT PROXY ON 'target_user'@'localhost' TO CURRENT_USER();
-- 然后可以模拟
SET ROLE 'target_user'@'localhost';
SHOW GRANTS; -- 现在看到的是target_user的权限
-- 重置
SET ROLE NONE;
五、SQL执行计划和优化
sql
-- 查看执行计划
EXPLAIN FORMAT=TREE -- 树形格式(MySQL 8.0+)
SELECT * FROM employees
WHERE department_id = 1
AND salary > 10000;
EXPLAIN ANALYZE -- 实际执行分析(MySQL 8.0.18+)
SELECT * FROM employees
WHERE department_id = 1
AND salary > 10000;
-- 执行计划各列含义:
-- id: 查询序号
-- select_type: 查询类型(SIMPLE, PRIMARY, SUBQUERY等)
-- table: 访问的表
-- partitions: 匹配的分区
-- type: 访问类型(ALL, index, range, ref等)
-- possible_keys: 可能使用的索引
-- key: 实际使用的索引
-- key_len: 索引长度
-- ref: 与索引比较的列
-- rows: 预估需要检查的行数
-- filtered: 按条件过滤的行百分比
-- Extra: 额外信息
-- 强制使用或不使用索引
SELECT * FROM employees USE INDEX (idx_department) WHERE department_id = 1;
SELECT * FROM employees IGNORE INDEX (idx_department) WHERE department_id = 1;
SELECT * FROM employees FORCE INDEX (idx_department) WHERE department_id = 1;
-- 查询缓存(MySQL 8.0已移除,但旧版本可用)
SHOW VARIABLES LIKE 'query_cache%';
SET GLOBAL query_cache_size = 1000000;
总结
通过以上详细的注释和示例,我们可以看到:
- DDL 关注数据库结构,操作要谨慎,生产环境需要在线DDL工具
- DML 关注数据内容,要使用事务保证一致性,注意WHERE条件
- DQL 使用最频繁,需要优化查询性能,合理使用索引
- DCL 关注安全性,要遵循最小权限原则,定期审计
每种SQL类型都有其特定的语法和最佳实践。掌握这些细节,能够帮助你写出更高效、更安全、更易维护的数据库代码。记住:理解原理,实践验证,持续优化是成为SQL高手的必经之路。