查看执行计划时,我们应该获得什么信息

本文介绍了一种通过审查和优化SQL查询来提高数据库性能的方法。针对一个执行缓慢的存储过程,通过分析执行计划、修复缺失索引、消除表扫描、解决嵌套循环连接问题,并重构UDF,最终将执行时间从50秒降低到1秒。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 查看执行计划时,我们应该获得什么信息

  当你的查询很慢时,你就应该看看预估的执行计划(当然也可以查看真实的执行计划),找出耗时最多的操作,注意观察以下成本通常较高的操作:

  1、表扫描(Table Scan)

  当表没有聚集索引时就会发生,这时只要创建聚集索引或重整索引一般都可以解决问题。

  2、聚集索引扫描(Clustered Index Scan)

  有时可以认为等同于表扫描,当某列上的非聚集索引无效时会发生,这时只要创建一个非聚集索引就ok了。

  3、哈希连接(Hash Join)

  当连接两个表的列没有被索引时会发生,只需在这些列上创建索引即可。

  4、嵌套循环(Nested Loops)

  当非聚集索引不包括select查询清单的列时会发生,只需要创建覆盖索引问题即可解决。

  5、RID查找(RID Lookup)

  当你有一个非聚集索引,但相同的表上却没有聚集索引时会发生,此时数据库引擎会使用行ID查找真实的行,这时一个代价高的操作,这时只要在该表上创建聚集索引即可。

  TSQL重构真实的故事

  只有解决了实际的问题后,知识才转变为价值。当我们检查应用程序性能时,发现一个存储过程比我们预期的执行得慢得多,在生产数据库中检索一个月的销售数据居然要50秒,下面就是这个存储过程的执行语句:

  exec uspGetSalesInfoForDateRange ‘1/1/2009’, 31/12/2009,’Cap’

  Tom受命来优化这个存储过程,下面是这个存储过程的代码:

  ALTER   PROCEDURE  uspGetSalesInfoForDateRange

   @startYear   DateTime ,

   @endYear   DateTime ,

   @keyword   nvarchar ( 50 )

   AS

   BEGIN

   SET  NOCOUNT  ON ;

   SELECT

  Name,

  ProductNumber,

  ProductRates.CurrentProductRate Rate,

  ProductRates.CurrentDiscount Discount,

  OrderQty Qty,

  dbo.ufnGetLineTotal(SalesOrderDetailID) Total,

  OrderDate,

  DetailedDescription

   FROM

  Products  INNER   JOIN  OrderDetails

   ON  Products.ProductID  =  OrderDetails.ProductID

   INNER   JOIN  Orders

   ON  Orders.SalesOrderID  =  OrderDetails.SalesOrderID

   INNER   JOIN  ProductRates

   ON

  Products.ProductID  =  ProductRates.ProductID

   WHERE

  OrderDate  between   @startYear   and   @endYear

   AND

  (

  ProductName  LIKE   ''   +   @keyword   +   '  % '   OR

  ProductName  LIKE   ' '   +   @keyword   +   '   '   +   ' % '   OR

  ProductName  LIKE   ' '   +   @keyword   +   ' % '   OR

  Keyword  LIKE   ''   +   @keyword   +   '  % '   OR

  Keyword  LIKE   ' '   +   @keyword   +   '   '   +   ' % '   OR

  Keyword  LIKE   ' '   +   @keyword   +   ' % '

  )

   ORDER   BY

  ProductName

   END

   GO

分析索引

  首先,Tom想到了审查这个存储过程使用到的表的索引,很快他发现下面两列的索引无故丢失了:

  OrderDetails.ProductID

  OrderDetails.SalesOrderID

  他在这两个列上创建了非聚集索引,然后再执行存储过程:

  exec uspGetSalesInfoForDateRange ‘1/1/2009’, 31/12/2009 with recompile

  性能有所改变,但仍然低于预期(这次花了35秒),注意这里的with recompile子句告诉SQL Server引擎重新编译存储过程,重新生成执行计划,以利用新创建的索引。

  分析查询执行计划

  Tom接下来查看了SQL Server Management Studio中的执行计划,通过分析,他找到了某些重要的线索:

  1、发生了一次表扫描,即使该表已经正确设置了索引,而表扫描占据了总体查询执行时间的30%;

  2、发生了一个嵌套循环连接。

  Tom想知道是否有索引碎片,因为所有索引配置都是正确的,通过TSQL他知道了有两个索引都产生了碎片,很快他重组了这两个索引,于是表扫描消失了,现在执行存储过程的时间减少到25秒了。

  为了消除嵌套循环连接,他又在表上创建了覆盖索引,时间进一步减少到23秒。

  实施最佳实践

  Tom发现有个UDF有问题,代码如下: 

