SQL 查询优化

Posted

技术标签:

【中文标题】SQL 查询优化【英文标题】:SQL Query Optimization 【发布时间】:2011-06-22 21:34:10 【问题描述】:

当有 8000 行要处理时,此报告过去需要大约 16 秒。现在有 50000 行,报表需要 2:30 分钟。

这是我的第一次通过,昨天客户需要它,所以我按照需要完成的逻辑顺序编写了这段代码,但没有考虑优化。

现在随着数据的增加报告需要更长的时间,我需要重新审视并优化它。我在考虑索引视图、表函数等。

我认为最大的瓶颈是循环遍历临时表,制作 4 条选择语句,并更新临时表...50,000 次。

我想我可以将所有这些压缩到一个大的 SELECT 中,使用 (a) 4 个连接到同一个表以获得 4 个状态,但是我不确定如何在那里获得 TOP 1,或者我可以尝试 (b) 使用嵌套子查询,但与当前代码相比,两者看起来都非常混乱。

我不希望任何人为我编写代码,但如果一些 SQL 专家可以仔细阅读此代码并告诉我任何明显的低效率和替代方法,或加快此过程的方法,或我应该使用的技术,将不胜感激。

PS:假设这个数据库大部分是规范化的,但设计不佳,并且我无法添加索引。我基本上必须照原样使用它。

在代码显示(小于)的地方,我不得不替换“小于”符号,因为它正在裁剪我的一些代码。

谢谢!

创建过程 RptCollectionAccountStatusReport AS 设置无计数; 声明@Accounts 表 ( [AccountKey] INT IDENTITY(1,1) NOT NULL, [管理公司] NVARCHAR(50), [关联] NVARCHAR(100), [AccountNo] INT 唯一, [街道地址] NVARCHAR(65), [状态] NVARCHAR(50), [PrimaryStatus] NVARCHAR(100), [PrimaryStatusDate] SMALLDATETIME, [PrimaryDaysRemaining] INT, [SecondaryStatus] NVARCHAR(100), [SecondaryStatusDate] SMALLDATETIME, [SecondaryDaysRemaining] INT, [第三状态] NVARCHAR(100), [TertiaryStatusDate] SMALLDATETIME, [TertiaryDaysRemaining] INT, [外部状态] NVARCHAR(100), [外部状态日期] SMALLDATETIME, [ExternalDaysRemaining] INT ); 插入 @帐户 ( [管理公司], [协会], [户口号码], [街道地址], [状态]) 选择 mc.Name AS [ManagementCompany], a.LegalName AS [协会], c.CollectionKey AS [AccountNo], u.StreetNumber + ' ' + u.StreetName AS [StreetAddress], CASE WHEN c.InheritedAccount = 1 THEN 'ZZ' ELSE u.State END AS [State] 从 ManagementCompany mc WITH (NOLOCK) 加入 关联 a WITH (NOLOCK) ON a.ManagementCompanyKey = mc.ManagementCompanyKey 加入 单元 u WITH (NOLOCK) ON u.AssociationKey = a.AssociationKey 加入 集合 c WITH (NOLOCK) ON c.UnitKey = u.UnitKey 在哪里 c.已关闭为空; 声明@MaxAccountKey INT; 从@Accounts 中选择@MaxAccountKey = MAX([AccountKey]); 声明@index INT; 设置@index = 1; WHILE @index(小于)@MaxAccountKey BEGIN 声明@CollectionKey INT; SELECT @CollectionKey = [AccountNo] FROM @Accounts WHERE [AccountKey] = @index; 声明@PrimaryStatus NVARCHAR(100) = NULL; 声明@PrimaryStatusDate SMALLDATETIME = NULL; 声明@PrimaryDaysRemaining INT = NULL; 声明@SecondaryStatus NVARCHAR(100) = NULL; 声明@SecondaryStatusDate SMALLDATETIME = NULL; 声明@SecondaryDaysRemaining INT = NULL; 声明@TertiaryStatus NVARCHAR(100) = NULL; 声明@TertiaryStatusDate SMALLDATETIME = NULL; 声明@TertiaryDaysRemaining INT = NULL; 声明@ExternalStatus NVARCHAR(100) = NULL; 声明@ExternalStatusDate SMALLDATETIME = NULL; 声明@ExternalDaysRemaining INT = NULL; 选择前 1 @PrimaryStatus = a.StatusName,@PrimaryStatusDate = c.StatusDate,@PrimaryDaysRemaining = c.DaysRemaining FROM CollectionAccountStatus c WITH (NOLOCK) JOIN AccountStatus a WITH (NOLOCK) ON c.AccountStatusKey = a.AccountStatusKey WHERE c.CollectionKey = @CollectionKey AND a.StatusType = 'Primary Status' AND a.StatusName 'Cleared' 按 c.sysCreated DESC 排序; 选择前 1 @SecondaryStatus = a.StatusName,@SecondaryStatusDate = c.StatusDate,@SecondaryDaysRemaining = c.DaysRemaining FROM CollectionAccountStatus c WITH (NOLOCK) JOIN AccountStatus a WITH (NOLOCK) ON c.AccountStatusKey = a.AccountStatusKey WHERE c.CollectionKey = @CollectionKey AND a.StatusType = 'Secondary Status' AND a.StatusName 'Cleared' 按 c.sysCreated DESC 排序; 选择前 1 @TertiaryStatus = a.StatusName,@TertiaryStatusDate = c.StatusDate,@TertiaryDaysRemaining = c.DaysRemaining FROM CollectionAccountStatus c WITH (NOLOCK) JOIN AccountStatus a WITH (NOLOCK) ON c.AccountStatusKey = a.AccountStatusKey WHERE c.CollectionKey = @CollectionKey AND a.StatusType = 'Tertiary Status' AND a.StatusName 'Cleared' 按 c.sysCreated DESC 排序; 选择前 1 @ExternalStatus = a.StatusName,@ExternalStatusDate = c.StatusDate,@ExternalDaysRemaining = c.DaysRemaining FROM CollectionAccountStatus c WITH (NOLOCK) JOIN AccountStatus a WITH (NOLOCK) ON c.AccountStatusKey = a.AccountStatusKey WHERE c.CollectionKey = @CollectionKey AND a.StatusType = '外部状态' AND a.StatusName '已清除' 按 c.sysCreated DESC 排序; 更新 @Accounts 放 [PrimaryStatus] = @PrimaryStatus, [PrimaryStatusDate] = @PrimaryStatusDate, [PrimaryDaysRemaining] = @PrimaryDaysRemaining, [SecondaryStatus] = @SecondaryStatus, [SecondaryStatusDate] = @SecondaryStatusDate, [SecondaryDaysRemaining] = @SecondaryDaysRemaining, [TertiaryStatus] = @TertiaryStatus, [TertiaryStatusDate] = @TertiaryStatusDate, [TertiaryDaysRemaining] = @TertiaryDaysRemaining, [外部状态] = @外部状态, [外部状态日期] = @外部状态日期, [ExternalDaysRemaining] = @ExternalDaysRemaining 在哪里 [AccountNo] = @CollectionKey; SET @index = @index + 1; 结尾; 选择 [管理公司], [协会], [户口号码], [街道地址], [状态], [主要状态], 转换(VARCHAR,[PrimaryStatusDate],101)作为 [PrimaryStatusDate], [PrimaryDaysRemaining], [次要状态], 转换(VARCHAR,[SecondaryStatusDate],101)作为 [SecondaryStatusDate], [SecondaryDaysRemaining], [第三状态], 转换(VARCHAR,[TertiaryStatusDate],101)作为 [TertiaryStatusDate], [TertiaryDaysRemaining], [外部状态], 转换(VARCHAR,[ExternalStatusDate],101)作为[ExternalStatusDate], [ExternalDaysRemaining] 从 @Accounts 订购方式 [管理公司], [协会], [街道地址] 升序;

