Skip to content

MySQL SQL书写规范

SQL书写规范概述

为什么需要SQL书写规范

SQL书写规范是确保SQL语句具有良好可读性、高性能和可维护性的一组规则。良好的SQL书写规范可以:

  • 提高可读性:便于团队成员理解和维护
  • 减少错误:标准化的书写方式减少语法错误
  • 提高性能:规范的SQL更容易被优化器识别和优化
  • 便于调试:结构清晰的SQL更容易定位问题
  • 促进团队协作:统一的书写风格便于团队成员之间的交流

书写规范的基本原则

  1. 可读性优先:清晰的结构比简洁更重要
  2. 性能优化:书写时考虑查询性能
  3. 一致性:团队内保持一致的书写风格
  4. 安全性:避免SQL注入等安全问题
  5. 可维护性:便于后续修改和扩展
  6. 标准化:遵循行业标准和最佳实践

SQL基础书写规范

命名规范

数据库对象命名

对象类型命名规则示例
数据库小写字母,下划线分隔,简短user_center
小写字母,下划线分隔,单数形式user
视图前缀v_,小写字母,下划线分隔v_active_user
索引前缀idx_uk_,小写字母,下划线分隔idx_usernameuk_email
存储过程前缀proc_,小写字母,下划线分隔proc_generate_report
函数前缀func_,小写字母,下划线分隔func_calculate_score
触发器前缀trig_,小写字母,下划线分隔trig_update_user
临时表前缀tmp_,小写字母,下划线分隔tmp_order_statistics

字段命名

规则示例
小写字母,下划线分隔user_idcreated_at
避免使用保留字不使用selectfromwhere
语义明确不使用col1data等模糊命名
保持一致性相同含义的字段在不同表中使用相同名称
使用标准后缀_id(主键/外键),_at(时间),_flag(布尔值),_count(计数)

格式规范

大小写规范

元素规范示例
SQL关键字大写SELECTFROMWHERE
数据库对象小写usersuser_id
函数名大写SUM()COUNT()DATE_FORMAT()
变量小写@user_id
常量大写'ACTIVE'100

缩进和换行

  1. SELECT语句格式

    sql
    SELECT
        u.user_id,
        u.username,
        u.email,
        o.order_count,
        o.total_amount
    FROM
        users u
    LEFT JOIN
        (
            SELECT
                user_id,
                COUNT(*) AS order_count,
                SUM(amount) AS total_amount
            FROM
                orders
            GROUP BY
                user_id
        ) o ON u.user_id = o.user_id
    WHERE
        u.status = 'ACTIVE'
        AND u.created_at >= '2023-01-01'
        AND u.age > 18
    ORDER BY
        u.created_at DESC
    LIMIT
        10 OFFSET 20;
  2. UPDATE语句格式

    sql
    UPDATE
        users
    SET
        status = 'INACTIVE',
        updated_at = NOW(),
        last_login = NULL
    WHERE
        user_id = 123
        AND status = 'ACTIVE';
  3. INSERT语句格式

    sql
    INSERT INTO
        users (username, email, status, created_at)
    VALUES
        ('john_doe', 'john@example.com', 'ACTIVE', NOW()),
        ('jane_smith', 'jane@example.com', 'ACTIVE', NOW());
  4. DELETE语句格式

    sql
    DELETE FROM
        users
    WHERE
        user_id IN (1, 2, 3)
        AND created_at < '2022-01-01';

空格和对齐

  1. 逗号后加空格

    sql
    -- 正确
    SELECT user_id, username, email FROM users;
    
    -- 错误
    SELECT user_id,username,email FROM users;
  2. 操作符两侧加空格

    sql
    -- 正确
    SELECT * FROM users WHERE age > 18;
    
    -- 错误
    SELECT * FROM users WHERE age>18;
  3. 括号内侧不加空格

    sql
    -- 正确
    SELECT COUNT(*) FROM users WHERE status IN ('ACTIVE', 'PENDING');
    
    -- 错误
    SELECT COUNT( * ) FROM users WHERE status IN ( 'ACTIVE' , 'PENDING' );
  4. 关键字对齐

    sql
    -- 正确
    SELECT
        column1,
        column2
    FROM
        table1
    WHERE
        condition1
        AND condition2
    ORDER BY
        column1;
    
    -- 错误
    SELECT column1, column2 FROM table1 WHERE condition1 AND condition2 ORDER BY column1;

