排查显示两个服务器之间显著性能差异的查询

适用于: SQL Server

本文提供有关查询在一台服务器上运行速度慢于另一台服务器上的性能问题的故障排除步骤。

症状

假设有两个服务器安装了SQL Server。 其中一个SQL Server实例包含另一个 SQL Server 实例中的数据库副本。 对两台服务器上的数据库运行查询时,查询在一台服务器上运行的速度比另一台服务器慢。

以下步骤可帮助排查此问题。

步骤 1:确定它是否是多个查询的常见问题

使用以下两种方法之一来比较两台服务器上两个或多个查询的性能:

  • 在两台服务器上手动测试查询:

    1. 选择多个查询进行测试,优先级放在以下查询上:
      • 在一台服务器上比另一台服务器要快得多。
      • 对用户/应用程序很重要。
      • 经常执行或设计为按需重现问题。
      • 足够长,以捕获数据 (例如,选择 10 秒的查询) ,而不是 5 毫秒的查询。
    2. 在两台服务器上运行查询。
    3. 比较每个查询的两个服务器上的运行时间 (持续时间) 。
  • 使用 SQL Nexus 分析性能数据。

    1. 收集两台服务器上的查询的 PSSDiag/SQLdiagSQL LogScout 数据。
    2. 使用 SQL Nexus 导入收集的数据文件,并比较来自两个服务器的查询。 有关详细信息,请参阅 两个日志集合 (慢速和快速之间的性能比较,例如)

方案 1:只有一个查询在两个服务器上执行不同

如果只有一个查询的执行方式不同,则问题更有可能特定于单个查询,而不是特定于环境。 在这种情况下,请转到 步骤 2:收集数据并确定性能问题的类型

方案 2:多个查询在两台服务器上以不同的方式执行

如果多个查询在一个服务器上运行的速度比另一个服务器慢,则最可能的原因是服务器或数据环境的差异。 转到 诊断环境差异 ,查看两个服务器之间的比较是否有效。

步骤 2:收集数据并确定性能问题的类型

收集运行时间、CPU 时间和逻辑读取

若要在两台服务器上收集查询的运行时间和 CPU 时间,请使用最适合你的情况的以下方法之一:

  • 对于当前正在执行的语句,检查 sys.dm_exec_requests 中的total_elapsed_time列和cpu_time列。 运行以下查询以获取数据:

    SELECT 
        req.session_id
        , req.total_elapsed_time AS duration_ms
        , req.cpu_time AS cpu_time_ms
        , req.total_elapsed_time - req.cpu_time AS wait_time
        , req.logical_reads
        , SUBSTRING (REPLACE (REPLACE (SUBSTRING (ST.text, (req.statement_start_offset/2) + 1, 
           ((CASE statement_end_offset
               WHEN -1
               THEN DATALENGTH(ST.text)  
               ELSE req.statement_end_offset
             END - req.statement_start_offset)/2) + 1) , CHAR(10), ' '), CHAR(13), ' '), 
          1, 512)  AS statement_text  
    FROM sys.dm_exec_requests AS req
        CROSS APPLY sys.dm_exec_sql_text(req.sql_handle) AS ST
    ORDER BY total_elapsed_time DESC;
    
  • 对于过去执行的查询,检查sys.dm_exec_query_stats中的last_elapsed_time列和last_worker_time列。 运行以下查询以获取数据:

    SELECT t.text,
         (qs.total_elapsed_time/1000) / qs.execution_count AS avg_elapsed_time,
         (qs.total_worker_time/1000) / qs.execution_count AS avg_cpu_time,
         ((qs.total_elapsed_time/1000) / qs.execution_count ) - ((qs.total_worker_time/1000) / qs.execution_count) AS avg_wait_time,
         qs.total_logical_reads / qs.execution_count AS avg_logical_reads,
         qs.total_logical_writes / qs.execution_count AS avg_writes,
         (qs.total_elapsed_time/1000) AS cumulative_elapsed_time_all_executions
    FROM sys.dm_exec_query_stats qs
         CROSS apply sys.Dm_exec_sql_text (sql_handle) t
    WHERE t.text like '<Your Query>%'
    -- Replace <Your Query> with your query or the beginning part of your query. The special chars like '[','_','%','^' in the query should be escaped.
    ORDER BY (qs.total_elapsed_time / qs.execution_count) DESC
    

    注意

    如果 avg_wait_time 显示负值,则为 并行查询

  • 如果可以在 SQL Server Management Studio (SSMS) 或 Azure Data Studio 中按需执行查询,请使用 SET STATISTICS TIMEONSET STATISTICS IOON 运行它。

    SET STATISTICS TIME ON
    SET STATISTICS IO ON
    <YourQuery>
    SET STATISTICS IO OFF
    SET STATISTICS TIME OFF
    

    然后,从 “消息”中,你将看到 CPU 时间、运行时间和逻辑读取,如下所示:

      Table 'tblTest'. Scan count 1, logical reads 3, physical reads 0, page server reads 0, read-ahead reads 0, page server read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob page server reads 0, lob read-ahead reads 0, lob page server read-ahead reads 0.
    
      SQL Server Execution Times:
        CPU time = 460 ms,  elapsed time = 470 ms.
    
  • 如果可以收集查询计划,检查执行计划属性中的数据。

    1. 运行包含 实际执行计划的 查询。

    2. 执行计划中选择最左侧的运算符。

    3. “属性”中,展开 “QueryTimeStats 属性”。

    4. 选中 “已用时间”“CpuTime”。

      “SQL Server执行计划属性”窗口的屏幕截图,其中展开了“QueryTimeStats”属性。