ALTER   FUNCTION   [ dbo ] . [ ufnGetLineTotal ]

  (

   @SalesOrderDetailID   int

  )

   RETURNS   money

   AS

   BEGIN

   DECLARE   @CurrentProductRate   money

   DECLARE   @CurrentDiscount   money

   DECLARE   @Qty   int

   SELECT

   @CurrentProductRate   =  ProductRates.CurrentProductRate,

   @CurrentDiscount   =  ProductRates.CurrentDiscount,

   @Qty   =  OrderQty

   FROM

  ProductRates  INNER   JOIN  OrderDetails  ON

  OrderDetails.ProductID  =  ProductRates.ProductID

   WHERE

  OrderDetails.SalesOrderDetailID  =   @SalesOrderDetailID

   RETURN  ( @CurrentProductRate - @CurrentDiscount ) * @Qty

   END

  在计算订单总金额时看起来代码很程序化,Tom决定在UDF的SQL中使用内联SQL。

  dbo.ufnGetLineTotal(SalesOrderDetailID) Total -- 旧代码

  (CurrentProductRate-CurrentDiscount)*OrderQty Total -- 新代码

  执行时间一下子减少到14秒了。

  在select查询清单中放弃不必要的Text列

  为了进一步提升性能,Tom决定检查一下select查询清单中使用的列,很快他发现有一个Products.DetailedDescription列是Text类型,通过对应用程序代码的走查,Tom发现其实这一列的数据并不会立即用到,于是他将这一列从select查询清单中取消掉,时间一下子从14秒减少到6秒,于是Tom决定使用一个存储过程应用延迟加载策略加载这个Text列。

  最后Tom还是不死心,认为6秒也无法接受,于是他再次仔细检查了SQL代码,他发现了一个like子句,经过反复研究他认为这个like搜索完全可以用全文搜索替换,最后他用全文搜索替换了like搜索,时间一下子降低到1秒,至此Tom认为调优应该暂时结束了。

转载于:https://www.cnblogs.com/wangyhua/p/4050546.html

### 如何在 PostgreSQL 中查看查询的执行计划 在 PostgreSQL 中,`EXPLAIN` 是一种核心工具,用于获取查询的执行计划。该命令能够以文本形式返回详细的描述信息,说明数据库引擎如何处理特定 SQL 查询[^1]。 #### 使用 `EXPLAIN` 查看基本执行计划 最简单的使用方式是直接在 SQL 查询前加上 `EXPLAIN` 关键字。例如: ```sql EXPLAIN SELECT * FROM users WHERE user_id = 1; ``` 此语句将显示查询的执行计划,包括访问方法(如顺序扫描或索引扫描)、估计的成本以及行数等信息[^3]。 --- #### 获取更详细的执行计划 如果希望获得更加详尽的信息,可以结合 `ANALYZE` 和 `VERBOSE` 参数一起使用。这些参数可以帮助理解实际运行的行为和更多上下文细节。 - **`ANALYZE`**: 实际执行查询并收集统计信息。 - **`VERBOSE`**: 提供额外的元数据信息,比如表 OID 或别名解析。 示例代码如下: ```sql EXPLAIN (ANALYZE, VERBOSE) SELECT * FROM orders WHERE order_date > '2023-01-01'; ``` 通过这种方式,不仅可以观察到理论上的执行路径,还能评估真实环境中的性能表现[^4]。 --- #### 输出格式的选择 默认情况下,`EXPLAIN` 的输出是以纯文本的形式呈现。为了便于阅读或者与其他工具集成,还可以指定不同的输出格式,例如 JSON、XML 或 YAML。 以下是设置为 JSON 格式的例子: ```sql EXPLAIN (FORMAT JSON) SELECT * FROM products WHERE category = 'Electronics'; ``` 这种结构化的输出非常适合自动化脚本分析或图形化展示工具[^1]。 --- #### 并行查询的支持 对于复杂的查询场景,尤其是涉及大量数据集的操作,PostgreSQL 支持生成并行执行计划。要深入了解此类情况下的优化策略,则需关注系统配置项(如 `max_parallel_workers_per_gather`),同利用前面提到的技术手段验证效果[^4]。 --- ### 总结 综上所述,在 PostgreSQL 中可以通过多种途径来审查查询的执行过程及其潜在改进空间。无论是基础版还是增强功能选项,都旨在协助开发人员快速定位低效环节进而提升整体应用效能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值