注释规范

注释风格

  1. 单行注释:使用-- (注意空格),用于单行注释

    sql
    -- 这是单行注释
    SELECT * FROM users; -- 查询所有用户
  2. 多行注释:使用/* */,用于多行注释或复杂注释

    sql
    /*
     * 这是多行注释
     * 用于详细说明SQL的用途
     * 和实现逻辑
     */
    SELECT * FROM users;

注释的使用场景

场景示例
复杂查询解释查询逻辑和意图
特殊处理说明为什么使用特定的实现方式
性能优化记录优化的原因和效果
业务规则说明SQL实现的业务规则
临时修改标记临时修改,便于后续恢复

SQL性能书写规范

查询优化规范

SELECT语句优化

  1. **避免SELECT ***:

    sql
    -- 正确:只查询需要的字段
    SELECT user_id, username, email FROM users;
    
    -- 错误:查询所有字段,包括不需要的
    SELECT * FROM users;
  2. 使用LIMIT限制结果集

    sql
    -- 正确:限制返回行数
    SELECT user_id, username FROM users LIMIT 10;
    
    -- 错误:不限制返回行数,可能导致性能问题
    SELECT user_id, username FROM users;
  3. 使用JOIN代替子查询

    sql
    -- 正确:使用JOIN
    SELECT u.user_id, u.username, o.order_id
    FROM users u
    JOIN orders o ON u.user_id = o.user_id;
    
    -- 错误:使用子查询,性能较差
    SELECT user_id, username,
        (SELECT order_id FROM orders WHERE user_id = u.user_id LIMIT 1) AS order_id
    FROM users u;
  4. 使用EXISTS代替IN

    sql
    -- 正确:使用EXISTS
    SELECT user_id, username
    FROM users u
    WHERE EXISTS (
        SELECT 1 FROM orders o WHERE o.user_id = u.user_id
    );
    
    -- 错误:使用IN,当orders表大时性能差
    SELECT user_id, username
    FROM users
    WHERE user_id IN (SELECT user_id FROM orders);
  5. 使用UNION ALL代替UNION

    sql
    -- 正确:使用UNION ALL,不需要去重
    SELECT user_id FROM users WHERE status = 'ACTIVE'
    UNION ALL
    SELECT user_id FROM users WHERE age > 60;
    
    -- 错误:使用UNION,需要去重,性能差
    SELECT user_id FROM users WHERE status = 'ACTIVE'
    UNION
    SELECT user_id FROM users WHERE age > 60;

WHERE子句优化

  1. 避免在WHERE子句中使用函数

    sql
    -- 正确:直接比较
    SELECT * FROM users WHERE created_at >= '2023-01-01';
    
    -- 错误:在字段上使用函数,无法使用索引
    SELECT * FROM users WHERE DATE(created_at) = '2023-01-01';
  2. 避免使用!=或<>

    sql
    -- 正确:使用范围查询
    SELECT * FROM users WHERE status = 'ACTIVE';
    
    -- 错误:使用!=,无法使用索引
    SELECT * FROM users WHERE status != 'INACTIVE';
  3. 避免使用IS NULL/IS NOT NULL

    sql
    -- 正确:使用默认值代替NULL
    SELECT * FROM users WHERE status = 'INACTIVE';
    
    -- 错误:使用IS NULL,索引效率低
    SELECT * FROM users WHERE status IS NULL;
  4. 使用BETWEEN代替>和<

    sql
    -- 正确:使用BETWEEN
    SELECT * FROM orders WHERE amount BETWEEN 100 AND 200;
    
    -- 错误:使用>和<,可读性差
    SELECT * FROM orders WHERE amount > 100 AND amount < 200;
  5. 使用IN代替多个OR

    sql
    -- 正确:使用IN
    SELECT * FROM users WHERE user_id IN (1, 2, 3, 4, 5);
    
    -- 错误:使用多个OR,性能差
    SELECT * FROM users WHERE user_id = 1 OR user_id = 2 OR user_id = 3 OR user_id = 4 OR user_id = 5;
  6. 合理使用LIKE

    sql
    -- 正确:前缀匹配,可以使用索引
    SELECT * FROM users WHERE username LIKE 'admin%';
    
    -- 错误:后缀匹配,无法使用索引
    SELECT * FROM users WHERE username LIKE '%admin';
    
    -- 错误:中间匹配,无法使用索引
    SELECT * FROM users WHERE username LIKE '%admin%';

