如何在SQL中优化查询?EXPLAIN与查询优化的技巧

<blockquote>使用EXPLAIN分析SQL执行计划,通过type、key、rows等列判断查询效率,结合索引优化、避免全表扫描和常见陷阱,提升查询性能。</blockquote> <p><img src="https://img.php.cn/upload/article/001/503/042/175706064121561.jpeg" alt="如何在sql中优化查询?explain与查询优化的技巧"></p> <p>SQL查询优化,简单来说,就是让你的数据库更快地返回结果。这不仅仅是让你的网站感觉更流畅,还能节省服务器资源,尤其是在数据量巨大的情况下。</p> <p>EXPLAIN与查询优化技巧</p> <h3>如何使用EXPLAIN分析SQL查询?</h3> <p>EXPLAIN 语句是 SQL 中一个强大的<a style="color:#f60; text-decoration:underline;" title="工具" href="https://www.php.cn/zt/16887.html" target="_blank">工具</a>,它能告诉你数据库是如何执行你的查询的。与其说是“如何使用”,不如说是“如何理解” EXPLAIN 的输出。</p> <p>首先,在你的 SQL 查询前加上 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">EXPLAIN</pre></div> 关键字,例如:<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">EXPLAIN SELECT * FROM users WHERE age > 25;</pre></div></p> <p>执行这个语句后,你会得到一张表,每一行代表查询计划中的一个步骤。这张表里有很多列,但最关键的几个是:</p> <ul> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">id</pre></div></strong>: 查询的标识符,数字越大,执行优先级越高(但也有例外,后面说)。</li> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">select_type</pre></div></strong>: 查询的类型,例如 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">SIMPLE</pre></div>(简单查询,不包含子查询或 UNION),<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">PRIMARY</pre></div>(最外层的 SELECT),<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">SUBQUERY</pre></div>(子查询)等等。</li> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">table</pre></div></strong>: 涉及的表名。</li> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">type</pre></div></strong>: 访问类型,这是最重要的列之一。它告诉你数据库是如何找到表中符合条件的行的。常见的类型有:<ul> <li><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">system</pre></div>: 表中只有一行记录,这是理想情况,速度非常快。</li> <li><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">const</pre></div>: 使用主键或唯一索引进行等值查询,也是非常快的。</li> <li><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">eq_ref</pre></div>: 使用唯一索引关联查询,效率较高。</li> <li><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">ref</pre></div>: 使用非唯一索引进行等值查询。</li> <li><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">range</pre></div>: 使用索引进行范围查询,例如 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">BETWEEN</pre></div>,<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">></pre></div>,<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;"><</pre></div>。</li> <li><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">index</pre></div>: 扫描整个索引树。</li> <li><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">ALL</pre></div>: 全表扫描,这是最慢的,应该尽量避免。</li> </ul> </li> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">possible_keys</pre></div></strong>: 数据库可能使用的索引。</li> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">key</pre></div></strong>: 数据库实际使用的索引。</li> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">key_len</pre></div></strong>: 索引的长度,可以用来判断索引的使用情况。</li> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">ref</pre></div></strong>: 显示索引的哪一列被用于查找值。</li> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">rows</pre></div></strong>: 数据库估计需要扫描的行数。</li> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">Extra</pre></div></strong>: 包含一些额外的信息,例如 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">Using index</pre></div>(表示使用了覆盖索引,不需要回表查询),<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">Using where</pre></div>(表示需要使用 WHERE 子句过滤结果),<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">Using temporary</pre></div>(表示需要使用临时表),<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">Using filesort</pre></div>(表示需要进行文件排序,速度较慢)。</li> </ul> <p>理解了这些列的含义,你就可以分析 EXPLAIN 的输出了。例如,如果 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">type</pre></div> 是 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">ALL</pre></div>,<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">rows</pre></div> 很大,<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">Extra</pre></div> 包含 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">Using filesort</pre></div> 或 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">Using temporary</pre></div>,那就说明你的查询需要优化。</p> <div class="aritcle_card"> <a class="aritcle_card_img" href="/xiazai/code/10800"> <img src="https://img.php.cn/upload/webcode/000/000/004/176379660214943.gif" alt="HiShop网店代理分销系统"> </a> <div class="aritcle_card_info"> <a href="/xiazai/code/10800">HiShop网店代理分销系统</a> <p>Hishop.5.2.BETA2版主要更新: [修改] 进一步优化了首页打开速度 [修改] 美化了默认模板 [修改] 优化系统架构,程序标签及SQL查询效率,访问系统页面的速度大大提高 [修改] 采用了HTML模板机制,实现了前台模板可视化编辑,降低模板制作与修改的难度. [修改] 全新更换前后台AJAX技术框架,提升了用户操作体验. 店铺管理 [新增] 整合TQ在线客服 [修改] 后台广告位增加</p> <div class=""> <img src="/static/images/card_xiazai.png" alt="HiShop网店代理分销系统"> <span>0</span> </div> </div> <a href="/xiazai/code/10800" class="aritcle_card_btn"> <span>查看详情</span> <img src="/static/images/cardxiayige-3.png" alt="HiShop网店代理分销系统"> </a> </div> <p>另外,关于 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">id</pre></div> 列的优先级,如果 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">id</pre></div> 相同,则从上到下执行;如果 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">id</pre></div> 不同,则 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">id</pre></div> 值越大,优先级越高。但是,如果 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">id</pre></div> 相同,并且 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">select_type</pre></div> 是 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">DERIVED</pre></div>(派生表),则派生表会先执行。</p> <h3>如何通过索引优化SQL查询?</h3> <p>索引就像书的目录,可以帮助数据库快速找到数据。但索引不是越多越好,过多的索引会增加数据库的负担。</p> <ul> <li> <strong>选择合适的列创建索引</strong>:通常,你应该在 WHERE 子句中经常使用的列上创建索引。例如,如果你经常根据 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">age</pre></div> 和 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">city</pre></div> 查询用户,可以考虑创建 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">age</pre></div> 和 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">city</pre></div> 的联合索引。</li> <li> <strong>注意索引的顺序</strong>:对于联合索引,索引的顺序很重要。应该将选择性高的列放在前面。选择性是指,该列的不同值的数量与总行数的比例。例如,<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">gender</pre></div> 的选择性很低,而 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">email</pre></div> 的选择性很高。</li> <li> <strong>避免在索引列上使用函数或表达式</strong>:例如,<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">WHERE YEAR(birthday) = 2000</pre></div> 就无法使用 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">birthday</pre></div> 上的索引。应该改为 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">WHERE birthday BETWEEN '2000-01-01' AND '2000-12-31'</pre></div>。</li> <li> <strong>尽量使用覆盖索引</strong>:覆盖索引是指,查询需要的所有列都包含在索引中。这样可以避免回表查询,提高查询效率。例如,如果你的查询是 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">SELECT age, city FROM users WHERE age > 25</pre></div>,可以创建一个包含 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">age</pre></div> 和 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">city</pre></div> 的联合索引。</li> <li> <strong>定期维护索引</strong>:随着数据的增加和删除,索引可能会变得碎片化,影响查询效率。可以使用 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">OPTIMIZE TABLE</pre></div> 命令来优化表,重建索引。</li> </ul> <h3>如何避免SQL查询中的常见陷阱?</h3> <p>除了索引,还有一些常见的陷阱需要避免:</p> <ul> <li>*<em>避免使用 `SELECT </em>`**:应该只选择需要的列,减少数据传输量。</li> <li> <strong>避免在 WHERE 子句中使用 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">OR</pre></div></strong>:<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">OR</pre></div> 可能会导致全表扫描。可以尝试使用 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">UNION</pre></div> 或分解成多个查询。</li> <li> <strong>避免在 WHERE 子句中使用 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">NOT IN</pre></div></strong>:<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">NOT IN</pre></div> 可能会导致全表扫描。可以尝试使用 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">NOT EXISTS</pre></div> 或 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">LEFT JOIN ... WHERE ... IS NULL</pre></div>。</li> <li> <strong>避免在 WHERE 子句中使用模糊查询 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">%keyword%</pre></div></strong>:这种查询无法使用索引。可以考虑使用全文索引或<a style="color:#f60; text-decoration:underline;" title="搜索引擎" href="https://www.php.cn/zt/20588.html" target="_blank">搜索引擎</a>。</li> <li> <strong>避免使用子查询</strong>:子查询可能会导致性能问题。可以尝试使用 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">JOIN</pre></div> 替代。</li> <li> <strong>合理使用 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">LIMIT</pre></div></strong>:<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">LIMIT</pre></div> 可以限制返回的行数,提高查询效率。但要注意,<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">LIMIT</pre></div> 只有在排序后才能生效。</li> </ul> <h3>如何优化复杂的SQL查询?</h3> <p>对于复杂的 SQL 查询,可以尝试以下方法:</p> <ul> <li> <strong>分解查询</strong>:将复杂的查询分解成多个简单的查询,然后将结果组合起来。</li> <li> <strong>使用临时表</strong>:将中间结果存储在临时表中,可以避免重复计算。</li> <li> <strong>使用物化视图</strong>:物化视图是指,将查询结果预先计算并存储起来,可以大大提高查询效率。但要注意,物化视图需要定期刷新。</li> <li> <strong>优化 JOIN</strong>:<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">JOIN</pre></div> 的顺序很重要。应该将小表放在前面,大表放在后面。可以使用 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">STRAIGHT_JOIN</pre></div> 强制指定 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">JOIN</pre></div> 的顺序。</li> <li> <strong>使用查询提示(Query Hints)</strong>:查询提示可以告诉数据库如何执行查询。例如,可以使用 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">USE INDEX</pre></div> 强制指定使用的索引,可以使用 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">IGNORE INDEX</pre></div> 忽略某些索引。但要注意,查询提示可能会导致数据库无法选择最优的执行计划。</li> </ul> <p>记住,SQL 优化是一个持续的过程,需要不断地学习和实践。没有什么万能的解决方案,只有最适合你的解决方案。</p>

以上就是如何在SQL中优化查询?EXPLAIN与查询优化的技巧的详细内容,更多请关注其它相关文章!

本文转自网络,如有侵权请联系客服删除。