8个MySQL常见的新手SQL错误用法详解

 更新时间:2026年03月25日 09:37:00   作者:python全栈小辉  
在MySQL学习和开发的过程中,新手很容易写出能跑但有问题的SQL,本文整理了8种最常见的新手SQL错误用法,每种错误都配详细的错误示例,危害分析,正确写法和避坑指南,帮助大家从入门开始就养成良好的SQL习惯

前言

在MySQL学习和开发的过程中,新手很容易写出“能跑但有问题”的SQL:要么性能极差,要么结果错误,甚至引发数据误删的严重故障。这些错误往往不是语法错误,而是逻辑错误、性能错误或安全错误,很难被发现,但危害极大。

本文整理了8种最常见的新手SQL错误用法,每种错误都配详细的“错误示例”、“危害分析”、“正确写法”和“避坑指南”,帮助你从入门开始就养成良好的SQL习惯,避免踩坑。

错误一:滥用 SELECT *,只图省事不看后果

错误用法

很多新手图省事,查询时直接写SELECT *,不管需要多少字段:

-- 错误写法:查询所有字段
SELECT * FROM user_info WHERE city = '武汉';

为什么错

  • 浪费磁盘IO和网络带宽:查询了很多不需要的字段(比如大字段contentavatar),增加了磁盘读取和网络传输的开销;
  • 无法使用覆盖索引:如果只查询需要的字段,且这些字段都在联合索引中,可以使用“覆盖索引”,无需回表,性能提升数倍;但SELECT *必须回表查整行数据,性能差;
  • 表结构变更风险:如果表结构新增或删除了字段,SELECT *的结果会变化,可能导致应用层报错。

正确用法

只查询业务需要的字段:

-- 正确写法:只查询需要的字段
SELECT id, username, phone, age FROM user_info WHERE city = '武汉';

如果这些字段在联合索引(idx_city_username_phone_age)中,就是覆盖索引,无需回表,性能极佳。

避坑指南

  • **永远不要写SELECT ***,除非你真的需要所有字段;
  • 写SQL前先想清楚:业务到底需要哪些字段?只查这些字段;
  • 用EXPLAIN查看执行计划,如果Extra列有Using index,说明用到了覆盖索引,很好。

错误二:不带 WHERE 条件的 UPDATE/DELETE,高危操作!

错误用法

这是最危险的错误,一不小心就会全表更新或删除:

-- 错误写法:不带WHERE条件的UPDATE,全表更新!
UPDATE user_info SET age = 28;

-- 错误写法:不带WHERE条件的DELETE,全表删除!
DELETE FROM user_info;

为什么错

  • 全表操作:不带WHERE条件,会更新/删除表中的所有数据,无法回滚(除非在事务中);
  • 线上故障:如果在生产环境执行,会导致所有数据丢失或错误,引发严重的线上事故,甚至需要离职赔偿。

正确用法

必须带WHERE条件

-- 正确写法:带WHERE条件,只更新指定行
UPDATE user_info SET age = 28 WHERE id = 1;

-- 正确写法:带WHERE条件,只删除指定行
DELETE FROM user_info WHERE id = 1;

执行前先SELECT验证:执行UPDATE/DELETE前,先用SELECT查看WHERE条件匹配的行数,确认无误后再执行:

-- 先SELECT验证:查看匹配的行数
SELECT COUNT(*) FROM user_info WHERE id = 1;
-- 确认只有1行后,再执行UPDATE/DELETE

生产环境用逻辑删除替代物理删除:不要直接DELETE,用is_deleted字段标记删除:

-- 逻辑删除:更新is_deleted为1,而不是DELETE
UPDATE user_info SET is_deleted = 1 WHERE id = 1;

避坑指南

  • UPDATE/DELETE必须带WHERE条件,不带条件绝对不执行;
  • 执行前先SELECT验证,确认匹配的行数和数据;
  • 生产环境开启SQL审核,禁止不带WHERE的UPDATE/DELETE;
  • 尽量用逻辑删除,避免物理删除,误删后还能恢复。

错误三:LIKE 通配符在开头,索引失效全表扫描

错误用法

用LIKE模糊查询时,把通配符%放在开头:

-- 错误写法:通配符在开头,索引失效
SELECT * FROM user_info WHERE username LIKE '%张三%';
-- 更糟:通配符只在开头
SELECT * FROM user_info WHERE username LIKE '%张三';

为什么错