比较查询的运行时间和 CPU 时间,以确定这两个服务器的问题类型。

类型 1:CPU 绑定 (运行器)

如果 CPU 时间接近、等于或高于已用时间,则可以将其视为 CPU 绑定查询。 例如,如果运行时间为 3000 毫秒 (毫秒) 且 CPU 时间为 2900 毫秒,则表示大部分运行时间都花在 CPU 上。 然后,我们可以说这是一个受 CPU 限制的查询。

运行 (CPU 绑定) 查询的示例:

运行时间 (ms) cpu time (ms) 读取 (逻辑)
3200 3000 300000
1080 1000 20

逻辑读取(读取缓存中的数据/索引页)是SQL Server中 CPU 使用率的驱动因素。 在某些情况下,CPU 使用可能来自其他源:T-SQL 中的 while 循环 (或其他代码(如 XProcs 或 SQL CRL 对象) )。 表中的第二个示例演示了这样的方案,其中大部分 CPU 不是来自读取。

注意

如果 CPU 时间大于持续时间,则表示执行并行查询;多个线程同时使用 CPU。 有关详细信息,请参阅 并行查询 - 运行器或等待者

类型 2:等待瓶颈 (服务员)

如果已用时间明显大于 CPU 时间,则查询正在等待瓶颈。 运行时间包括在 CPU 上执行查询的时间 (CPU 时间) ,以及等待释放资源的时间 (等待时间) 。 例如,如果运行时间为 2000 毫秒,CPU 时间为 300 毫秒,则等待时间为 1700 毫秒 (2000 - 300 = 1700) 。 有关详细信息,请参阅 等待类型

等待查询的示例:

运行时间 (ms) cpu time (ms) 读取 (逻辑)
2000 300 28000
10080 700 80000

并行查询 - 运行器或服务员

并行查询使用的 CPU 时间可能比总持续时间多。 并行度的目标是允许多个线程同时运行查询的一部分。 在时钟时间的一秒内,查询可以通过执行 8 个并行线程来使用 8 秒的 CPU 时间。 因此,根据已用时间和 CPU 时间差确定受 CPU 限制或等待的查询变得具有挑战性。 但是,作为一般规则,请遵循上述两节中列出的原则。 摘要如下:

  • 如果已用时间远远大于 CPU 时间,则将其视为服务员。
  • 如果 CPU 时间远远大于已用时间,则将其视为运行器。

并行查询的示例:

运行时间 (ms) cpu time (ms) 读取 (逻辑)
1200 8100 850000
3080 12300 1500000

步骤 3:比较来自两个服务器的数据,找出方案,并排查问题

假设有两台名为 Server1 和 Server2 的计算机。 并且查询在 Server1 上的运行速度比 Server2 慢。 比较两个服务器的时间,然后按照以下部分中最匹配的方案的操作进行操作。

方案 1:Server1 上的查询占用的 CPU 时间更多,并且 Server1 上的逻辑读取数高于 Server2 上的逻辑读取数

