【问题标题】:SQL Server Performance With Large Query大型查询的 SQL Server 性能
【发布时间】:2013-11-03 19:49:03
【问题描述】:

大家好,我对一些报告有几个查询,其中每个查询都从 35 个以上的表中提取数据。每个表都有近 10 万条记录。例如,所有查询都是 Union ALL

;With CTE
AS
(
Select col1, col2, col3 FROM Table1 WHERE Some_Condition
UNION ALL 
Select col1, col2, col3 FROM Table2 WHERE Some_Condition
UNION ALL 
Select col1, col2, col3 FROM Table3 WHERE Some_Condition
UNION ALL 
Select col1, col2, col3 FROM Table4 WHERE Some_Condition
.
.
. And so on 
)
SELECT col1, col2, col3 FROM CTE
ORDER BY col3 DESC

到目前为止,我只在 Dev Server 上测试了这个查询,我可以看到它需要一些时间才能得到结果。所有这些 35+ 表都彼此不相关,这是我能想到的在结果集中获取所有所需数据的唯一方法。

  1. 有没有更好的方法来进行这种查询?

  2. 如果这是进行此类查询的唯一方法,我该如何 通过进行任何更改来提高此查询的性能,如果 可能的??

我的看法
我不介意在这份报告中有一些脏读。我正在考虑使用 查询提示 with nolockTransaction Isolation Level 设置为 READ UNCOMMITED

这些有帮助吗???

编辑
每个表都有 5-10 位列和每个位列对应的日期列,我对每个 SELECT 语句的条件类似于

WHERE BitColumn = 1 AND DateColumn IS NULL 

同行建议

过滤索引

CREATE NONCLUSTERED INDEX IX_Table_Column
ON TableName(BitColumn)
WHERE BitColum = 1

包含列的过滤索引

CREATE NONCLUSTERED INDEX fIX_IX_Table_Column
ON TableName(BitColumn)
INCLUDE (DateColumn)
WHERE DateColumn IS NULL

这是最好的方法吗?或者有什么建议吗???

【问题讨论】:

  • 为什么需要这种查询?
  • 您确定这些表不相关吗?很难理解如何合并完全不相关的数据。
  • 您查看过查询的执行计划吗? 35+ 表中的每一个都有适当的索引吗?
  • 从不同的表格中收集有关缺失数据的信息并提示案例处理人员获取该信息并填写表格上的字段,因为他们有一定的时间限制来获取每个客户的所有信息。此查询检查所有客户的 35 多个表单中的多个字段中的缺失数据。
  • 对我来说听起来像是次优设计,但如果你真的需要检查 35 个表中的一个条件,我真的不知道你会怎么做。条件总是一样吗?也许您可以创建一个与谓词匹配的过滤索引。否则,耸耸肩Also, please don't ping people on one question to go look at another question。对于本网站的大多数用户来说,这有点破坏了整个工作流程。

标签: sql-server sql-server-2008 tsql sql-server-2008-r2 query-performance


【解决方案1】:

有很多事情可以让它更快。 如果我假设您需要执行这些 UNION,那么您可以通过以下方式加快查询速度:

  1. 缓存结果,例如,
    • 你能从整个语句创建一个索引视图吗?还是有很多不同的 WHERE 条件,所以会有很多索引视图?但要知道,这会减慢对这些表的修改(INSERT 等)
    • 你能以不同的方式缓存它吗?也许在中间层?
    • 可以提前重新计算吗?
  2. 制作覆盖索引。前导列是 WHERE 形式的列,然后是查询中的所有其他列作为包含列
    • 请注意,也可以过滤覆盖索引,但如果查询中的 WHERE 包含变量/参数并且它们可能具有过滤索引未覆盖的值(即行不包括在内)
  3. ORDER BY 将导致排序
    • 如果你可以缓存它,那很好 - 不需要排序(它是缓存排序的)
    • 否则,排序受 CPU 限制(如果不在内存中,则受 I/O 限制)。为了加快速度,您是否使用快速整理?最慢排序和最快排序之间的性能差异甚至可以达到 3 倍。例如,SQL_EBCDIC280_CP1_CS_AS、SQL_Latin1_General_CP1251_CS_AS、SQL_Latin1_General_CP1_CI_AS 是最快的排序规则之一。但是,如果我不知道您需要的排序规则特征,就很难提出建议
  4. 网络
    • 执行 SELECT 的连接的“网络数据包大小”应该是可能的最大值 - 如果结果集(行数)很大,则为 32,767 字节。这可以在客户端设置,例如,如果您在连接字符串中使用 .NET 和 SqlConnection。这将最大限度地减少从 SQL Server 发送数据时的 CPU 开销,并提高客户端和服务器端的性能。如果网络是瓶颈,这甚至可以将性能提高几十个百分点
    • 如果客户端在 SQL Server 上,则使用共享内存端点;否则使用 TCP/IP 以获得最佳性能
  5. 一般的东西
    • 如您所说,使用未提交的隔离级别会提高性能

...

除了重写查询等之外,您可能无法进行更改,但以防万一,添加更多内存以防现在不够用,或者在内存功能中使用 SQL Server 2014 :-),...肯定有帮助。

有太多的事情可以调整,但如果问题不是很具体,就很难指出关键的事情。

希望对你有所帮助