MySQL的联合索引遵循最左前缀原则,LIKE查询只有通配符在结尾时才能用到索引:

  • LIKE '张三%' 能用索引(前缀匹配);
  • LIKE '%张三' 索引失效(后缀匹配);
  • LIKE '%张三%' 索引失效(中间匹配)。

通配符在开头时,MySQL无法利用索引的有序性,只能全表扫描,性能极差。

正确用法

尽量用前缀匹配

-- 正确写法:通配符在结尾,能用索引
SELECT * FROM user_info WHERE username LIKE '张三%';

如果必须用中间/后缀匹配,用全文索引或Elasticsearch

如果业务必须用%张三%这样的模糊查询,不要用LIKE,改用:

  • MySQL的全文索引(FULLTEXT INDEX);
  • 或者把数据同步到Elasticsearch,用ES做模糊查询,性能更好。

避坑指南

  • LIKE查询尽量用前缀匹配,通配符只放结尾;
  • 用EXPLAIN查看执行计划,如果type列是ALL,说明全表扫描,索引失效;
  • 必须用中间/后缀匹配时,用全文索引或ES,不要用LIKE。

错误四:在索引列上用函数/表达式,索引白白浪费

错误用法

在索引列上使用函数(比如YEAR()DATE())或表达式(比如id + 1):

-- 错误写法:在索引列create_time上用YEAR()函数,索引失效
SELECT * FROM user_info WHERE YEAR(create_time) = 2026;

-- 错误写法:在索引列id上用表达式,索引失效
SELECT * FROM user_info WHERE id + 1 = 2;

为什么错

MySQL的索引是对列的原始值建立的B+树,如果在列上用了函数或表达式,索引的有序性就被破坏了,优化器无法使用索引,只能全表扫描。

正确用法

把函数/表达式移到等号的右边,让索引列保持“干净”:

-- 正确写法:把YEAR()移到右边,用范围查询,能用索引
SELECT * FROM user_info 
WHERE create_time >= '2026-01-01' 
  AND create_time < '2027-01-01';

-- 正确写法:把表达式移到右边,id保持干净,能用索引
SELECT * FROM user_info WHERE id = 2 - 1;

如果必须在列上用函数,可以创建函数索引(MySQL 8.0+支持):

-- 创建函数索引
CREATE INDEX idx_year_create_time ON user_info ((YEAR(create_time)));

-- 现在可以用YEAR()查询了,能用到函数索引
SELECT * FROM user_info WHERE YEAR(create_time) = 2026;

避坑指南

  • 永远不要在索引列上用函数/表达式,保持索引列“干净”;
  • 把函数/表达式移到等号右边,用范围查询替代;
  • 如果必须用函数,MySQL 8.0+可以创建函数索引;
  • 用EXPLAIN查看执行计划,确认索引是否生效。

错误五:隐式类型转换,索引失效还可能查错数据

错误用法

查询时,字段类型和参数类型不一致,导致隐式类型转换:

-- 错误写法:phone是varchar类型,却用数字13800138000查询,隐式类型转换
SELECT * FROM user_info WHERE phone = 13800138000;

-- 错误写法:id是bigint类型,却用字符串'1'查询,隐式类型转换
SELECT * FROM user_info WHERE id = '1';

为什么错

索引失效:隐式类型转换会破坏索引的有序性,优化器无法使用索引,只能全表扫描;

查错数据:隐式类型转换可能导致查询结果错误。比如phone是varchar,phone = 13800138000会把phone的字符串转换为数字,'13800138000a'这样的字符串也会被转换为13800138000,导致查错数据。

正确用法

保持字段类型和参数类型一致:

-- 正确写法:phone是varchar,用字符串'13800138000'查询
SELECT * FROM user_info WHERE phone = '13800138000';

-- 正确写法:id是bigint,用数字1查询
SELECT * FROM user_info WHERE id = 1;

避坑指南

  • 保持字段类型和参数类型一致,避免隐式类型转换;
  • 建表时,选择合适的数据类型:手机号、身份证号用varchar,不要用bigint
  • 用EXPLAIN查看执行计划,如果type列是ALL,且字段类型不一致,可能是隐式类型转换导致的。

错误六:滥用 NOT IN / <>,索引失效还可能结果错误

错误用法

很多新手喜欢用NOT IN<>(不等于)来排除数据:

-- 错误写法:NOT IN,可能索引失效
SELECT * FROM user_info WHERE city NOT IN ('武汉', '北京');

-- 错误写法:<>,可能索引失效
SELECT * FROM user_info WHERE age <> 28;

为什么错

索引失效NOT IN<>属于“负向查询”,MySQL优化器通常不会选择索引,而是全表扫描,性能差;