如果 Server1 上的 CPU 时间远远大于 Server2 上的 CPU 时间,并且运行时间与两台服务器上的 CPU 时间密切相关,则不存在主要的等待或瓶颈。 Server1 上的 CPU 时间增加很可能是由逻辑读取的增加引起的。 逻辑读取的重大更改通常表示查询计划存在差异。 例如:

服务器 运行时间 (ms) cpu time (ms) 读取 (逻辑)
Server1 3100 3000 300000
Server2 1100 1000 90200

操作:检查执行计划和环境

  1. 比较两台服务器上的查询执行计划。 为此,请使用以下两种方法之一:
    • 直观地比较执行计划。 有关详细信息,请参阅 显示实际执行计划
    • 使用SQL Server Management Studio计划比较功能保存执行计划并进行比较
  2. 比较环境。 不同的环境可能会导致查询计划差异或 CPU 使用率的直接差异。 环境包括服务器版本、数据库或服务器配置设置、跟踪标志、CPU 计数或时钟速度,以及虚拟机与物理机。 有关详细信息 ,请参阅诊断查询计划差异

方案 2:查询是 Server1 上的服务员,但在 Server2 上不是

如果两台服务器上的查询的 CPU 时间相似,但 Server1 上的运行时间远远大于 Server2 上的运行时间,则 Server1 上的查询将花费更长的时间 等待瓶颈。 例如:

服务器 运行时间 (ms) cpu time (ms) 读取 (逻辑)
Server1 4500 1000 90200
Server2 1100 1000 90200
  • Server1 上的等待时间:4500 - 1000 = 3500 毫秒
  • Server2 上的等待时间:1100 - 1000 = 100 毫秒

操作:检查 Server1 上的等待类型

识别并消除 Server1 上的瓶颈。 等待的示例包括阻止 (锁等待) 、闩锁等待、磁盘 I/O 等待、网络等待和内存等待。 若要排查常见的瓶颈问题,请转到 诊断等待或瓶颈

方案 3:两个服务器上的查询都是等待者,但等待类型或时间不同

例如:

服务器 运行时间 (ms) cpu time (ms) 读取 (逻辑)
Server1 8000 1000 90200
Server2 3000 1000 90200
  • Server1 上的等待时间:8000 - 1000 = 7000 毫秒
  • Server2 上的等待时间:3000 - 1000 = 2000 毫秒

在这种情况下,两台服务器上的 CPU 时间相似,这表示查询计划可能相同。 如果查询不等待瓶颈,则查询在两台服务器上执行相同。 因此,持续时间差异来自不同的等待时间量。 例如,查询在 Server1 上的锁上等待 7000 毫秒,而 Server2 上的 I/O 等待 2000 毫秒。

操作:检查两台服务器上的等待类型

在每个服务器上单独解决每个瓶颈等待问题,并加快这两台服务器上的执行速度。 排查此问题需要耗费大量精力,因为需要消除两台服务器上的瓶颈并使性能可比。 若要排查常见的瓶颈问题,请转到 诊断等待或瓶颈

方案 4:Server1 上的查询使用的 CPU 时间比 Server2 上的多,但逻辑读取已关闭

例如:

服务器 运行时间 (ms) cpu time (ms) 读取 (逻辑)
Server1 3000 3000 90200
Server2 1000 1000 90200

如果数据符合以下条件:

  • Server1 上的 CPU 时间远大于 Server2 上的 CPU 时间。
  • 运行时间与每个服务器上的 CPU 时间紧密匹配,这表示没有等待。
  • 逻辑读取(通常是 CPU 时间的最高驱动程序)在两台服务器上相似。

然后,额外的 CPU 时间来自其他一些受 CPU 限制的活动。 此方案是所有方案中最罕见的。

原因:跟踪、UDF 和 CLR 集成

此问题可能是由以下原因引起的:

  • XEvents/SQL Server跟踪,尤其是对文本列进行筛选 (数据库名称、登录名、查询文本等) 。 如果在一台服务器上启用了跟踪,但另一台服务器上未启用跟踪,则这可能是产生差异的原因。
  • 用户定义的函数 (UDF) 或其他执行 CPU 绑定操作的 T-SQL 代码。 当 Server1 和 Server2 上的其他条件不同(例如数据大小、CPU 时钟速度或电源计划)不同时,这通常是原因。
  • SQL Server CLR 集成扩展存储过程 (可能驱动 CPU 但不执行逻辑读取的 XP) 。 DLL 的差异可能会导致不同的 CPU 时间。
  • (CPU 绑定SQL Server功能的差异,例如字符串操作代码) 。