【讨论】:

    【解决方案2】:

    好吧,您没有提供任何执行的任何统计信息或示例运行时间,因此无法猜测什么是慢的,它是否真的很慢。结果集中有多少数据?它可能只是检索 100K 行,因为结果只是花时间。如果 10000 行的结果集需要 5 分钟,那么肯定可以查看一些内容。因此,如果您有示例查询、结果中的行数以及在不同 where 条件下执行几次所需的时间,请发布。它将帮助我们比较结果。

    顺便说一句,不要使用 CTE,只需使用常规的内部和外部查询选择。确保 Temp DB 配置正确。 LDF 和 MDF 未默认配置为增加 10%。通过某些尝试和错误,您将了解为范围查询的真实性增加了多少日志和临时数据库,并基于此设置临时数据库的 MDF 和 LDF 的初始和增量大小。对于 Covered 过滤器索引,包含列应该是 col1、col2 和 co3,而不是列 Date,除非 Date 也在选择列表中。

    原始 35 个表中的数据多久更新一次?如果每天最多一次,或者如果他们几乎同时获得更新,那么索引视图可能是一个可能的解决方案。但是,如果原始表每天更新一次以上,或者它们随时都得到更新,而且它们不在同一行,那么就不要考虑索引视图。

    如果磁盘空间不是问题,作为最后的手段,请尝试在每个 35 表上使用触发器测试性能。按照您对此选择查询的期望,创建新表来保存最终结果。在每个 35 个表上创建插入/更新/删除触发器,在其中检查触发器内的条件,如果是,则仅将相同的插入/更新/删除复制到新表。是的,您将需要新表中的一列来标识哪些数据来自哪个表。因为 Date 是 Null-Able 列,所以您无法充分利用该列上的 Index,因为“大多数情况下您正在寻找 WHERE Date is NULL”。 在新表中,您总是做的唯一查询是 Date 为 NULL,然后甚至不必费心创建该列只需创建 BIT 列和其他 col1、col2、col3 等...如果您给出查询的真实示例并解释实际表,其他细节可以稍后锻炼。

    【讨论】:

      【解决方案3】:

      查询提示或隔离级别只会在发生任何阻塞时为您提供帮助。 如果您不介意脏读并且在执行期间有锁,那可能是个好主意。

      关键问题是有多少数据适合您需要使用的 Where 子句(WHERE BitColumn = 1 AND DateColumn IS NULL) 如果按它过滤的子集与总行数相比较小,则在 BitColum 和 DateColumn 两列(包括 select 子句中的列)上使用索引,以避免查询计划中的“页面查找”操作。

      CREATE NONCLUSTERED INDEX IX_[Choose an IndexName]
      ON TableName(BitColumn, DateColumn)
      INCLUDE (col1, col2, col3)
      

      当然,覆盖过滤索引所需的空间取决于所涉及字段的数据类型以及满足 WHERE BitColumn = 1 AND DateColumn IS NULL 的行数。

      之后我建议使用 View 而不是 CTE:

      CREATE VIEW [Choose a ViewName]
      AS
      (
      Select col1, col2, col3 FROM Table1 WHERE Some_Condition
      UNION ALL 
      Select col1, col2, col3 FROM Table2 WHERE Some_Condition
      .
      .
      .
      )
      

      通过这样做,您的查询计划应该看起来像 35 个小型索引扫描,但如果大多数数据满足索引的 where 子句,则性能将类似于扫描 35 个源表,并且解决方案不会'不值得。

      但是您说“每个表都有 5-10 位列和一个对应的日期列..”然后我认为为每个位列创建索引不是一个好主意。 如果您需要使用不同的 BitColums 和不同的 DateColums 进行过滤,请在表中使用计算列:

      ALTER TABLE Table1 ADD ComputedFilterFlag AS
      CAST(
          CASE WHEN BitColum1 = 1 AND DateColumn1 IS NULL THEN 1 ELSE 0 END +
          CASE WHEN BitColum2 = 1 AND DateColumn2 IS NULL THEN 2 ELSE 0 END +
          CASE WHEN BitColum3 = 1 AND DateColumn3 IS NULL THEN 4 ELSE 0 END
      AS tinyint)
      

      我建议您将值 2^(X-1) 用于 conditionX(BitColumnX=1 和 DateColumnX IS NOT NULL)。它将允许您使用该标准的任意组合进行过滤。 通过使用值 3,您可以找到完成以下条件的所有行:Bit1、Date1 和 Bit2、Date2 条件。任何条件组合都有其对应的 ComputedFilterFlag 值,因为 ComputedFilterFlag 充当条件的位图。 如果你的过滤器少于 8 个,你应该使用 tinyint 来节省索引空间并减少所需的 IO 操作。

      然后在 ComputedFilterFlag 列上使用索引:

      CREATE NONCLUSTERED INDEX IX_[Choose an IndexName]
      ON TableName(ComputedFilterFlag)
      INCLUDE (col1, col2, col3)
      

      并创建视图:

      CREATE VIEW [Choose a ViewName]
      AS
      (
      Select col1, col2, col3 FROM Table1 WHERE ComputedFilterFlag IN [Choose the Target Filter Value set]--(1, 3, 5, 7)
      UNION ALL 
      Select col1, col2, col3 FROM Table2 WHERE ComputedFilterFlag IN [Choose the Target Filter Value set]--(1, 3, 5, 7)
      .
      .
      .
      )
      

      通过这样做,您的索引涵盖了所有条件,并且您的查询计划应该看起来像 35 次小型索引搜索。

      但这是一个棘手的解决方案,可能是在您的表架构中重构可以产生更简单和更快的结果。

      【讨论】:

        【解决方案4】:

        您永远无法从多个表的 union all 查询中获得实时结果,但我可以告诉您我是如何从类似情况中获得一点速度的。希望这会对您有所帮助。

        您实际上可以通过一点编码和独创性一次运行所有这些。

        您创建一个全局临时表而不是一个公用表表达式,并且不要在全局临时表上放置任何键,这只会减慢速度。然后,您启动所有插入到全局临时表中的单个查询。我已经手动完成了一百次左右,它比联合查询更快,因为您可以在每个 cpu 核心上运行一个查询。棘手的部分是确定各个查询何时完成您自己对该部分的机制,因此我手动执行这些操作。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-04-19
          • 2013-07-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-09-30
          • 1970-01-01
          相关资源
          最近更新 更多