MySQL 基础语法完全指南
概述
MySQL 是最流行的开源关系型数据库之一,SQL(Structured Query Language)是操作 MySQL 的标准语言。无论是简单的数据查询还是复杂的多表联结,SQL 都是每个后端开发者必须掌握的核心技能。本文系统介绍 MySQL 的基础语法,覆盖了数据定义(DDL)、数据操作(DML)、查询优化和常见函数,帮助你快速上手并写出高效的 SQL 语句。
基础语法
SQL 语句可以分为几大类:DDL(Data Definition Language)用于定义数据库对象(表、索引、视图),DML(Data Manipulation Language)用于操作数据(增删改查),DCL(Data Control Language)用于权限控制。在实际开发中,SELECT(查询)是使用最频繁的部分,也是最复杂的部分。
- SELECT:从表中查询数据,是 SQL 最核心的部分。
- INSERT:向表中插入新记录。
- UPDATE:修改表中已存在的记录。
- DELETE:删除表中的记录(注意加 WHERE 条件)。
- CREATE/ALTER/DROP:DDL 语句,创建或修改表结构。
理解 SQL 的执行顺序有助于写出正确的子查询。实际执行顺序是:FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT,而不是按照书写顺序。
完整代码示例+注释
-- === 创建数据库和表 ===
CREATE DATABASE IF NOT EXISTS shop DEFAULT CHARSET utf8mb4;
USE shop;
-- 用户表
CREATE TABLE IF NOT EXISTS users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_email (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 订单表
CREATE TABLE IF NOT EXISTS orders (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
total_amount DECIMAL(10,2) NOT NULL DEFAULT 0,
status ENUM('pending','paid','shipped','completed','cancelled') DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- === 插入数据 ===
INSERT INTO users (username, email) VALUES
('张三', 'zhangsan@example.com'),
('李四', 'lisi@example.com'),
('王五', 'wangwu@example.com');
INSERT INTO orders (user_id, total_amount, status) VALUES
(1, 299.00, 'paid'),
(1, 150.50, 'shipped'),
(2, 89.00, 'pending'),
(3, 520.00, 'completed');
-- === 基础查询 ===
-- 查询所有用户
SELECT id, username, email FROM users;
-- 带 WHERE 条件过滤
SELECT * FROM orders WHERE status = 'paid' AND total_amount > 100;
-- 聚合统计
SELECT
COUNT(*) as total_orders,
SUM(total_amount) as total_revenue,
AVG(total_amount) as avg_order_value,
MAX(total_amount) as max_order
FROM orders;
-- GROUP BY 分组统计
SELECT
u.username,
COUNT(o.id) as order_count,
SUM(o.total_amount) as total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username
HAVING order_count > 0
ORDER BY total_spent DESC
LIMIT 10;
-- === 子查询:查询消费最高的前3名用户 ===
SELECT u.username, total_spent
FROM (
SELECT user_id, SUM(total_amount) as total_spent
FROM orders
WHERE status != 'cancelled'
GROUP BY user_id
) o
JOIN users u ON o.user_id = u.id
ORDER BY total_spent DESC
LIMIT 3;
-- === 联合查询:同时查询多个状态 ===
SELECT id, total_amount, '高单价订单' as tag
FROM orders
WHERE total_amount > 200
UNION ALL
SELECT id, total_amount, '待处理订单' as tag
FROM orders
WHERE status = 'pending'
ORDER BY total_amount DESC;
-- === 更新和删除(注意 WHERE) ===
-- 将所有 pending 订单更新为 cancelled(超过30天)
UPDATE orders
SET status = 'cancelled', updated_at = NOW()
WHERE status = 'pending'
AND created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
-- 删除指定用户的所有订单(先删子表再删主表)
DELETE FROM orders WHERE user_id = 1;
DELETE FROM users WHERE id = 1;
运行效果
执行上述 SQL 后,shop 数据库中会创建 users 和 orders 两张表,并插入测试数据。GROUP BY 查询会输出每个用户的订单数和累计消费金额,按消费降序排列。子查询示例找出消费最高的前3名用户并显示用户名和消费总额。UNION ALL 示例将高单价订单和待处理订单合并展示,并标注来源标签。
UPDATE 和 DELETE 操作特别强调 WHERE 条件,缺少 WHERE 会导致全表数据被修改或清空。在生产环境中执行 UPDATE/DELETE 前,建议先用 SELECT 确认影响范围,或者在事务中执行并设置合适的隔离级别。
常见问题
Q1:为什么查询很慢,怎么优化
首先检查是否在 WHERE 条件列上建立了索引(用 EXPLAIN 查看执行计划)。避免在索引列上使用函数或运算(如 WHERE YEAR(created_at) = 2024),这会导致索引失效。LIMIT 分页时,使用覆盖索引(covering index)避免回表。对于大表 JOIN,确保关联字段类型一致且都有索引。
Q2:LEFT JOIN 和 INNER JOIN 的区别
INNER JOIN 只保留两表都匹配的记录,如果一方的关联字段为 NULL 或找不到匹配则被过滤掉。LEFT JOIN 保留左表全部记录,右表没有匹配的字段填充为 NULL。右表需要用 IS NOT NULL 条件过滤才能达到类似 INNER JOIN 的效果,但语义不同。
Q3:如何避免 SQL 注入
永远不要将用户输入直接拼接到 SQL 字符串中。使用预处理语句(Prepared Statement),在 PHP 中用 PDO 或 MySQLi,在 Python 中用 mysql-connector 或 SQLAlchemy ORM。预处理语句将 SQL 结构和数据分离,数据库引擎会自动转义特殊字符。
Q4:DECIMAL 和 FLOAT/DOUBLE 的区别
DECIMAL 是精确数值类型,适合存储金额等需要精确计算的数据,运算速度稍慢。FLOAT 和 DOUBLE 是浮点数类型,存储近似值,在涉及大量计算或比较时可能产生精度误差。金融类系统务必使用 DECIMAL(10,2) 等精确类型。
延伸阅读
- MySQL 官方文档:https://dev.mysql.com/doc/refman/8.0/en/ — 权威的 MySQL 8.0 完整参考手册。
- EXPLAIN 分析:使用 EXPLAIN 查看查询执行计划,是分析 SQL 性能瓶颈的必备工具。
- MySQL Performance Blog:Percona 团队的博客,提供大量 MySQL 深度调优案例。