操作:检查跟踪和查询

  1. 检查两台服务器上的跟踪是否存在以下情况:

    1. 如果在 Server1 上启用了任何跟踪,但在 Server2 上未启用任何跟踪。
    2. 如果启用了任何跟踪,请禁用跟踪并在 Server1 上再次运行查询。
    3. 如果这次查询运行得更快,请启用跟踪,但从中删除文本筛选器(如果有)。
  2. 检查查询是否使用执行字符串操作或对列表中的数据列 SELECT 执行广泛处理的 UDF。

  3. 检查查询是否包含循环、函数递归或嵌套。

诊断环境差异

检查以下问题并确定两个服务器之间的比较是否有效。

  • 这两个SQL Server实例的版本或内部版本是否相同?

    否则,可能存在一些导致差异的修复。 运行以下查询以获取这两个服务器上的版本信息:

    SELECT @@VERSION
    
  • 两台服务器上的物理内存量是否相似?

    如果一个服务器具有 64 GB 内存,而另一台服务器具有 256 GB 内存,则这是一个显著差异。 由于有更多的内存可用于缓存数据/索引页和查询计划,因此可以根据硬件资源可用性以不同的方式优化查询。

  • 这两台服务器上的 CPU 相关硬件配置是否相似? 例如:

    • CPU 数量在一台计算机上 (24 个 CPU 的计算机与另一台) 上的 96 个 CPU 之间变化。

    • 电源计划 - 均衡与高性能。

    • 虚拟机 (VM) 与物理 (裸机) 计算机。

    • Hyper-V 与 VMware - 配置差异。

    • 时钟速度差异 (较低的时钟速度与更高的时钟速度) 。 例如,2 GHz 与 3.5 GHz 可能会有所不同。 若要获取服务器上的时钟速度,请运行以下 PowerShell 命令:

      Get-CimInstance Win32_Processor | Select-Object -Expand MaxClockSpeed
      

    使用以下两种方法之一来测试服务器的 CPU 速度。 如果他们不产生可比的结果,则问题不在SQL Server。 这可能是电源计划差异、CPU 更少、VM 软件问题或时钟速度差异。

    • 在两台服务器上运行以下 PowerShell 脚本并比较输出。

      $bf = [System.DateTime]::Now
      for ($i = 0; $i -le 20000000; $i++) {}
      $af = [System.DateTime]::Now
      Write-Host ($af - $bf).Milliseconds " milliseconds"
      Write-Host ($af - $bf).Seconds " Seconds"
      
    • 在两个服务器上运行以下 Transact-SQL 代码,并比较输出。

      SET NOCOUNT ON 
      DECLARE @spins INT = 0
      DECLARE @start_time DATETIME = GETDATE(), @time_millisecond INT
      
      WHILE (@spins < 20000000)
      BEGIN
         SET @spins = @spins +1
      END
      
      SELECT @time_millisecond = DATEDIFF(millisecond, @start_time, getdate())
      
      SELECT @spins Spins, @time_millisecond Time_ms,  @spins / @time_millisecond Spins_Per_ms
      

诊断等待或瓶颈

若要优化等待瓶颈的查询,请确定等待时间以及瓶颈 (等待类型) 的位置。 确认 等待类型 后,请减少等待时间或完全消除等待。

若要计算近似等待时间,请从查询的运行时间中减去 CPU 时间 (辅助角色时间) 。 通常,CPU 时间是实际执行时间,查询生存期的剩余部分正在等待。

有关如何计算近似等待持续时间的示例:

运行时间 (ms) cpu time (ms) 等待时间 (ms)
3200 3000 200
7080 1000 6080

