如何优化太慢的存储过程? [关闭]

Posted

技术标签:

【中文标题】如何优化太慢的存储过程? [关闭]【英文标题】:How to optimise a stored procedure that is too slow? [closed] 【发布时间】:2017-09-25 21:28:25 【问题描述】:

我的存储过程大约需要 1 分 45 秒,我该如何优化它?我尝试了几件事,例如在插入之前创建临时表。

我有预计的执行计划,但我不知道如何上传。

这是我的存储过程的一部分,它需要更长的时间,查询成本是 53%。

SELECT Distinct
    BackupCTE.[InstanceName]
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.EnvironmentType = 0 THEN '-1' Else CONVERT(VARCHAR,BackupCTE.EnvironmentType) END  AS EnvironmentType
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.InstanceStatus = 0 THEN '-1' Else CONVERT(VARCHAR,BackupCTE.InstanceStatus) END AS [InstanceStatus] 
   ,BackupCTE.[BackupShare]
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.DatabaseOwner = 0 THEN '-1'
          WHEN ISNULL(BackupCTE.IsMirroringEnabled,0) = 0 AND BackupCTE.DatabaseOwner NOT IN ('SA') AND BackupCTE.DatabaseStatus = 'Normal' AND BackupCTE.DatabaseReadOnly = 0
              AND ISNULL(BackupCTE.IsDatabaseSnapshot,0) = 0 THEN '0'
         ELSE '1' END AS DatabaseOwner
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.DBRecoveryModel = 0 THEN '-1' 
          WHEN BackupCTE.DatabaseStatus = 'Normal' AND BackupCTE.DatabaseName NOT IN ('master', 'msdb', 'IHC_DBA','distribution','ReportServerTempDB','NavicareReporting','ReportServer') AND
               BackupCTE.RecoveryModel NOT IN ('Full','BulkLogged') THEN '0'
          ELSE '1'
    END AS [DBRecoveryModel]
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.AutoShrink = 0 THEN '-1'              
         ELSE CONVERT(VARCHAR, BackupCTE.[AutoShrink]) END AS [AutoShrink]
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.AutoClose = 0 THEN '-1' Else CONVERT(VARCHAR,BackupCTE.[AutoClose]) END AS [AutoClose]
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.BackupCompression = 0 THEN '-1' 
          WHEN BackupCTE.[BackupCompression] = 0 THEN '0'
          Else '1' END AS [BackupCompression]
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.XPCmdShell = 0 THEN '-1' 
         WHEN CONVERT(VARCHAR,BackupCTE.[XPCmdShell]) = 0 THEN '1'
         Else '0' END AS [XPCmdShell]
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.EncryptionEnabled = 0 THEN '-1' Else CONVERT(VARCHAR,BackupCTE.[EncryptionEnabled]) END AS [EncryptionEnabled]
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.IsIdera = 0 THEN '-1' Else CONVERT(VARCHAR,BackupCTE.[IsIdera]) END AS [IsIdera]
   ,[HoursRetentionShare]
   ,[HoursSinceLastFullBackup]
   ,[HoursSinceLastDiffBackup]
   ,[HoursSinceLastLogBackup]
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.ExceededBkpTimeFrame = 0 THEN '-1' Else BackupCTE.ExceededBkpTimeFrame 
    END AS ExceededBkpTimeFrame
   ,BackupType
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.[Primary] = 0 THEN '-1' Else BackupCTE.[Primary] END AS [Primary]
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.[Secondary] = 0 THEN '-1' Else BackupCTE.[Secondary] END AS [Secondary]
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.[LockPages] = 0 THEN '-1' 
        Else CONVERT(varchar,BackupCTE.[LockPages]) END  AS [LockPages]
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.[SADisabled] = 0 THEN '-1'  
         WHEN CONVERT(varchar,BackupCTE.[SADisabled]) = 0 AND BackupCTE.VersionName like 'Microsoft SQL Server  2000 %' THEN '1' 
         ELSE CONVERT(varchar,BackupCTE.[SADisabled])
         END AS [SADisabled]
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.ServiceAccount = 0 THEN '-1'
         WHEN StdServiceAccount.IsValid = 1
                    OR BackupCTE.ServiceAccount IN ('CO\lpsqldbadmin','lpsqldbadmin@CO.IHC.COM')                
            THEN  '1'
            ELSE '0' END AS ServiceAccount
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.[ServicePackFlag] = 0 THEN '-1' Else BackupCTE.[ServicePack] END AS [ServicePack]
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.[MaxMemory] = 0 THEN '-1' Else BackupCTE.[MaxMemory] END AS [MaxMemory]
   ,[JobName]
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.[JobOwner] = 0 THEN '-1'
         WHEN BackupCTE.JobOwner NOT IN ('SA') AND JobEnabled = 1 AND DELETED IS NULL THEN '0' 
         ELSE '1' END AS JobOwner 
   ,[JobEnabled]
   ,CASE WHEN BackupCTE.EnvironmentType = 'PROD' AND BackupCTE.[Primary] NOT IN ('Tamie Jensen', 'Chase Mahony','Megna Musapeta','Aaron Uppencamp') THEN '0' 
         WHEN BackupCTE.EnvironmentType = 'STBY' AND BackupCTE.[Primary] NOT IN ('Tamie Jensen', 'Chase Mahony','Megna Musapeta','Aaron Uppencamp') THEN '0'
         ELSE '1' END AS IsNonPrimary
   ,CASE WHEN BackupCTE.EnvironmentType = 'PROD' AND BackupCTE.[Secondary] IN ('NONE') THEN '0' 
         WHEN BackupCTE.EnvironmentType = 'STBY' AND BackupCTE.[Secondary] IN ('NONE') THEN '0'
         ELSE '1' END AS IsNonSecondary
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.[MaxMemory] = 0 THEN '-1' 
         WHEN BackupCTE.[MaxMemory]%8 = 0 THEN '1' 
         ELSE '0' END AS FlagMemory
   ,CASE WHEN BackupCTE.JobName NOT LIKE '%Insure%'
   THEN
       CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.MaintenanceJob = 0 THEN '-1'
              WHEN JobSchMultiplier < 0 THEN '1' --One Time Job. No Need To Flag
              WHEN DATEDIFF(HH, BackupCTE.JobRunDate, GETDATE()) > 24*JobSchMultiplier THEN '0'
              ELSE '1'            
        END
    ELSE '1' END  AS MaintenanceJob
    ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.ServicePackFlag = 0 THEN '-1'
          WHEN LTRIM(RTRIM(SUBSTRING(PatchingStds.ServicePack,3, LEN(PatchingStds.ServicePack)-2))) > ISNULL(LTRIM(RTRIM(SUBSTRING(InstanceSQLDTl.ServicePack,3, 
              LEN(InstanceSQLDTl.ServicePack)-2))),0) THEN '0'
         WHEN LTRIM(RTRIM(SUBSTRING(VersionName,CHARINDEX('-',VersionName )+1,abs(CASE WHEN CHARINDEX('(',VersionName ) > 0 THEN CHARINDEX('(',VersionName )-CHARINDEX('-',VersionName) ELSE len(VersionName)-CHARINDEX('-',VersionName)  end)))) <> LTRIM(RTRIM(PatchingStds.SQLBuild)) THEN '2'
         ELSE '1' END AS ServicePackFlag
   ,VersionName 
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.SAViolations = 0 THEN '-1'
         WHEN LoginName Not IN ('CO\DBA Group','CO\DBAdmin') AND SYSAdmin = 1 OR SecurityAdmin = 1 OR ServerAdmin = 1 OR SetupAdmin = 1 OR ProcessAdmin = 1 OR DiskAdmin = 1
                                                             OR DBCreator = 1 OR BulkAdmin = 1 THEN '0'
         ELSE '1' END AS SAViolations 
   ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.IsBackupShare = 0 THEN '-1'
         WHEN NOT (BackupCTE.[BackupShare] LIKE '\\CO.IHC.COM%' OR BackupCTE.[BackupShare] LIKE '\\CO-LP-SQL1%' OR BackupCTE.[BackupShare] LIKE '\\CO-LP-SQL2%' 
               OR BackupCTE.[BackupShare] LIKE '\\CO-TX-VAULT2%' OR BackupCTE.[BackupShare] LIKE '\\co-tx-vpdsfile2\txPDSsqlBackups%' AND BackupCTE.[BackupShare] NOT LIKE '%Test') OR BackupCTE.[BackupShare] = 'NONE LISTED' THEN '0'
         ELSE '1' END AS IsBackupShare
    ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.Tempdb_DataFileCount = 0 THEN '-1'
          WHEN TEMPDBFileCount.Tempdb_DataFileCount >= 8 OR TEMPDBFileCount.Tempdb_DataFileCount > TEMPDBFileCount.OptimalTempdbFilecount THEN '1'
          WHEN TEMPDBFileCount.OptimalTempdbFilecount > TEMPDBFileCount.Tempdb_DataFileCount THEN '0'
          ELSE '1'END AS Tempdb_DataFileCount
    ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.DataAutoGrow = 0 THEN '-1' ELSE BackupCTE.DataAutoGrow END AS DataAutoGrow
    ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.LogAutoGrow = 0 THEN '-1' ELSE BackupCTE.LogAutoGrow END AS LogAutoGrow
    ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.MaintVersion = 0 THEN '-1' ELSE BackupCTE.MaintVersion END AS MaintVersion
    ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.MaintVersion = 0 THEN '-1' 
         WHEN BackupCTE.MaintVersion = 2.02 OR BackupCTE.MaintVersion = 2.03 THEN '1'
         ELSE '0' END AS FlagMaintVersion
    ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.DBCompatibilityLevel = 0 THEN '-1' 
          WHEN BackupCTE.DBCompatibilityLevel < 90 THEN '0'
          ELSE '1' END AS DBCompatibilityLevel
    ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.FlagVersionName = 0 THEN '-1' 
          WHEN NOT (BackupCTE.VersionName LIKE 'Microsoft SQL Server 2005%' OR BackupCTE.VersionName LIKE 'Microsoft SQL Server  2000%') THEN '1'
          ELSE '0' END AS FlagVersionName
    ,CASE WHEN DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL AND ComplianceReportExceptions.VLFCount = 0 THEN '-1' 
          ELSE BackupCTE.VLFCount END AS VLFCount
