首页 文章

SQL查询优化

提问于
浏览
0

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

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

现在随着数据的增加,报告需要更长的时间,我需要再看一下并优化它 . 我正在考虑索引视图,表函数等 .

我认为最大的瓶颈是循环访问临时表,制作4个select语句,并更新临时表... 50,000次 .

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

我不希望任何人为我编写代码,但是如果一些SQL专家可以仔细阅读这些代码并告诉我任何明显的低效率和替代方法,或者加快速度的方法,或者我应该使用的技术,那就是赞赏 .

PS: Assume that this DB is for the most part normalized, but poorly designed, and that I am not able to add indexes. I basically have to work with it, as is.


Where the code says (less than) I had to replace a "less than" symbol because it was cropping some of my code.

谢谢!

CREATE PROCEDURE RptCollectionAccountStatusReport AS

SET NOCOUNT ON;

DECLARE @Accounts TABLE
(
  [AccountKey] INT IDENTITY(1,1) NOT NULL,
  [ManagementCompany] NVARCHAR(50),
  [Association] NVARCHAR(100),
  [AccountNo] INT UNIQUE,
  [StreetAddress] NVARCHAR(65),
  [State] NVARCHAR(50),
  [PrimaryStatus] NVARCHAR(100),
  [PrimaryStatusDate] SMALLDATETIME,
  [PrimaryDaysRemaining] INT,
  [SecondaryStatus] NVARCHAR(100),
  [SecondaryStatusDate] SMALLDATETIME,
  [SecondaryDaysRemaining] INT,
  [TertiaryStatus] NVARCHAR(100),
  [TertiaryStatusDate] SMALLDATETIME,
  [TertiaryDaysRemaining] INT,
  [ExternalStatus] NVARCHAR(100),
  [ExternalStatusDate] SMALLDATETIME,
  [ExternalDaysRemaining] INT
);

INSERT INTO
  @Accounts (
    [ManagementCompany],
    [Association],
    [AccountNo],
    [StreetAddress],
    [State])
SELECT
  mc.Name AS [ManagementCompany],
  a.LegalName AS [Association],
  c.CollectionKey AS [AccountNo],
  u.StreetNumber + ' ' + u.StreetName AS [StreetAddress],
  CASE WHEN c.InheritedAccount = 1 THEN 'ZZ' ELSE u.State END AS [State]
FROM
  ManagementCompany mc WITH (NOLOCK)
JOIN
  Association a WITH (NOLOCK) ON a.ManagementCompanyKey = mc.ManagementCompanyKey
JOIN
  Unit u WITH (NOLOCK) ON u.AssociationKey = a.AssociationKey
JOIN
  Collection c WITH (NOLOCK) ON c.UnitKey = u.UnitKey
WHERE
  c.Closed IS NULL;

DECLARE @MaxAccountKey INT;
SELECT @MaxAccountKey = MAX([AccountKey]) FROM @Accounts;

DECLARE @index INT;
SET @index = 1;

WHILE @index (less than) @MaxAccountKey BEGIN

DECLARE @CollectionKey INT;
SELECT @CollectionKey = [AccountNo] FROM @Accounts WHERE [AccountKey] = @index;

DECLARE @PrimaryStatus NVARCHAR(100) = NULL;
DECLARE @PrimaryStatusDate SMALLDATETIME = NULL;
DECLARE @PrimaryDaysRemaining INT = NULL;
DECLARE @SecondaryStatus NVARCHAR(100) = NULL;
DECLARE @SecondaryStatusDate SMALLDATETIME = NULL;
DECLARE @SecondaryDaysRemaining INT = NULL;
DECLARE @TertiaryStatus NVARCHAR(100) = NULL;
DECLARE @TertiaryStatusDate SMALLDATETIME = NULL;
DECLARE @TertiaryDaysRemaining INT = NULL;
DECLARE @ExternalStatus NVARCHAR(100) = NULL;
DECLARE @ExternalStatusDate SMALLDATETIME = NULL;
DECLARE @ExternalDaysRemaining INT = NULL;

SELECT TOP 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'
ORDER BY c.sysCreated DESC;

SELECT TOP 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'
ORDER BY c.sysCreated DESC;

SELECT TOP 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'
ORDER BY c.sysCreated DESC;

SELECT TOP 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 = 'External Status' AND a.StatusName  'Cleared'
ORDER BY c.sysCreated DESC;

UPDATE
  @Accounts
SET
  [PrimaryStatus] = @PrimaryStatus,
  [PrimaryStatusDate] = @PrimaryStatusDate,
  [PrimaryDaysRemaining] = @PrimaryDaysRemaining,
  [SecondaryStatus] = @SecondaryStatus,
  [SecondaryStatusDate] = @SecondaryStatusDate,
  [SecondaryDaysRemaining] = @SecondaryDaysRemaining,
  [TertiaryStatus] = @TertiaryStatus,
  [TertiaryStatusDate] = @TertiaryStatusDate,
  [TertiaryDaysRemaining] = @TertiaryDaysRemaining,
  [ExternalStatus] = @ExternalStatus,
  [ExternalStatusDate] = @ExternalStatusDate,
  [ExternalDaysRemaining] = @ExternalDaysRemaining
WHERE
  [AccountNo] = @CollectionKey;

SET @index = @index + 1;

END;

SELECT
  [ManagementCompany],
  [Association],
  [AccountNo],
  [StreetAddress],
  [State],
  [PrimaryStatus],
  CONVERT(VARCHAR, [PrimaryStatusDate], 101) AS [PrimaryStatusDate],
  [PrimaryDaysRemaining],
  [SecondaryStatus],
  CONVERT(VARCHAR, [SecondaryStatusDate], 101) AS [SecondaryStatusDate],
  [SecondaryDaysRemaining],
  [TertiaryStatus],
  CONVERT(VARCHAR, [TertiaryStatusDate], 101) AS [TertiaryStatusDate],
  [TertiaryDaysRemaining],
  [ExternalStatus],
  CONVERT(VARCHAR, [ExternalStatusDate], 101) AS [ExternalStatusDate],
  [ExternalDaysRemaining]
FROM
 @Accounts
ORDER BY
  [ManagementCompany],
  [Association],
  [StreetAddress]
ASC;

3 回答

  • 4

    首先使用临时表而不是表变量 . 这些可以编入索引 .

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

    并且(nolock)将提供脏数据读取,这对于报告非常糟糕 . 如果您的SQl Server版本高于2000,则有更好的选择 .

  • 2

    这将允许您将循环中的所有内容组合成单个(大量)语句 . 您可以使用不同的别名加入相同的表,以获取二级和三级状态,例如,

    JOIN AccountStatus As TertiaryAccountStatus...AND a.StatusType = 'Tertiary Status'
    JOIN AccountStatus AS SecondaryAccountStatus...AND a.StatusType = 'Secondary Status'
    
    • I 'll bet you don' t在AccountStatus.StatusType字段上有一个索引 . 您可以尝试使用该表的PK .

    HTH .

  • 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

相关问题