JOIN操作优化

  1. JOIN顺序:将小表放在前面,大表放在后面

    sql
    -- 正确:小表users在前,大表orders在后
    SELECT u.username, o.order_id
    FROM users u
    JOIN orders o ON u.user_id = o.user_id;
  2. JOIN条件:使用索引字段作为JOIN条件

    sql
    -- 正确:使用索引字段user_id作为JOIN条件
    SELECT u.username, o.order_id
    FROM users u
    JOIN orders o ON u.user_id = o.user_id;
  3. 避免笛卡尔积:确保JOIN条件完整

    sql
    -- 正确:有完整的JOIN条件
    SELECT u.username, o.order_id
    FROM users u
    JOIN orders o ON u.user_id = o.user_id;
    
    -- 错误:缺少JOIN条件,导致笛卡尔积
    SELECT u.username, o.order_id
    FROM users u, orders o;
  4. 使用STRAIGHT_JOIN:强制MySQL按照指定顺序JOIN表

    sql
    -- 当优化器选择的JOIN顺序不合理时使用
    SELECT STRAIGHT_JOIN u.username, o.order_id
    FROM users u
    JOIN orders o ON u.user_id = o.user_id;

插入、更新、删除优化

INSERT语句优化

  1. 使用批量插入

    sql
    -- 正确:批量插入,减少网络开销
    INSERT INTO users (username, email) VALUES
        ('user1', 'user1@example.com'),
        ('user2', 'user2@example.com'),
        ('user3', 'user3@example.com');
    
    -- 错误:单条插入,性能差
    INSERT INTO users (username, email) VALUES ('user1', 'user1@example.com');
    INSERT INTO users (username, email) VALUES ('user2', 'user2@example.com');
    INSERT INTO users (username, email) VALUES ('user3', 'user3@example.com');
  2. 指定字段名

    sql
    -- 正确:指定字段名,提高可读性和兼容性
    INSERT INTO users (username, email) VALUES ('user1', 'user1@example.com');
    
    -- 错误:不指定字段名,依赖表结构,容易出错
    INSERT INTO users VALUES ('user1', 'user1@example.com');
  3. 使用INSERT IGNORE或ON DUPLICATE KEY UPDATE

    sql
    -- 忽略重复键错误
    INSERT IGNORE INTO users (username, email) VALUES ('user1', 'user1@example.com');
    
    -- 遇到重复键时更新
    INSERT INTO users (username, email) VALUES ('user1', 'user1@example.com')
    ON DUPLICATE KEY UPDATE email = VALUES(email);

UPDATE语句优化

  1. 使用WHERE条件

    sql
    -- 正确:使用WHERE条件,只更新需要的行
    UPDATE users SET status = 'INACTIVE' WHERE user_id = 123;
    
    -- 错误:没有WHERE条件,更新所有行
    UPDATE users SET status = 'INACTIVE';
  2. 更新必要的字段

    sql
    -- 正确:只更新需要的字段
    UPDATE users SET last_login = NOW() WHERE user_id = 123;
    
    -- 错误:更新所有字段,包括不需要的
    UPDATE users SET username = 'new_name', email = 'new_email', last_login = NOW() WHERE user_id = 123;
  3. 避免更新主键

    sql
    -- 错误:更新主键,会导致索引重建
    UPDATE users SET user_id = 456 WHERE user_id = 123;

DELETE语句优化

  1. 使用WHERE条件

    sql
    -- 正确:使用WHERE条件,只删除需要的行
    DELETE FROM users WHERE user_id = 123;
    
    -- 错误:没有WHERE条件,删除所有行
    DELETE FROM users;
  2. 使用LIMIT限制删除数量

    sql
    -- 正确:限制删除数量,避免长时间锁表
    DELETE FROM users WHERE status = 'INACTIVE' LIMIT 1000;
    
    -- 错误:不限制删除数量,可能导致锁表
    DELETE FROM users WHERE status = 'INACTIVE';
  3. 使用TRUNCATE代替DELETE

    sql
    -- 正确:清空表,性能更好
    TRUNCATE TABLE users;
    
    -- 错误:删除所有行,性能差
    DELETE FROM users;