Into #FinalData
FROM [DBAPP].InstanceSQLDtl 
LEFT JOIN #BackupCTE BackupCTE ON  InstanceSQLDtl.InstanceID = BackupCTE.InstanceID
LEFT JOIN  [DBAPP].[SQLPatchingStandards] PatchingStds ON LTRIM(RTRIM(LEFT(VersionName, CHARINDEX('-', VersionName)-1))) = LTRIM(RTRIM(LEFT
           (PatchingStds.SQLVersion,CHARINDEX('-',PatchingStds.SQLVersion)-1))) 
LEFT JOIN [DBAPP].[ComplianceReportExceptions] ON ComplianceReportExceptions.InstanceName =  BackupCTE.InstanceName
LEFT JOIN [DBAPP].[TEMPDBFileCount] ON InstanceSQLDtl.InstanceID = [TEMPDBFileCount].InstanceID AND Type_DESC = 'ROWS'
LEFT JOIN DBO.fnGetServiceNames() StdServiceAccount ON BackupCTE.InstanceID = StdServiceAccount.InstanceID
Where BackupCTE.[InstanceName] IS NOT NULL
ORDER BY [InstanceName]

【问题讨论】:

所以您在论坛上删除了一个包含近 500 行且没有详细信息的存储过程,并希望有人能够提供帮助???? 你正在解析字符串并在连接中使用 UDF...我想说你很幸运它完成了... 现在只有 100 多行。但是仍然......没有表定义和执行计划,几乎没有人可以在这里做。不过,该函数 fnGetServiceNames 可能是我要开始的地方。 【参考方案1】:

