哪些技术可以有效地提高 SQL 查询的性能?有适用的一般规则吗?
11 回答
- 使用主键
- 避免选择 *
- 在构建条件语句时尽可能具体
- 反规范化通常可以更有效
- 表变量和临时表(如果可用)通常比使用大型源表更好
- 分区视图
- 使用索引和约束
了解幕后真正发生的事情 - 您应该能够详细了解以下概念:
- 索引(不仅仅是它们是什么,而是它们实际上是如何工作的)。
- 聚集索引与堆分配表。
- 文本和二进制查找以及何时可以内联。
- 填充因子。
- 如何为更新/删除创建幻影记录。
- 何时发生页面拆分以及原因。
- 统计数据,以及它们如何影响各种查询速度。
- 查询规划器,以及它如何为您的特定数据库工作(例如,在某些系统上,“select *”很慢,在现代 MS-Sql DB 上,规划器可以处理它)。
您可以做的最重要的事情是在 sql server 查询分析器中查找表扫描(确保打开“显示执行计划”)。否则,在 MSDN 和其他地方有无数的文章可以提供很好的建议。
顺便说一句,当我开始学习优化查询时,我针对跟踪运行了 sql server 查询分析器,查看了生成的 SQL,并试图弄清楚为什么这是一个改进。查询分析器远非最佳,但它是一个不错的开始。
您可以查看几件事来优化查询性能。
确保您只有最少的数据。确保仅选择所需的列。将字段大小减少到最小。
考虑对数据库进行反规范化以减少连接
避免循环(即获取游标),坚持设置操作。
将查询实现为存储过程,因为这是预编译的,并且执行速度更快。
确保您设置了正确的索引。如果您的数据库主要用于搜索,请考虑更多索引。
使用执行计划来查看处理是如何完成的。您要避免的是表扫描,因为这很昂贵。
确保自动统计设置为打开。SQL 需要它来帮助确定最佳执行。有关更多信息,请参阅 Mike Gunderloy 的精彩帖子。SQL Server 2005 中的统计基础
确保您的索引没有碎片化。减少 SQL Server 索引碎片
- 确保您的表没有碎片。如何检测 SQL Server 2000 和 2005 中的表碎片
使用with语句来处理查询过滤。将每个子查询限制为尽可能少的行数。然后加入子查询。
WITH
master AS
(
SELECT SSN, FIRST_NAME, LAST_NAME
FROM MASTER_SSN
WHERE STATE = 'PA' AND
GENDER = 'M'
),
taxReturns AS
(
SELECT SSN, RETURN_ID, GROSS_PAY
FROM MASTER_RETURNS
WHERE YEAR < 2003 AND
YEAR > 2000
)
SELECT *
FROM master,
taxReturns
WHERE master.ssn = taxReturns.ssn
with 语句中的子查询最终可能与内联视图或自动生成的临时表相同。我发现在我所做的工作(零售数据)中,大约 70-80% 的时间会带来性能优势。
100% 的时间,有维护的好处。
我认为使用 SQL 查询分析器将是一个好的开始。
在 Oracle 中,您可以查看解释计划以比较查询的变体
确保表上有正确的索引。如果您经常使用列作为排序或限制数据集的一种方式,则索引可能会产生很大的不同。我在最近的一篇文章中看到 select distinct 确实会减慢查询速度,尤其是在没有索引的情况下。
SELECT 查询的明显优化是确保您在用于连接的列或 WHERE 子句中具有索引。
由于添加索引会减慢数据写入速度,因此您确实需要监控性能以确保不会破坏数据库的写入性能,但这就是使用良好的查询分析工具可以帮助您相应地平衡事情的地方。
- 索引
- 统计数据
- 在 microsoft stack 上,数据库引擎优化顾问
其他一些观点(我的基于 SQL 服务器,因为每个数据库后端都有自己的实现,它们可能适用于所有数据库,也可能不适用):
避免在语句的选择部分出现相关子查询,它们本质上是游标。
设计您的表以使用正确的数据类型,以避免必须对其应用函数来获取数据。例如,当您将数据存储为 varchar 时,进行日期数学运算要困难得多。
如果您发现您经常执行包含函数的连接,那么您需要考虑重新设计您的表。
如果您的 WHERE 或 JOIN 条件包括 OR 语句(速度较慢),则使用 UNION 语句可能会获得更好的速度。
如果(且仅当)这两个语句互斥并且以任何方式返回相同的结果时,UNION ALL 比 UNION 快。
NOT EXISTS 通常比 NOT IN 或使用带有 ID = null 的 WHERE 子句的左连接更快
在 UPDATE 查询中添加 WHERE 条件以确保您没有更新已经相等的值。更新 10,000,000 条记录和 4 条记录之间的差异可能非常显着!
如果您将经常查询它们或用于大型报告,请考虑预先计算一些值。订单中的值的总和只需要在订单下达或调整时进行,而不是在报表中汇总 10,000,000 亿个订单的结果时进行。应在触发器中进行预计算,以便它们始终是最新的底层数据更改。它也不必只是数字,我们有一个计算字段,可以连接我们在报告中使用的名称。
小心标量 UDF,它们可能比将代码排成一行要慢。
对于大型数据集,临时表往往更快,而对于小型数据集,表变量往往更快。此外,您可以索引临时表。
在用户界面中格式化通常比在 SQL 中更快。
不要返回比实际需要更多的数据。
这似乎很明显,但您不会相信我最终修复此问题的频率。不要加入您不用于过滤记录或实际调用语句选择部分中的字段之一的表。不必要的连接可能非常昂贵。
创建调用其他视图的视图调用其他视图是一个非常糟糕的主意。当您只需要一次并在基础视图中创建 100,000,00 条记录以获得最终结果中的 6 条记录时,您可能会发现您正在加入同一个表 6 次。
在设计数据库时,不仅要考虑报告输入数据的用户界面。数据不使用就没有用,所以要考虑它在数据库中之后将如何使用,以及如何维护或审计这些数据。这通常会改变设计。(这就是为什么让 ORM 设计你的表是一个糟糕的主意的原因之一,它只考虑数据的一个用例。)影响最多数据的最复杂的查询是在报告中,因此设计更改以帮助报告可以大大加快查询(并简化它们)。
特定于数据库的功能实现可能比使用标准 SQL 更快(这是他们销售产品的方式之一),因此了解您的数据库功能并找出哪些更快。
And because it can't be said too often, use indexes correctly, not too many or too few. And make your WHERE clauses sargable (Able to use indexes).