事务和锁优化

事务优化

  1. 保持事务短小:减少事务持有锁的时间

    sql
    -- 正确:事务只包含必要的操作
    START TRANSACTION;
    UPDATE users SET balance = balance - 100 WHERE user_id = 1;
    UPDATE orders SET status = 'PAID' WHERE order_id = 100;
    COMMIT;
    
    -- 错误:事务包含不必要的操作
    START TRANSACTION;
    UPDATE users SET balance = balance - 100 WHERE user_id = 1;
    -- 执行耗时操作
    DO SLEEP(10);
    UPDATE orders SET status = 'PAID' WHERE order_id = 100;
    COMMIT;
  2. 避免在事务中执行DDL操作:DDL操作会隐式提交事务

    sql
    -- 错误:事务中执行DDL操作
    START TRANSACTION;
    UPDATE users SET balance = balance - 100 WHERE user_id = 1;
    ALTER TABLE orders ADD COLUMN new_column INT; -- 隐式提交事务
    UPDATE orders SET status = 'PAID' WHERE order_id = 100;
    COMMIT; -- 事务已经被隐式提交
  3. 合理使用隔离级别:根据业务需求选择合适的隔离级别

    sql
    -- 正确:根据业务需求选择隔离级别
    SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
    START TRANSACTION;
    -- 事务操作
    COMMIT;

锁优化

  1. 使用行级锁:避免使用表级锁

    sql
    -- 正确:使用行级锁
    SELECT * FROM users WHERE user_id = 1 FOR UPDATE;
    
    -- 错误:使用表级锁
    LOCK TABLES users WRITE;
    SELECT * FROM users WHERE user_id = 1;
    UNLOCK TABLES;
  2. 避免锁定不必要的数据:只锁定需要的行

    sql
    -- 正确:只锁定需要的行
    SELECT * FROM users WHERE user_id IN (1, 2, 3) FOR UPDATE;
    
    -- 错误:锁定所有行
    SELECT * FROM users FOR UPDATE;
  3. 使用共享锁代替排他锁:如果只需要读取数据

    sql
    -- 正确:使用共享锁
    SELECT * FROM users WHERE user_id = 1 LOCK IN SHARE MODE;
    
    -- 错误:使用排他锁
    SELECT * FROM users WHERE user_id = 1 FOR UPDATE;

SQL安全书写规范

防止SQL注入

  1. 使用参数化查询

    sql
    -- 正确:使用参数化查询
    PREPARE stmt FROM 'SELECT * FROM users WHERE username = ?';
    SET @username = 'admin';
    EXECUTE stmt USING @username;
    DEALLOCATE PREPARE stmt;
    
    -- 错误:直接拼接SQL,存在SQL注入风险
    SELECT * FROM users WHERE username = 'admin';
  2. 过滤和转义输入

    sql
    -- 正确:转义特殊字符
    SELECT * FROM users WHERE username = 'admin\' OR '1'='1';
    
    -- 错误:不转义特殊字符,导致SQL注入
    SELECT * FROM users WHERE username = 'admin' OR '1'='1';
  3. 使用最小权限原则

    sql
    -- 正确:只授予必要的权限
    GRANT SELECT, INSERT, UPDATE ON users TO 'app_user'@'%';
    
    -- 错误:授予所有权限
    GRANT ALL PRIVILEGES ON *.* TO 'app_user'@'%';

防止敏感信息泄露

  1. 避免在日志中记录敏感信息

    sql
    -- 正确:不记录密码等敏感信息
    UPDATE users SET password = 'new_password' WHERE user_id = 1;
    
    -- 错误:在注释中记录敏感信息
    UPDATE users SET password = 'new_password' WHERE user_id = 1; -- 密码:new_password
  2. 使用加密函数处理敏感数据

    sql
    -- 正确:使用加密函数
    INSERT INTO users (username, password) VALUES ('admin', PASSWORD('password'));
    
    -- 错误:明文存储密码
    INSERT INTO users (username, password) VALUES ('admin', 'password');
  3. 限制返回的敏感数据

    sql
    -- 正确:只返回需要的字段,不返回敏感信息
    SELECT user_id, username, email FROM users;
    
    -- 错误:返回所有字段,包括敏感信息
    SELECT * FROM users;