这么多的字符串操作和 CASE 语句总是会很慢。也许您可以将它作为夜间批处理运行到数据集市中?否则,这里有一些提示:

    由于您的许多 CASE 语句都在测试 ...InstanceName IS NULL,因此您可以将查询分成两半,并使用 UNION ALL 将其为空和不为空的记录连接在一起(参见示例)。这将删除一堆 CASE 检查。 如果只有一项,不要使用 WHERE IN('SA'),而是使用不等于 ()。 如果可能,请避免在 JOIN 条件下进行字符串操作 (...PatchingStds ON LTRIM(RTRIM(LEFT(VersionName...)。尝试将这些字段存储在某个地方,无论是在专用列中还是作为持久的计算列。这样您就可以在该字段上添加索引以加快连接速度。 在可能的情况下,在连接字段上设置索引,这受许多因素的影响,但大多数情况下,单个表上不要有太多索引,因为这会减慢 INSERT 语句的速度。
SELECT DISTINCT 
    BackupCTE.[InstanceName]
   , EnvironmentType = CASE WHEN ComplianceReportExceptions.EnvironmentType = 0 THEN '-1' ELSE CONVERT(VARCHAR, BackupCTE.EnvironmentType) END
   , [InstanceStatus] = CASE WHEN ComplianceReportExceptions.InstanceStatus = 0 THEN '-1' ELSE CONVERT(VARCHAR,BackupCTE.InstanceStatus) END 
   , BackupCTE.[BackupShare]
   , DatabaseOwner = 
        CASE WHEN ComplianceReportExceptions.DatabaseOwner = 0 THEN '-1'
             WHEN ISNULL(BackupCTE.IsMirroringEnabled,0) = 0 
                AND BackupCTE.DatabaseOwner <> 'SA'
                AND BackupCTE.DatabaseStatus = 'Normal' 
                AND BackupCTE.DatabaseReadOnly = 0
                AND ISNULL(BackupCTE.IsDatabaseSnapshot,0) = 0 
            THEN '0'
            ELSE '1' 
         END
    -- Etc...
INTO #FinalData
FROM [DBAPP].InstanceSQLDtl 
    LEFT JOIN #BackupCTE BackupCTE ON  InstanceSQLDtl.InstanceID = BackupCTE.InstanceID
    LEFT JOIN  [DBAPP].[SQLPatchingStandards] PatchingStds ON LTRIM(RTRIM(LEFT(VersionName, CHARINDEX('-', VersionName)-1))) = LTRIM(RTRIM(LEFT
               (PatchingStds.SQLVersion,CHARINDEX('-',PatchingStds.SQLVersion)-1))) 
    LEFT JOIN [DBAPP].[ComplianceReportExceptions] ON ComplianceReportExceptions.InstanceName =  BackupCTE.InstanceName
    LEFT JOIN [DBAPP].[TEMPDBFileCount] ON InstanceSQLDtl.InstanceID = [TEMPDBFileCount].InstanceID AND Type_DESC = 'ROWS'
    LEFT JOIN DBO.fnGetServiceNames() StdServiceAccount ON BackupCTE.InstanceID = StdServiceAccount.InstanceID
WHERE BackupCTE.[InstanceName] IS NOT NULL
    AND DBAPP.ComplianceReportExceptions.InstanceName IS NOT NULL
--****************
UNION ALL 
--****************
SELECT DISTINCT 
    BackupCTE.[InstanceName]
   , EnvironmentType = CONVERT(VARCHAR,BackupCTE.EnvironmentType)
   , [InstanceStatus] = CONVERT(VARCHAR,BackupCTE.InstanceStatus)
   , BackupCTE.[BackupShare]
   , DatabaseOwner = 
       CASE 
            WHEN ISNULL(BackupCTE.IsMirroringEnabled,0) = 0 
                AND BackupCTE.DatabaseOwner <> 'SA'
                AND BackupCTE.DatabaseStatus = 'Normal' 
                AND BackupCTE.DatabaseReadOnly = 0
                AND ISNULL(BackupCTE.IsDatabaseSnapshot,0) = 0 
            THEN '0'
            ELSE '1' 
        END
-- Etc...
INTO #FinalData
FROM [DBAPP].InstanceSQLDtl 
    LEFT JOIN #BackupCTE BackupCTE ON  InstanceSQLDtl.InstanceID = BackupCTE.InstanceID
    LEFT JOIN  [DBAPP].[SQLPatchingStandards] PatchingStds ON LTRIM(RTRIM(LEFT(VersionName, CHARINDEX('-', VersionName)-1))) = LTRIM(RTRIM(LEFT
               (PatchingStds.SQLVersion,CHARINDEX('-',PatchingStds.SQLVersion)-1))) 
    LEFT JOIN [DBAPP].[ComplianceReportExceptions] ON ComplianceReportExceptions.InstanceName =  BackupCTE.InstanceName
    LEFT JOIN [DBAPP].[TEMPDBFileCount] ON InstanceSQLDtl.InstanceID = [TEMPDBFileCount].InstanceID AND Type_DESC = 'ROWS'
    LEFT JOIN DBO.fnGetServiceNames() StdServiceAccount ON BackupCTE.InstanceID = StdServiceAccount.InstanceID
WHERE BackupCTE.[InstanceName] IS NOT NULL
    AND DBAPP.ComplianceReportExceptions.InstanceName IS NULL
--****************
ORDER BY [InstanceName]

【讨论】:

分而治之是解决这个问题的好方法。【参考方案2】:

以下是导致查询性能下降的可能性,

    如果您使用转换,请不要在 on 子句中转换列值,而是创建一个单独的列并预先填充转换 它禁止使用索引来搜索表中的记录。

    与(无锁)一起使用

    Distinct 和 order by 是查询的昂贵操作,如果您不需要将其从查询中删除。

【讨论】:

以上是关于如何优化太慢的存储过程? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

SQL(及存储过程)跑得太慢怎么办?

SQL(及存储过程)跑得太慢怎么办?

SQL(及存储过程)跑得太慢怎么办?

SQL(及存储过程)跑得太慢怎么办?

SQL(及存储过程)跑得太慢怎么办?

SQL(及存储过程)跑得太慢怎么办?