【问题讨论】:

您使用的是什么版本的 SQL Server? 请不要使用 NOLOCK - 它会导致错误的结果! 【参考方案1】:

    不要试图猜测查询出错的地方 - 查看执行计划。它会告诉你是什么在消耗你的资源。

    您可以直接从另一个表更新,甚至从表变量:SQL update from one Table to another based on a ID match

这将允许您将循环中的所有内容组合成一个(大量)语句。您可以使用不同的别名,例如,

JOIN AccountStatus As TertiaryAccountStatus...AND a.StatusType = 'Tertiary Status'
JOIN AccountStatus AS SecondaryAccountStatus...AND a.StatusType = 'Secondary Status'
    我敢打赌,您在 AccountStatus.StatusType 字段上没有索引。您可以尝试改用该表的 PK。

HTH。

【讨论】:

【参考方案2】:

首先使用临时表而不是表变量。这些可以被索引。

接下来,不要循环!几乎在所有情况下,循环都不利于性能。这个循环运行 50000 次而不是 50000 条记录一次,当你有 100 万条记录时,这将是可怕的!这是一个链接,可以帮助您了解如何进行基于集合的处理。它是为了避免游标而编写的,但循环类似于游标,所以它应该有所帮助。 http://wiki.lessthandot.com/index.php/Cursors_and_How_to_Avoid_Them

And (nolock) 会读取脏数据,这对报告非常不利。如果您使用的 SQl Server 版本高于 2000,则有更好的选择。

【讨论】:

【参考方案3】:
SELECT @CollectionKey = [AccountNo] FROM @Accounts WHERE [AccountKey] = @index;

此查询将受益于表变量上的 PRIMARY KEY 声明。

当您说 IDENTITY 时,您是在要求数据库自动填充该列。 当您说 PRIMARY KEY 时,您是在要求数据库将数据组织成一个聚集索引。

这两个概念非常不同。通常,您应该同时使用它们。

DECLARE @Accounts TABLE
(
  [AccountKey] INT IDENTITY(1,1) PRIMARY KEY,

我无法添加索引。

在这种情况下,请将数据复制到可以添加索引的数据库中。并使用:SET STATISTICS IO ON

【讨论】:

此报告由公司高管每天运行 4-5 次,必须指向生产数据库。这不是一次性报告,因此将数据复制到另一个数据库似乎不是一种选择。 如果你使用临时表,你可以索引。

以上是关于SQL 查询优化的主要内容,如果未能解决你的问题,请参考以下文章

数据库牛人是如何进行SQL优化的?

SQL多个表联合查询优化的问题

DBA的五款最佳SQL查询优化工具

优化SQL查询:如何写出高性能SQL语句

SQL优化----百万数据查询优化

一文终结SQL 子查询优化