NOT IN包含NULL时结果错误:如果NOT IN的列表中有NULL,整个查询会返回空结果,因为NULL的三值逻辑导致的。

正确用法

尽量用正向查询替代:如果业务允许,用IN替代NOT IN,用=替代<>

如果必须用负向查询,用EXISTS或LEFT JOIN IS NULL替代

-- 用NOT EXISTS替代NOT IN,性能更好,且不受NULL影响
SELECT * FROM user_info u 
WHERE NOT EXISTS (
  SELECT 1 FROM exclude_city e WHERE e.city = u.city
);

-- 用LEFT JOIN + IS NULL替代NOT IN
SELECT u.* FROM user_info u
LEFT JOIN exclude_city e ON u.city = e.city
WHERE e.city IS NULL;

NOT IN列表中绝对不要包含NULL

-- 错误:NOT IN列表中有NULL,返回空结果
SELECT * FROM user_info WHERE city NOT IN ('武汉', NULL);

-- 正确:NOT IN列表中没有NULL
SELECT * FROM user_info WHERE city NOT IN ('武汉', '北京');

避坑指南

  • 尽量避免用NOT IN和<>,优先用正向查询;
  • 如果必须用负向查询,用NOT EXISTS或LEFT JOIN IS NULL替代
  • NOT IN列表中绝对不要包含NULL,否则结果错误;
  • 用EXPLAIN查看执行计划,确认是否用到索引。

错误七:用 ORDER BY RAND() 随机查询,性能极差

错误用法

很多新手用ORDER BY RAND()来随机查询数据:

-- 错误写法:ORDER BY RAND(),全表扫描+全表排序,性能极差
SELECT * FROM user_info ORDER BY RAND() LIMIT 10;

为什么错

ORDER BY RAND()的执行逻辑是:

  • 为表中的每一行生成一个随机数;
  • 按照随机数对所有行进行排序(通常是文件排序filesort);
  • 取前N条。

如果表有100万行,就需要生成100万个随机数,然后对100万行进行排序,磁盘IO和CPU开销极大,查询耗时可能达到秒级甚至分钟级。

正确用法

方案一:利用自增主键ID范围随机(推荐,性能最高)

如果表有连续的自增主键ID:

-- 步骤1:获取ID的最小值和最大值
SELECT MIN(id) AS min_id, MAX(id) AS max_id FROM user_info;

-- 步骤2:在应用层生成10个不重复的随机ID(比如123, 456...)
-- 步骤3:通过ID精准查询
SELECT * FROM user_info WHERE id IN (123, 456, 789, ...);

方案二:覆盖索引+ORDER BY RAND()(折中方案)

如果没有连续的自增主键,先通过覆盖索引随机查ID,再回表:

-- 先通过覆盖索引随机查10个ID,排序的数据量小
SELECT t.* FROM user_info t
INNER JOIN (
  SELECT id FROM user_info ORDER BY RAND() LIMIT 10
) tmp ON t.id = tmp.id;

避坑指南

  • 绝对不要直接用SELECT * ORDER BY RAND(),性能极差;
  • 优先用自增主键ID范围随机,性能最高;
  • 如果没有连续主键,用覆盖索引+ORDER BY RAND(),减少排序的数据量;
  • 大表随机查询,考虑用Redis缓存ID列表,在应用层随机。

错误八:忽略 NULL 值的三值逻辑,结果错误

错误用法

很多新手对NULL的三值逻辑不了解,写出错误的SQL:

-- 错误写法:用= NULL判断NULL,永远返回FALSE
SELECT * FROM user_info WHERE email = NULL;

-- 错误写法:NOT IN列表中有NULL,返回空结果
SELECT * FROM user_info WHERE id NOT IN (1, 2, NULL);

-- 错误写法:COUNT(email)会忽略NULL值,结果不对
SELECT COUNT(email) FROM user_info;

为什么错

MySQL的逻辑判断有三种结果:TRUEFALSEUNKNOWN,而NULL代表“未知”:

  • = NULL:结果是UNKNOWN,不会返回任何行;
  • NOT IN (..., NULL):结果是UNKNOWN,整个查询返回空;
  • COUNT(列名):会忽略NULL值,只统计非NULL的行数;COUNT(*)才会统计所有行数。

正确用法

用IS NULL / IS NOT NULL判断NULL

-- 正确写法:用IS NULL判断NULL
SELECT * FROM user_info WHERE email IS NULL;

-- 正确写法:用IS NOT NULL判断非NULL
SELECT * FROM user_info WHERE email IS NOT NULL;

