Skip to content

MySQL SQL 编写规范

命名规范

1. 数据库命名

  • 命名规则:使用小写字母、数字和下划线组合,避免使用特殊字符
  • 命名长度:建议不超过 32 个字符,符合 MySQL 标识符长度限制
  • 命名格式:采用业务名称或项目名称,如 ecommerce, blog_platform
  • 示例
    sql
    -- 推荐
    CREATE DATABASE ecommerce;
    CREATE DATABASE blog_platform;
    
    -- 不推荐
    CREATE DATABASE ECommerce; -- 大小写混合
    CREATE DATABASE 电商平台; -- 中文名称
    CREATE DATABASE my-db; -- 特殊字符

2. 表命名

  • 命名规则:使用小写字母、数字和下划线组合,避免使用特殊字符
  • 命名长度:建议不超过 32 个字符
  • 命名格式:采用 业务模块_表名表名 格式,如 user_info, order_detail
  • 避免使用保留字:如 order, table, select
  • 示例
    sql
    -- 推荐
    CREATE TABLE user_info (
        id INT PRIMARY KEY,
        username VARCHAR(50) NOT NULL,
        email VARCHAR(100) NOT NULL
    );
    
    CREATE TABLE order_detail (
        id INT PRIMARY KEY,
        order_id INT NOT NULL,
        product_id INT NOT NULL,
        quantity INT NOT NULL
    );
    
    -- 不推荐
    CREATE TABLE UserInfo; -- 驼峰命名
    CREATE TABLE order; -- 保留字
    CREATE TABLE 订单详情; -- 中文名称

3. 字段命名

  • 命名规则:使用小写字母、数字和下划线组合,避免使用特殊字符
  • 命名长度:建议不超过 32 个字符
  • 命名格式:采用 字段含义业务_字段含义 格式,如 user_id, product_name
  • 避免使用缩写:除非是广为人知的缩写,如 id, url, ip
  • 示例
    sql
    -- 推荐
    CREATE TABLE user (
        id INT PRIMARY KEY,
        username VARCHAR(50) NOT NULL,
        email VARCHAR(100) NOT NULL,
        created_at DATETIME NOT NULL,
        updated_at DATETIME NOT NULL
    );
    
    -- 不推荐
    CREATE TABLE user (
        uid INT PRIMARY KEY, -- 不明确的缩写
        uname VARCHAR(50) NOT NULL, -- 不明确的缩写
        email VARCHAR(100) NOT NULL,
        ctime DATETIME NOT NULL, -- 不明确的缩写
        utime DATETIME NOT NULL -- 不明确的缩写
    );