SQL可维护性书写规范

模块化设计

  1. 使用视图封装复杂查询

    sql
    -- 创建视图封装复杂查询
    CREATE VIEW v_active_user_orders AS
    SELECT
        u.user_id,
        u.username,
        COUNT(o.order_id) AS order_count,
        SUM(o.amount) AS total_amount
    FROM
        users u
    JOIN
        orders o ON u.user_id = o.user_id
    WHERE
        u.status = 'ACTIVE'
    GROUP BY
        u.user_id;
    
    -- 使用视图简化查询
    SELECT * FROM v_active_user_orders WHERE order_count > 10;
  2. 使用存储过程封装业务逻辑

    sql
    -- 创建存储过程封装业务逻辑
    DELIMITER //
    CREATE PROCEDURE proc_update_user_status(IN p_user_id INT, IN p_status VARCHAR(20))
    BEGIN
        UPDATE users SET status = p_status, updated_at = NOW() WHERE user_id = p_user_id;
        INSERT INTO user_status_history (user_id, status, changed_at) VALUES (p_user_id, p_status, NOW());
    END //
    DELIMITER ;
    
    -- 使用存储过程简化调用
    CALL proc_update_user_status(123, 'INACTIVE');

注释和文档

  1. 为复杂SQL添加注释

    sql
    /*
     * 统计活跃用户的订单信息
     * 1. 筛选状态为ACTIVE的用户
     * 2. 关联订单表统计订单数量和金额
     * 3. 按用户ID分组
     * 4. 只返回订单数量大于10的用户
     */
    SELECT
        u.user_id,
        u.username,
        COUNT(o.order_id) AS order_count,
        SUM(o.amount) AS total_amount
    FROM
        users u
    JOIN
        orders o ON u.user_id = o.user_id
    WHERE
        u.status = 'ACTIVE'
    GROUP BY
        u.user_id
    HAVING
        order_count > 10;
  2. 记录SQL的业务用途

    sql
    -- 业务需求:统计每个用户的订单数量,用于用户等级评定
    SELECT
        user_id,
        COUNT(*) AS order_count
    FROM
        orders
    GROUP BY
        user_id;
  3. 记录优化历史

    sql
    -- 优化前:执行时间10秒,扫描100万行
    -- 优化后:执行时间0.1秒,使用索引idx_user_id,扫描100行
    SELECT
        user_id,
        username,
        email
    FROM
        users
    WHERE
        user_id IN (SELECT user_id FROM orders WHERE amount > 100);

测试和验证

  1. 编写测试用例

    sql
    -- 测试用例1:正常情况
    SELECT * FROM users WHERE user_id = 1;
    
    -- 测试用例2:边界情况
    SELECT * FROM users WHERE user_id = 0;
    
    -- 测试用例3:异常情况
    SELECT * FROM users WHERE user_id = NULL;
  2. 使用EXPLAIN分析执行计划

    sql
    -- 分析查询执行计划
    EXPLAIN SELECT * FROM users WHERE username = 'admin';
    
    -- 查看详细执行计划
    EXPLAIN EXTENDED SELECT * FROM users WHERE username = 'admin';
    SHOW WARNINGS;
  3. 验证结果的正确性

    sql
    -- 验证查询结果是否符合预期
    SELECT COUNT(*) FROM users WHERE status = 'ACTIVE';
    -- 预期结果:100

常见SQL错误和规范示例

常见错误示例

  1. 不规范的命名

    sql
    -- 错误:使用大写表名
    SELECT * FROM Users;
    
    -- 错误:使用保留字作为字段名
    SELECT * FROM users WHERE select = 1;
  2. 不良的格式

    sql
    -- 错误:没有缩进和换行
    SELECT user_id,username,email FROM users WHERE status='ACTIVE' AND created_at>='2023-01-01' ORDER BY created_at DESC;
  3. 性能问题

    sql
    -- 错误:使用SELECT *
    SELECT * FROM users;
    
    -- 错误:在WHERE子句中使用函数
    SELECT * FROM users WHERE DATE(created_at) = '2023-01-01';
    
    -- 错误:使用LIKE '%admin%'
    SELECT * FROM users WHERE username LIKE '%admin%';
  4. 安全问题

    sql
    -- 错误:直接拼接SQL
    SELECT * FROM users WHERE username = 'admin' OR '1'='1';
    
    -- 错误:明文存储密码
    INSERT INTO users (username, password) VALUES ('admin', 'password');

