具有包括所有列的非聚集索引的临时表

bas*_*n22 6 sql-server nonclustered-index sql-server-2014 temporary-tables

背景:每天都会创建几个临时表,用于计算各种不同的聚合值。它们都包含相同的唯一标识符(我将在其上创建 PRIMARY KEY 的字段)。每个表大约有 10 万行,只有 2-8 列。然后在临时表上完成几个 JOIN 和 UNION ALL。使用所有临时表中的每个字段。此外,大多数时候每一行都被使用——最坏的情况是,一半的行在某处使用,另一半在其他地方使用。查询完成后,结果将保存在磁盘上,以便个人可以访问当天剩余时间的数据。

问题:以下哪种方法应该是最好的最快的:

• 临时表上没有索引。

• 在所有临时表上都有一个聚集索引(通过唯一标识符上的 PRIMARY KEY 声明)。

• 在唯一标识符上有一个非聚集索引,同时包括所有临时表上的其余列。

• 最后两个要点放在一起。

想法:同时运行所有四个选项时,每个选项的查询成本为 25%(相对于批处理);然而,当第一个要点运行时,执行计划(在 SELECT 查询上)指出我应该在唯一标识符上创建一个非聚集索引,同时包括每个临时表的剩余列。

我对此有些困惑。如果我有效地使用所有临时表中的每个字段和行,为什么会建议这样做?堆或聚集索引不是更好吗?

对我来说,堆可以最大限度地减少创建索引和排序数据的开销;因为我基本上需要每一行,所以进行表扫描没有坏处。

另一方面,聚集索引应该改进通过在其他两个临时表上的 JOIN 创建的临时表,并改进依赖于 JOIN 和 UNION ALL 的最终 SELECT 查询。

相关说明:在一个有 400 列和 70M 行的大表上——不要问我为什么我们有一个荒谬的列——当使用不相关的非聚集索引时,只获取 PRIMARY KEY 的查询快 50 倍聚集索引。

如果有人有任何见解,我将不胜感激。

Joe*_*ish 6

总结一下(并包括评论中的一些信息),您有一个每天早上运行一次的流程,该流程会填充多个 100k 行临时表,每个表有 2 - 8 列。这是一个广泛的问题,但我最初的反应是在所有表上创建主键。如果性能足够好,那么我会很满意。如果性能不够好,我会进一步调查以找到改进代码的方法。删除主键在技术上是一种选择,但总的来说,我希望通过更改填充临时表的查询来提高性能。

堆是没有聚集索引的表。请注意,主键不需要以与聚集索引相同的方式定义。实际上,您可以使用主键定义堆。据我所知,这不是一件很常见的事情。为工作负载使用堆有几个优点:

  1. 插入数据不会导致排序。如果将 100k 行插入带有聚集索引的表中,则可能需要根据填充表的查询对数据进行排序。所有列或仅聚类键都可以包含在排序中。
  2. 从 SQL Server 2014 开始,如果您使用SELECT INTO语法,插入可以并行运行。请注意,查询优化器可能会选择不使用并行插入,这取决于多种原因,包括数据的估计大小和MAXDOP设置。

一般来说,我不会担心 100k 行,特别是如果这个过程在早上没有别的东西的情况下运行。对于如此少量的数据,并行插入不太可能有很大帮助。

对此工作负载使用堆有一些缺点:

  1. 没有数据完整性。您的进程中可能存在错误或损坏的数据,这些数据可能在早期使用临时表上的主键被捕获。
  2. 数据未排序,因此任何需要排序数据的查询计划运算符(例如合并连接运算符或插入聚簇表)都需要显式排序。假设您在三个查询中引用了一个表,并且查询优化器对数据进行了所有 3 次排序。为什么不在开始时对磁盘上的数据进行排序以避免查询计划中的这三种排序?

关于这些点很难说更多,因为我对你的过程一无所知。您对临时表的查询可能会受益于 aMERGE JOIN但我怀疑它会对您的数量产生很大的影响。

现在让我们谈谈带有聚集索引的表。如前所述,聚集索引不需要匹配主键,但我假设最常见的默认值是它们都是相同的。当您将PRIMARY KEY内联定义为表定义的一部分时,这就是您最终得到的结果。

具有聚簇索引的表对于您的工作负载有一些优势:

  1. 数据的完整性!
  2. 受益于排序的查询计划操作员可能已经按正确的顺序对数据进行了排序。例如,这使得合并连接更具吸引力,尤其是当您从相关表中获取所有数据时。
  3. 如果查询优化器知道某些列是唯一的,它可能能够选择更有效的计划。这方面的一个例子是 SQL Server 可能知道连接不是多对多,这可以导致更准确的基数估计和连接运算符的成本计算。

具有聚簇索引的表对于您的工作负载有一些缺点:

  1. 在任何版本的 SQL Server 中都不提供对临时表的并行插入。
  2. 填充表可能需要显式排序。

优点和缺点大多与堆表完全相反,这不足为奇。

第三个和第四个选项真的不值得考虑。您可以创建一个包含所有列的索引,但它会复制数据。只需以正确的方式定义主键,您就不应该使用包含所有列的附加非聚集索引。请注意,SQL Server Management Studio 不建议您在表上创建聚集索引。

聚集索引/主键和非聚集索引之间的一个区别在于,聚集索引/主键不允许键列中存在 NULL 值,并自动强制执行唯一性约束。当然,您可以创建具有相同限制的非聚集索引。

最终,您所能做的就是使用不同的选项测试您的工作负载。所以我们不能肯定地说哪种方法最快。使用一组数据和查询,堆方法可能是最快的。对于不同的数据集和查询,聚簇表方法可能是最快的。不要低估数据完整性的价值,但您可能有其他方法来强制执行数据完整性。