【问题标题】:Indexing a single-use temporary table索引一次性临时表
【发布时间】:2018-12-07 00:28:52
【问题描述】:

一位同事在使用 Microsoft SQL Server 的企业工作。他们的团队创建了每天执行的存储过程以创建数据提取。基础表很大(有些有数十亿行),因此大多数存储过程的设计是首先它们仅将这些巨大表的相关行提取到临时表中,然后将临时表相互连接并与其他较小的表以创建最终提取。类似的东西:

SELECT COL1, COL2, COL3
INTO #TABLE1
FROM HUGETABLE1
WHERE COL4 IN ('foo', 'bar');

SELECT COL1, COL102, COL103
INTO #TABLE2
FROM HUGETABLE2
WHERE COL14 = 'blah';

SELECT COL1, COL103, COL306
FROM #TABLE1 AS T1
JOIN #TABLE2 AS T2
ON T1.COL1 = T2.COL1
LEFT JOIN SMALLTABLE AS ST
ON T1.COL3 = ST.COL3
ORDER BY T1.COL1;

一般来说,临时表在创建后不会被修改(因此没有后续的 ALTER、UPDATE 或 INSERT 操作)。出于讨论的目的,我们假设临时表仅在以后使用一次(因此只有一个 SELECT 查询会依赖它们)。

这里的问题是:在这些临时表创建之后和在后续查询中使用它们之前对它们进行索引是一个好主意吗?

我的同事认为创建索引将使联接和排序操作更快。但是,我相信总时间会更长,因为创建索引需要时间。换句话说,我假设除了边缘情况(比如临时表本身非常大,或者最终的 SELECT 查询非常复杂),SQL Server 将使用它在临时表上的统计信息来优化最终查询,这样做会有效地索引临时表,因为它认为合适。

换句话说,我习惯于认为创建索引只有在您知道该表经常使用时才有用;存储过程完成后删除的一次性临时表不值得索引。

我们对 SQL Server 优化器的了解都不够,无法知道我们在哪些方面是对的或错的。您能否帮助我们更好地理解我们的哪些假设更接近事实?

【问题讨论】:

标签: sql-server stored-procedures indexing temp-tables


【解决方案1】:

如果您每天提取数十亿行的数据,我建议您使用临时表而不是临时表。这将使用 tempdb 将您的数据提取与其他资源隔离。

这里的问题是:在这些临时表创建之后和在后续查询中使用它们之前对它们进行索引是一个好主意吗?

将数据加载到临时表后创建索引。这将消除碎片并创建统计信息。

优化器将使用统计信息来生成最佳计划。因此,如果您没有统计信息,它可能会极大地影响您的查询性能,尤其是对于大型数据集。

下例查询临时表创建索引前后对比:

/* Create index after data load into temp table -- stats is created */
CREATE TABLE #temp ( [text] varchar(50), [num] int);
INSERT INTO #temp([text], [num]) VALUES ('aaa', 1), ('bbb', 2) , ('ccc',3);
CREATE UNIQUE CLUSTERED INDEX [IX_num] ON #temp (num);
DBCC SHOW_STATISTICS ('tempdb..#temp', 'IX_num');

/* Create index before data load into temp table -- stats is not created */
CREATE TABLE #temp_nostats ( [text] varchar(50), [num] int);
CREATE UNIQUE CLUSTERED INDEX [IX_num] ON #temp_nostats (num);
INSERT INTO #temp_nostats([text], [num]) VALUES ('aaa', 1), ('bbb', 2) , ('ccc',3);
DBCC SHOW_STATISTICS ('tempdb..#temp_nostats', 'IX_num');

您需要测试索引是否对您有帮助。您需要平衡您可以拥有多少索引,因为如果您拥有太多索引,它也会影响您的性能。

【讨论】:

  • 感谢 dco,但您回答了一个不同的问题:应该在加载数据之前还是之后创建索引。在这里,问题是是否应该创建一个索引。换句话说,如果我们不创建索引,SQL Server 不首先会收集一些统计信息作为运行最终 SELECT 查询的第一步吗?
  • @Merik 这取决于。您需要测试该索引是否对您有帮助。如果您的表上没有索引,SQL Server 将扫描整个表。优化器依靠统计数据来计算表或索引的估计成本“基数”。
  • 但是如果我确实创建了索引,SQL Server 在创建索引时会扫描整个表,不是吗?因此,这意味着无法以某种方式避免表扫描。
  • 你能提供一些链接来支持它吗?就我对索引的理解而言,如果不扫描所有数据,就不可能创建索引。
  • @Merik 我上次忽略了执行计划。当数据存在时它会扫描表。如果您想在创建临时表之前或之后创建索引,您的里程可能会有所不同。大多数时候,我会最后创建索引以生成统计信息(它有助于优化器生成最佳计划)。如前所述,您需要测试它是否有助于您创建索引。
【解决方案2】:

您的朋友可能是对的,因为即使要在单个查询中使用表,但没有看到查询(即使我们看到了,我们仍然不知道它的执行计划是什么样的) 我们不知道 SQL Server 需要多少次在每个表的各个列中查找数据以进行连接、排序等。

但是,我们永远无法确定,除非它实际上以两种方式完成并测量和比较结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-15
    • 2011-12-01
    • 2021-07-18
    • 2018-01-04
    • 1970-01-01
    • 2011-03-17
    • 1970-01-01
    相关资源
    最近更新 更多