规范示例

  1. SELECT语句

    sql
    SELECT
        u.user_id,
        u.username,
        u.email,
        COUNT(o.order_id) AS order_count,
        SUM(o.amount) AS total_amount
    FROM
        users u
    LEFT JOIN
        orders o ON u.user_id = o.user_id
    WHERE
        u.status = 'ACTIVE'
        AND u.created_at >= '2023-01-01'
        AND u.age > 18
    GROUP BY
        u.user_id,
        u.username,
        u.email
    HAVING
        order_count > 5
    ORDER BY
        total_amount DESC
    LIMIT
        10 OFFSET 0;
  2. INSERT语句

    sql
    INSERT INTO users (
        username,
        email,
        password,
        status,
        created_at,
        updated_at
    ) VALUES (
        'john_doe',
        'john@example.com',
        PASSWORD('secure_password'),
        'ACTIVE',
        NOW(),
        NOW()
    );
  3. UPDATE语句

    sql
    UPDATE users
    SET
        email = 'new_email@example.com',
        updated_at = NOW()
    WHERE
        user_id = 123
        AND status = 'ACTIVE';
  4. DELETE语句

    sql
    DELETE FROM users
    WHERE
        user_id = 123
        AND status = 'INACTIVE'
        AND created_at < '2022-01-01';
  5. 事务处理

    sql
    START TRANSACTION;
    
    -- 1. 检查余额
    SELECT balance INTO @balance FROM users WHERE user_id = 1 FOR UPDATE;
    
    -- 2. 检查余额是否足够
    IF @balance >= 100 THEN
        -- 3. 扣除余额
        UPDATE users SET balance = balance - 100 WHERE user_id = 1;
        
        -- 4. 创建订单
        INSERT INTO orders (user_id, amount, status, created_at) VALUES (1, 100, 'PAID', NOW());
        
        -- 5. 提交事务
        COMMIT;
    ELSE
        -- 6. 回滚事务
        ROLLBACK;
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Insufficient balance';
    END IF;

SQL书写规范检查表

基础规范检查

  • [ ] 命名是否符合规范?
  • [ ] 格式是否规范(缩进、换行、大小写)?
  • [ ] 是否添加了必要的注释?
  • [ ] 是否避免了使用保留字?
  • [ ] 是否使用了标准的后缀?

性能规范检查

  • [ ] 是否避免了SELECT *?
  • [ ] 是否使用了LIMIT限制结果集?
  • [ ] WHERE子句是否使用了索引字段?
  • [ ] 是否避免了在WHERE子句中使用函数?
  • [ ] JOIN操作是否使用了索引字段?
  • [ ] 是否避免了笛卡尔积?
  • [ ] 事务是否保持短小?

安全规范检查

  • [ ] 是否使用了参数化查询?
  • [ ] 是否过滤和转义了输入?
  • [ ] 是否使用了最小权限原则?
  • [ ] 是否避免了在日志中记录敏感信息?
  • [ ] 是否使用了加密函数处理敏感数据?

可维护性检查

  • [ ] 是否使用了视图或存储过程封装复杂逻辑?
  • [ ] 是否添加了详细的注释和文档?
  • [ ] 是否编写了测试用例?
  • [ ] 是否使用了EXPLAIN分析执行计划?
  • [ ] 是否验证了结果的正确性?

总结

SQL书写规范是数据库开发和运维的重要组成部分,良好的SQL书写规范可以提高SQL语句的可读性、性能、安全性和可维护性。

作为数据库开发人员或DBA,应该:

  1. 遵循统一的SQL书写规范
  2. 不断学习和更新SQL优化知识
  3. 定期审查和优化现有SQL
  4. 分享和推广良好的SQL书写习惯
  5. 结合业务需求和系统特点调整规范

通过严格遵循SQL书写规范,可以减少SQL错误,提高数据库性能,增强系统的可靠性和可维护性,为业务提供更好的支持。