确定瓶颈或等待

  • 若要识别历史长时间等待查询 (例如, >总运行时间的 20% 是等待时间) ,请运行以下查询。 此查询使用自SQL Server开始以来缓存查询计划的性能统计信息。

    SELECT t.text,
             qs.total_elapsed_time / qs.execution_count
             AS avg_elapsed_time,
             qs.total_worker_time / qs.execution_count
             AS avg_cpu_time,
             (qs.total_elapsed_time - qs.total_worker_time) / qs.execution_count
             AS avg_wait_time,
             qs.total_logical_reads / qs.execution_count
             AS avg_logical_reads,
             qs.total_logical_writes / qs.execution_count
             AS avg_writes,
             qs.total_elapsed_time
             AS cumulative_elapsed_time
    FROM sys.dm_exec_query_stats qs
             CROSS apply sys.Dm_exec_sql_text (sql_handle) t
    WHERE (qs.total_elapsed_time - qs.total_worker_time) / qs.total_elapsed_time
             > 0.2
    ORDER BY qs.total_elapsed_time / qs.execution_count DESC
    
  • 若要确定等待时间超过 500 毫秒的当前正在执行的查询,请运行以下查询:

    SELECT r.session_id, r.wait_type, r.wait_time AS wait_time_ms
    FROM sys.dm_exec_requests r 
       JOIN sys.dm_exec_sessions s ON r.session_id = s.session_id 
    WHERE wait_time > 500
    AND is_user_process = 1
    
  • 如果可以收集查询计划,检查 SSMS 中的执行计划属性中的 WaitStats

    1. 运行包含 实际执行 计划的查询。
    2. 右键单击“ 执行计划 ”选项卡中最左侧的运算符
    3. 选择 “属性” ,然后选择 “WaitStats 属性”。
    4. 检查 WaitTimeMsWaitType
  • 如果熟悉 PSSDiag/SQLdiagSQL LogScout LightPerf/GeneralPerf 方案,请考虑使用其中任一方案来收集性能统计信息,并确定SQL Server实例上的等待查询。 可以导入收集的数据文件,并使用 SQL Nexus 分析性能数据。

帮助消除或减少等待的引用

每种等待类型的原因和解决方法各不相同。 没有一种常规方法可以解决所有等待类型。 下面是排查和解决常见等待类型问题的文章:

有关许多 Wait 类型的说明及其指示的内容,请参阅 “等待类型”中的表。

诊断查询计划差异