4. 索引命名

  • 命名规则:使用小写字母、数字和下划线组合
  • 命名格式
    • 主键索引:PRIMARY KEY(MySQL 自动命名为 PRIMARY
    • 唯一索引:uk_表名_字段名,如 uk_user_username
    • 普通索引:idx_表名_字段名,如 idx_user_email
    • 复合索引:idx_表名_字段1_字段2,如 idx_order_userid_createdat
  • 示例
    sql
    -- 推荐
    CREATE TABLE user (
        id INT PRIMARY KEY,
        username VARCHAR(50) NOT NULL,
        email VARCHAR(100) NOT NULL,
        created_at DATETIME NOT NULL,
        UNIQUE KEY uk_user_username (username),
        INDEX idx_user_email (email),
        INDEX idx_user_createdat (created_at)
    );
    
    CREATE TABLE order (
        id INT PRIMARY KEY,
        user_id INT NOT NULL,
        created_at DATETIME NOT NULL,
        status VARCHAR(20) NOT NULL,
        INDEX idx_order_userid_createdat (user_id, created_at)
    );

5. 存储过程和函数命名

  • 命名规则:使用小写字母、数字和下划线组合
  • 命名格式
    • 存储过程:proc_功能描述,如 proc_user_register
    • 函数:func_功能描述,如 func_calculate_total
  • 示例
    sql
    -- 推荐
    DELIMITER //
    CREATE PROCEDURE proc_user_register(
        IN p_username VARCHAR(50),
        IN p_email VARCHAR(100),
        OUT p_user_id INT
    )

BEGIN -- 存储过程逻辑 END // DELIMITER ;

CREATE FUNCTION func_calculate_total( p_quantity INT, p_price DECIMAL(10,2) ) RETURNS DECIMAL(10,2) BEGIN RETURN p_quantity * p_price; END;


## 格式规范

### 1. 缩进和换行

- **缩进**:使用 4 个空格进行缩进,避免使用制表符
- **换行**:
- SELECT、FROM、WHERE、GROUP BY、HAVING、ORDER BY 等关键字单独占一行
- 多个字段或条件时,每个字段或条件单独占一行
- JOIN 子句单独占一行,ON 条件单独占一行
- **示例**:
```sql
-- 推荐
SELECT
    u.id,
    u.username,
    u.email,
    o.id AS order_id,
    o.total_amount,
    o.created_at
FROM
    user u
JOIN
    order o ON u.id = o.user_id
WHERE
    u.created_at >= '2023-01-01'
    AND o.status = 'completed'
ORDER BY
    o.created_at DESC;

-- 不推荐
SELECT u.id, u.username, u.email, o.id AS order_id, o.total_amount, o.created_at FROM user u JOIN order o ON u.id = o.user_id WHERE u.created_at >= '2023-01-01' AND o.status = 'completed' ORDER BY o.created_at DESC;

2. 大小写规范

  • 关键字:SQL 关键字使用大写,如 SELECT、FROM、WHERE、JOIN 等
  • 标识符:数据库名、表名、字段名、索引名等使用小写
  • 函数名:内置函数名使用大写,如 COUNT(), SUM(), DATE() 等
  • 示例
    sql
    -- 推荐
    SELECT
        COUNT(*) AS total_users,
        MAX(created_at) AS latest_user
    FROM
        user
    WHERE
        status = 'active';
    
    -- 不推荐
    select
        count(*) as total_users,
        max(created_at) as latest_user
    from
        User
    where
        Status = 'active';

3. 空格使用

  • 操作符两侧:在 =, +, -, *, /, <>, >=, <= 等操作符两侧添加空格
  • 逗号后:在逗号后添加空格
  • 括号内:括号内不添加空格,如 COUNT(*) 而不是 COUNT( *)
  • 示例
    sql
    -- 推荐
    SELECT
        id,
        username,
        email
    FROM
        user
    WHERE
        age >= 18
        AND status = 'active'
    ORDER BY
        created_at DESC;
    
    -- 不推荐
    SELECT id,username,email FROM user WHERE age>=18 AND status='active' ORDER BY created_at DESC;

4. 注释规范

  • 单行注释:使用 -- (双横线加空格)进行单行注释
  • 多行注释:使用 /* */ 进行多行注释
  • 注释位置
    • 表和字段创建时添加注释
    • 复杂 SQL 语句添加注释
    • 存储过程和函数添加注释
  • 示例
    sql
    -- 推荐
    /*
     * 用户信息表
     * 存储系统用户的基本信息
     */
    CREATE TABLE user_info (
        id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',
        username VARCHAR(50) NOT NULL COMMENT '用户名',
        email VARCHAR(100) NOT NULL COMMENT '邮箱地址',
        created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
        updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
    ) COMMENT='用户信息表';
    
    -- 查询 2023 年注册的活跃用户
    SELECT
        id,
        username,
        email
    FROM
        user_info
    WHERE
        YEAR(created_at) = 2023 -- 2023 年注册
        AND status = 'active'; -- 活跃用户

性能优化规范

1. 查询优化

避免 SELECT *

  • 问题SELECT * 会查询所有字段,增加网络传输开销,可能无法使用覆盖索引
  • 解决方案:明确指定需要查询的字段
  • 示例
    sql
    -- 推荐
    SELECT
        id,
        username,
        email
    FROM
        user;
    
    -- 不推荐
    SELECT * FROM user;

使用 LIMIT 限制返回行数

  • 问题:不使用 LIMIT 可能返回大量数据,影响性能
  • 解决方案:使用 LIMIT 限制返回行数,特别是在分页查询中
  • 示例
    sql
    -- 推荐
    SELECT
        id,
        username,
        email
    FROM
        user
    ORDER BY
        created_at DESC
    LIMIT 10;
    
    -- 分页查询
    SELECT
        id,
        username,
        email
    FROM
        user
    ORDER BY
        created_at DESC
    LIMIT 10 OFFSET 20;

合理使用索引

  • 问题:缺少索引会导致全表扫描,影响查询性能
  • 解决方案:为经常查询的字段创建索引
  • 注意事项
    • 不要过度索引,索引会影响写入性能
    • 考虑联合索引的顺序,将选择性高的字段放在前面
    • 避免在索引字段上使用函数或表达式
  • 示例
    sql
    -- 推荐
    SELECT
        id,
        username,
        email
    FROM
        user
    WHERE
        email = 'user@example.com'; -- email 字段有索引
    
    -- 不推荐
    SELECT
        id,
        username,
        email
    FROM
        user
    WHERE
        SUBSTRING(email, 1, 5) = 'user@'; -- 在索引字段上使用函数

避免在 WHERE 子句中使用函数或表达式

  • 问题:在 WHERE 子句中使用函数或表达式会导致索引失效
  • 解决方案:将函数或表达式移到等号右侧,或使用计算列
  • 示例
    sql
    -- 推荐
    SELECT
        id,
        username,
        email
    FROM
        user
    WHERE
        created_at >= '2023-01-01';
    
    -- 不推荐
    SELECT
        id,
        username,
        email
    FROM
        user
    WHERE
        DATE(created_at) = '2023-01-01'; -- 在索引字段上使用函数

2. 写入优化

使用批量插入

  • 问题:单条插入语句效率低下,特别是大量数据插入时
  • 解决方案:使用批量插入,减少网络往返次数
  • 示例
    sql
    -- 推荐
    INSERT INTO user (username, email) VALUES
        ('user1', 'user1@example.com'),
        ('user2', 'user2@example.com'),
        ('user3', 'user3@example.com');
    
    -- 不推荐
    INSERT INTO user (username, email) VALUES ('user1', 'user1@example.com');
    INSERT INTO user (username, email) VALUES ('user2', 'user2@example.com');
    INSERT INTO user (username, email) VALUES ('user3', 'user3@example.com');

避免使用 INSERT SELECT *

  • 问题INSERT SELECT * 可能插入不需要的字段,影响性能和数据完整性
  • 解决方案:明确指定需要插入的字段
  • 示例
    sql
    -- 推荐
    INSERT INTO user_archive (id, username, email, created_at)
    SELECT id, username, email, created_at FROM user WHERE status = 'inactive';
    
    -- 不推荐
    INSERT INTO user_archive SELECT * FROM user WHERE status = 'inactive';

合理使用事务

  • 问题:长事务会占用锁资源,影响并发性能
  • 解决方案
    • 缩短事务长度,尽快提交或回滚
    • 避免在事务中执行耗时操作
    • 合理设置事务隔离级别
  • 示例
    sql
    -- 推荐
    START TRANSACTION;
    UPDATE order SET status = 'paid' WHERE id = 1;
    INSERT INTO payment (order_id, amount, payment_method) VALUES (1, 100.00, 'credit_card');
    COMMIT;
    
    -- 不推荐
    START TRANSACTION;
    UPDATE order SET status = 'paid' WHERE id = 1;
    -- 执行耗时操作
    DO SLEEP(10);
    INSERT INTO payment (order_id, amount, payment_method) VALUES (1, 100.00, 'credit_card');
    COMMIT;

3. 表设计优化

选择合适的数据类型

  • 问题:使用不合适的数据类型会浪费存储空间,影响性能
  • 解决方案
    • 整数类型:根据范围选择 TINYINT, SMALLINT, INT, BIGINT
    • 字符串类型:根据长度选择 CHAR, VARCHAR, TEXT
    • 日期时间类型:根据精度选择 DATE, DATETIME, TIMESTAMP
  • 示例
    sql
    -- 推荐
    CREATE TABLE user (
        id INT PRIMARY KEY, -- 适用于中小型用户量
        age TINYINT NOT NULL, -- 年龄范围 0-255
        username VARCHAR(50) NOT NULL, -- 可变长度字符串
        created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 时间戳,自动更新
        bio TEXT -- 长文本
    );
    
    -- 不推荐
    CREATE TABLE user (
        id BIGINT PRIMARY KEY, -- 不必要的大类型
        age INT NOT NULL, -- 年龄使用 INT 浪费空间
        username CHAR(50) NOT NULL, -- 固定长度字符串浪费空间
        created_at DATETIME NOT NULL, -- 没有默认值
        bio VARCHAR(65535) -- 应该使用 TEXT 类型
    );

避免使用 NULL

  • 问题:NULL 值会增加查询复杂性,影响索引性能
  • 解决方案
    • 为字段设置默认值
    • 使用 NOT NULL 约束
    • 对于字符串类型,使用空字符串 '' 代替 NULL
  • 示例
    sql
    -- 推荐
    CREATE TABLE user (
        id INT PRIMARY KEY,
        username VARCHAR(50) NOT NULL,
        email VARCHAR(100) NOT NULL,
        status VARCHAR(20) NOT NULL DEFAULT 'active', -- 默认值
        bio TEXT NOT NULL DEFAULT '' -- 空字符串代替 NULL
    );
    
    -- 不推荐
    CREATE TABLE user (
        id INT PRIMARY KEY,
        username VARCHAR(50) NULL,
        email VARCHAR(100) NULL,
        status VARCHAR(20) NULL,
        bio TEXT NULL
    );

合理使用范式

  • 问题:过度范式化会导致复杂的 JOIN 查询,影响性能;过度反范式化会导致数据冗余
  • 解决方案
    • 主要表遵循第三范式
    • 适当反范式化,如在订单表中存储用户名,避免 JOIN 查询
    • 使用缓存或物化视图处理复杂查询

安全规范

1. 避免 SQL 注入

  • 问题:SQL 注入是最常见的数据库安全漏洞
  • 解决方案
    • 使用参数化查询或预处理语句
    • 避免拼接 SQL 语句
    • 对输入进行验证和过滤
  • 示例
    sql
    -- 推荐:使用参数化查询
    PREPARE stmt FROM 'SELECT * FROM user WHERE username = ?';
    SET @username = 'user1';
    EXECUTE stmt USING @username;
    DEALLOCATE PREPARE stmt;
    
    -- 不推荐:直接拼接 SQL
    SET @username = 'user1';
    SET @sql = CONCAT('SELECT * FROM user WHERE username = ''', @username, '''');
    PREPARE stmt FROM @sql;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

2. 最小权限原则

  • 问题:给予过多权限会增加安全风险
  • 解决方案
    • 为用户授予最小必要权限
    • 避免使用 root 用户进行日常操作
    • 定期审查用户权限
  • 示例
    sql
    -- 推荐
    CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'password';
    GRANT SELECT, INSERT, UPDATE, DELETE ON ecommerce.* TO 'app_user'@'localhost';
    
    -- 不推荐
    GRANT ALL PRIVILEGES ON *.* TO 'app_user'@'%' IDENTIFIED BY 'password';

3. 加密敏感数据

  • 问题:敏感数据明文存储会增加数据泄露风险
  • 解决方案
    • 使用 MySQL 加密函数加密敏感数据
    • 使用 SSL/TLS 加密数据传输
    • 考虑使用透明数据加密 (TDE)
  • 示例
    sql
    -- 推荐:使用 AES 加密
    INSERT INTO user (username, password, email)
    VALUES ('user1', AES_ENCRYPT('password123', 'encryption_key'), 'user1@example.com');
    
    SELECT username, AES_DECRYPT(password, 'encryption_key') AS password FROM user WHERE username = 'user1';

4. 避免使用 SELECT * 查询敏感数据

  • 问题:SELECT * 可能返回敏感数据,增加数据泄露风险
  • 解决方案:明确指定需要查询的字段,避免返回敏感数据
  • 示例
    sql
    -- 推荐
    SELECT id, username, email FROM user WHERE id = 1;
    
    -- 不推荐
    SELECT * FROM user WHERE id = 1; -- 可能返回密码等敏感数据

事务处理规范

1. 明确指定事务隔离级别

  • 问题:不同的事务隔离级别会影响并发性能和数据一致性
  • 解决方案:根据业务需求选择合适的隔离级别
  • 隔离级别
    • READ UNCOMMITTED:读取未提交的数据
    • READ COMMITTED:读取已提交的数据
    • REPEATABLE READ:可重复读(MySQL 默认)
    • SERIALIZABLE:串行化
  • 示例
    sql
    -- 推荐:明确指定隔离级别
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
    START TRANSACTION;
    -- 事务操作
    COMMIT;

2. 及时提交或回滚事务

  • 问题:长事务会占用锁资源,影响并发性能
  • 解决方案
    • 尽快提交或回滚事务
    • 避免在事务中执行耗时操作
    • 设置合理的事务超时时间
  • 示例
    sql
    -- 推荐
    START TRANSACTION;
    -- 执行事务操作
    UPDATE order SET status = 'paid' WHERE id = 1;
    INSERT INTO payment (order_id, amount) VALUES (1, 100.00);
    -- 及时提交
    COMMIT;
    
    -- 不推荐
    START TRANSACTION;
    -- 执行事务操作
    UPDATE order SET status = 'paid' WHERE id = 1;
    -- 执行耗时操作
    SELECT SLEEP(10);
    INSERT INTO payment (order_id, amount) VALUES (1, 100.00);
    COMMIT;

3. 处理事务异常

  • 问题:事务异常可能导致数据不一致
  • 解决方案
    • 使用 TRY-CATCH 块处理异常(MySQL 8.0+)
    • 在应用程序中处理事务异常
    • 定期检查和修复数据一致性
  • 示例
    sql
    -- MySQL 8.0+:使用 TRY-CATCH
    START TRANSACTION;
    BEGIN
        UPDATE order SET status = 'paid' WHERE id = 1;
        INSERT INTO payment (order_id, amount) VALUES (1, 100.00);
        COMMIT;
    EXCEPTION
        ROLLBACK;
        -- 处理异常
    END;

存储过程和函数规范

1. 命名和格式

  • 命名:使用小写字母、数字和下划线组合,如 proc_user_register, func_calculate_total
  • 格式
    • 输入参数以 p_ 开头,如 p_username
    • 输出参数以 o_ 开头,如 o_user_id
    • 局部变量以 v_ 开头,如 v_total
  • 示例
    sql
    DELIMITER //
    CREATE PROCEDURE proc_user_register(
        IN p_username VARCHAR(50),
        IN p_email VARCHAR(100),
        OUT o_user_id INT
    )

BEGIN DECLARE v_existing_count INT;

-- 检查用户名是否已存在
SELECT COUNT(*) INTO v_existing_count FROM user WHERE username = p_username;

IF v_existing_count = 0 THEN
    INSERT INTO user (username, email) VALUES (p_username, p_email);
    SET o_user_id = LAST_INSERT_ID();
ELSE
    SET o_user_id = NULL;
END IF;

END // DELIMITER ;


### 2. 错误处理

- **问题**:缺少错误处理会导致存储过程或函数异常终止
- **解决方案**:
- 使用 DECLARE HANDLER 处理异常
- 设置合理的错误码和错误信息
- 记录错误日志
- **示例**:
```sql
DELIMITER //
CREATE PROCEDURE proc_deposit(
    IN p_account_id INT,
    IN p_amount DECIMAL(10,2)
)
BEGIN
  DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
      ROLLBACK;
      SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '存款失败';
  END;
  
  START TRANSACTION;
  UPDATE account SET balance = balance + p_amount WHERE id = p_account_id;
  INSERT INTO transaction (account_id, type, amount) VALUES (p_account_id, 'deposit', p_amount);
  COMMIT;
END //
DELIMITER ;

3. 性能优化

  • 问题:存储过程和函数可能导致性能问题
  • 解决方案
    • 避免在存储过程中使用游标,使用集合操作代替
    • 合理使用索引
    • 避免在存储过程中执行大量数据操作
    • 定期优化和重构存储过程

常见问题(FAQ)

Q1: 为什么要遵循 SQL 编写规范?

A1: 遵循 SQL 编写规范的好处:

  • 提高可读性:统一的格式和命名使 SQL 语句更容易阅读和理解
  • 提高可维护性:规范的代码便于团队协作和后续维护
  • 提高性能:合理的 SQL 编写可以提高查询和写入性能
  • 提高安全性:遵循安全规范可以减少 SQL 注入等安全漏洞
  • 符合最佳实践:遵循行业最佳实践,避免常见错误

Q2: 如何选择合适的数据类型?

A2: 选择数据类型时需要考虑以下因素:

  • 数据范围:根据数据的实际范围选择合适的类型
  • 存储空间:选择占用空间最小的类型
  • 查询性能:考虑索引性能和查询效率
  • 数据完整性:选择能够确保数据完整性的类型
  • 兼容性:考虑与应用程序的兼容性

Q3: 如何避免 SQL 注入?

A3: 避免 SQL 注入的方法:

  • 使用参数化查询或预处理语句
  • 避免拼接 SQL 语句
  • 对输入进行验证和过滤
  • 限制用户权限,遵循最小权限原则
  • 使用存储过程处理复杂逻辑

Q4: 如何优化查询性能?

A4: 优化查询性能的方法:

  • 为经常查询的字段创建索引
  • 避免使用 SELECT *,明确指定需要的字段
  • 避免在 WHERE 子句中使用函数或表达式
  • 使用 LIMIT 限制返回行数
  • 合理使用 JOIN 查询,避免过多 JOIN
  • 考虑使用缓存或物化视图

Q5: 如何处理长事务?

A5: 处理长事务的方法:

  • 缩短事务长度,尽快提交或回滚
  • 避免在事务中执行耗时操作
  • 合理设置事务隔离级别
  • 使用乐观锁代替悲观锁
  • 考虑将长事务拆分为多个短事务

Q6: 如何选择合适的索引策略?

A6: 选择索引策略时需要考虑以下因素:

  • 查询模式:根据常见的查询条件创建索引
  • 数据分布:考虑字段的数据分布,选择选择性高的字段
  • 写入性能:索引会影响写入性能,避免过度索引
  • 联合索引顺序:将选择性高的字段放在前面
  • 覆盖索引:考虑创建覆盖索引,避免回表查询

Q7: 如何处理 NULL 值?

A7: 处理 NULL 值的方法:

  • 为字段设置默认值
  • 使用 NOT NULL 约束
  • 对于字符串类型,使用空字符串 '' 代替 NULL
  • 在查询中使用 IS NULL 或 IS NOT NULL 进行判断
  • 考虑使用 COALESCE() 函数处理 NULL 值

Q8: 如何确保数据安全性?

A8: 确保数据安全性的方法:

  • 使用参数化查询避免 SQL 注入
  • 遵循最小权限原则,限制用户权限
  • 加密敏感数据
  • 使用 SSL/TLS 加密数据传输
  • 定期备份数据
  • 实施访问控制和审计

案例分析

案例1:优化慢查询

场景描述

某电商平台的订单查询页面响应时间较长,经分析发现是以下 SQL 查询导致的:

sql
SELECT * FROM order WHERE user_id = 123 AND created_at >= '2023-01-01' ORDER BY id DESC;

问题分析

  • 使用了 SELECT *,返回不必要的字段
  • order 表有 1000 万条记录,没有合适的索引
  • 查询条件包含 user_id 和 created_at,但只有 user_id 上有索引

解决方案

  1. 明确指定需要的字段,避免使用 SELECT *
  2. 创建联合索引 idx_order_userid_createdat (user_id, created_at)
  3. 优化后的 SQL:
sql
SELECT id, order_no, total_amount, status, created_at 
FROM order 
WHERE user_id = 123 AND created_at >= '2023-01-01' 
ORDER BY id DESC;

优化效果

  • 查询响应时间从 5 秒降低到 0.1 秒
  • 扫描的行数从 100 万减少到 100
  • 索引利用率从 20% 提高到 100%

案例2:避免 SQL 注入

场景描述

某应用程序使用以下方式执行 SQL 查询:

java
String username = request.getParameter("username");
String password = request.getParameter("password");
String sql = "SELECT * FROM user WHERE username = '" + username + "' AND password = '" + password + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);

问题分析

  • 直接拼接 SQL 语句,存在 SQL 注入风险
  • 攻击者可以通过输入 ' OR '1'='1 绕过身份验证

解决方案

使用预处理语句:

java
String username = request.getParameter("username");
String password = request.getParameter("password");
String sql = "SELECT * FROM user WHERE username = ? AND password = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();

优化效果

  • 消除了 SQL 注入风险
  • 提高了查询性能,预处理语句可以被缓存
  • 代码更安全,更易于维护