NOT IN列表中不要包含NULL

-- 正确:NOT IN列表中没有NULL
SELECT * FROM user_info WHERE id NOT IN (1, 2, 3);

区分COUNT(*)和COUNT(列名)

-- COUNT(*):统计所有行数,包括NULL值
SELECT COUNT(*) FROM user_info;

-- COUNT(email):统计email非NULL的行数
SELECT COUNT(email) FROM user_info;

避坑指南

  • 永远不要用= NULL或<> NULL,用IS NULL / IS NOT NULL;
  • NOT IN列表中绝对不要包含NULL
  • 区分COUNT(*)和COUNT(列名):统计所有行数用COUNT(*),统计非NULL行数用COUNT(列名);
  • 建表时,尽量给字段设置NOT NULL和默认值,避免NULL值带来的问题。

总结:新手SQL避坑的5个核心习惯

看完这8种错误,我们可以总结出新手SQL避坑的5个核心习惯:

  • 永远不写SELECT ,只查需要的字段,尽量用覆盖索引;
  • UPDATE/DELETE必须带WHERE条件,执行前先SELECT验证,生产环境用逻辑删除;
  • 保持索引列“干净”:不用函数/表达式、不用隐式类型转换、LIKE通配符只放结尾;
  • 尽量用正向查询:避免NOT IN/<>,用EXISTS/LEFT JOIN替代;
  • 重视NULL值的三值逻辑:用IS NULL判断NULL,NOT IN列表不含NULL,区分COUNT(*)和COUNT(列名)。

最后,写SQL后一定要用EXPLAIN查看执行计划,重点看type(访问类型)、key(实际用到的索引)、rows(预计扫描的行数)、Extra(额外信息),确认SQL的性能符合预期,避免踩坑。

以上就是8个MySQL常见的新手SQL错误用法详解的详细内容,更多关于MySQL新手SQL错误用法的资料请关注脚本之家其它相关文章!

相关文章

  • MySQL分区表的实现示例

    MySQL分区表的实现示例

    MySQL分区是将一张表分割成独立的子表的技术,本文主要介绍了MySQL分区表的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-08-08
  • mysql 8.0.15 winx64压缩包安装配置方法图文教程

    mysql 8.0.15 winx64压缩包安装配置方法图文教程

    这篇文章主要为大家详细介绍了mysql 8.0.15 winx64压缩包安装配置方法图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-05-05
  • 详解mysql8.0创建用户授予权限报错解决方法

    详解mysql8.0创建用户授予权限报错解决方法

    这篇文章主要介绍了详解mysql8.0创建用户授予权限报错解决方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • MYSQL GROUP BY用法详解

    MYSQL GROUP BY用法详解

    这篇文章主要为大家详细介绍了MYSQL GROUP BY用法,具有一定的实用性和参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • mysql表的清空、删除和修改操作详解

    mysql表的清空、删除和修改操作详解

    这篇文章主要详细介绍了mysql表的清空、删除和修改操作的方法,以及一些常用的mysql的常用操作,非常的简单实用,有需要的可以参考下
    2014-09-09
  • MySQL派生表合并优化的原理和实现过程

    MySQL派生表合并优化的原理和实现过程

    本文从一个案例出发梳理了MySQL派生表合并优化的流程实现和优化原理,并对优化前后同一条SQL语句在代码层面的类实例映射关系进行了对比,这篇文章主要介绍了MySQL派生表合并优化的原理和实现,需要的朋友可以参考下
    2024-07-07
  • MySQL中Binary Log二进制日志文件的基本操作命令小结

    MySQL中Binary Log二进制日志文件的基本操作命令小结

    这篇文章主要介绍了MySQL中Binary Log二进制日志文件的基本操作小结,包括利用二进制日志恢复数据的方法,需要的朋友可以参考下
    2015-12-12
  • MySQL追踪数据库表更新操作来源的全面指南

    MySQL追踪数据库表更新操作来源的全面指南

    本文将以一个具体问题为例,如何监测哪个IP来源对数据库表 statistics_test 进行了UPDATE操作,文内探讨了多种方法,并提供了详细的代码示例,需要的可以了解下
    2025-06-06
  • mysql之查询两个时间段是否有交集的情况

    mysql之查询两个时间段是否有交集的情况

    这篇文章主要介绍了mysql之查询两个时间段是否有交集的情况,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • MySQL null与not null和null与空值''''''''的区别详解

    MySQL null与not null和null与空值''''''''的区别详解

    这篇文章主要介绍了MySQL null与not null和null与空值''的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11

最新评论