下面是查询计划差异的一些常见原因:

  • 数据大小或数据值差异

    是否在两个服务器上使用同一个数据库 - 使用相同的数据库备份? 与另一台服务器相比,数据是否已修改? 数据差异可能会导致不同的查询计划。 例如,将表 T1 (1000 行) 与表 T2 (2,000,000 行) 联接表 T1 (100 行) 与表 T2 (2,000,000 行) 不同。 操作的类型和速度 JOIN 可能大不相同。

  • 统计信息差异

    是否已在一个数据库上更新统计信息,而不是另一个数据库上的 统计信息 ? 是否已使用不同的采样率 (更新统计信息,例如 30% 与 100% 完全扫描) ? 确保使用相同的采样率更新两端的统计信息。

  • 数据库兼容性级别差异

    检查两台服务器之间数据库的兼容级别是否不同。 若要获取数据库兼容性级别,请运行以下查询:

    SELECT name, compatibility_level
    FROM sys.databases
    WHERE name = '<YourDatabase>'
    
  • 服务器版本/内部版本差异

    两个服务器之间的SQL Server版本或内部版本是否不同? 例如,一台服务器是否SQL Server版本 2014,另一台SQL Server版本 2016? 产品更改可能会导致查询计划的选择方式发生更改。 请确保比较SQL Server的相同版本和内部版本。

    SELECT ServerProperty('ProductVersion')
    
  • 基数估算器 (CE) 版本差异

    检查是否在数据库级别激活了旧基数估算器。 有关 CE 的详细信息,请参阅基数估计 (SQL Server)

    SELECT name, value, is_value_default
    FROM sys.database_scoped_configurations
    WHERE name = 'LEGACY_CARDINALITY_ESTIMATION'
    
  • 已启用/禁用优化器修补程序

    如果在一台服务器上启用了查询优化器修补程序,但在另一台服务器上禁用,则可以生成不同的查询计划。 有关详细信息,请参阅 SQL Server查询优化器修补程序跟踪标志 4199 服务模型

    若要获取查询优化器修补程序的状态,请运行以下查询:

    -- Check at server level for TF 4199
    DBCC TRACESTATUS (-1)
    -- Check at database level
    USE <YourDatabase>
    SELECT name, value, is_value_default 
    FROM sys.database_scoped_configurations
    WHERE name = 'QUERY_OPTIMIZER_HOTFIXES'
    
  • 跟踪标志差异

    某些跟踪标志会影响查询计划的选择。 检查一台服务器上是否启用了另一台服务器上未启用的跟踪标志。 在两个服务器上运行以下查询并比较结果:

    -- Check at server level for trace flags
    DBCC TRACESTATUS (-1)
    
  • 硬件差异 (CPU 计数、内存大小)

    若要获取硬件信息,请运行以下查询:

    SELECT cpu_count, physical_memory_kb/1024/1024 PhysicalMemory_GB 
    FROM sys.dm_os_sys_info
    
  • 根据查询优化器的硬件差异

    OptimizerHardwareDependentProperties检查查询计划的 ,查看硬件差异是否被视为对于不同的计划而言是显著的。

    WITH xmlnamespaces(DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
    SELECT
      txt.text,
      t.OptHardw.value('@EstimatedAvailableMemoryGrant', 'INT') AS EstimatedAvailableMemoryGrant , 
      t.OptHardw.value('@EstimatedPagesCached', 'INT') AS EstimatedPagesCached, 
      t.OptHardw.value('@EstimatedAvailableDegreeOfParallelism', 'INT') AS EstimatedAvailDegreeOfParallelism,
      t.OptHardw.value('@MaxCompileMemory', 'INT') AS MaxCompileMemory
    FROM sys.dm_exec_cached_plans AS cp
    CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
    CROSS APPLY qp.query_plan.nodes('//OptimizerHardwareDependentProperties') AS t(OptHardw)
    CROSS APPLY sys.dm_exec_sql_text (CP.plan_handle) txt
    WHERE text Like '%<Part of Your Query>%'
    
  • 优化器超时

    是否存在 优化器超时 问题? 如果执行的查询过于复杂,查询优化器可以停止评估计划选项。 停止时,它会选择当时可用成本最低的计划。 这可能会导致在一台服务器与另一台服务器上选择任意计划。

  • SET 选项

    某些 SET 选项会影响计划,例如 SET ARITHABORT。 有关详细信息,请参阅 SET 选项

  • 查询提示差异

    一个查询是否使用 查询提示, 另一个查询不使用? 手动检查查询文本,以确定是否存在查询提示。

  • 参数敏感计划 (参数探查问题)

    是否使用完全相同的参数值测试查询? 如果没有,则可以从该处开始。 计划之前是否已基于不同的参数值在一台服务器上编译? 使用 RECOMPILE 查询提示测试这两个查询,以确保不会进行计划重用。 有关详细信息,请参阅 调查并解决参数敏感问题

  • 不同的数据库选项/范围配置设置

    这两个服务器上是否使用相同的数据库选项或作用域内配置设置? 某些数据库选项可能会影响计划选择。 例如,数据库兼容性、旧版 CE 与默认 CE 以及参数探查。 从一台服务器运行以下查询,以比较两台服务器上使用的数据库选项:

    -- On Server1 add a linked server to Server2 
    EXEC master.dbo.sp_addlinkedserver @server = N'Server2', @srvproduct=N'SQL Server'
    
    -- Run a join between the two servers to compare settings side by side
    SELECT 
       s1.name AS srv1_config_name, 
       s2.name AS srv2_config_name,
       s1.value_in_use AS srv1_value_in_use, 
       s2.value_in_use AS srv2_value_in_use, 
       Variance = CASE WHEN ISNULL(s1.value_in_use, '##') != ISNULL(s2.value_in_use,'##') THEN 'Different' ELSE '' END
    FROM sys.configurations s1 
    FULL OUTER JOIN [server2].master.sys.configurations s2 ON s1.name = s2.name
    
    
    SELECT 
       s1.name AS srv1_config_name,
       s2.name AS srv2_config_name,
       s1.value srv1_value_in_use,
       s2.value srv2_value_in_use,
       s1.is_value_default,
       s2.is_value_default,
       Variance = CASE WHEN ISNULL(s1.value, '##') != ISNULL(s2.value, '##') THEN 'Different' ELSE '' END
    FROM sys.database_scoped_configurations s1
    FULL OUTER JOIN [server2].master.sys.database_scoped_configurations s2 ON s1.name = s2.name
    
  • 计划指南

    是否有任何计划指南用于一台服务器上的查询,但未用于另一台服务器上的查询? 运行以下查询以建立差异:

    SELECT * FROM sys.plan_guides