截断表和更新统计信息

5

在调用Truncate table之后,我们需要更新表统计信息吗?还是它会自动更新?

问:在截断表后,我们需要调用"UPDATE STATISTICS"吗?

3个回答

7
这取决于管理员如何配置统计信息。通常的方法是每周维护一次。然后,你可以等待作业运行,或手动更新统计信息。
还有一个选项是自动更新统计信息
当打开自动更新统计信息选项AUTO_UPDATE_STATISTICS时,查询优化器确定何时统计信息可能已过期,然后在查询使用它们时更新它们。
这可能会在截断后重新计算。
你可以像这样打开自动更新:
ALTER DATABASE AdventureWorks
    SET AUTO_UPDATE_STATISTICS ON;

手动更新统计数据,请点击此处

UPDATE STATISTICS Sales.SalesOrderDetail

要验证您的统计数据的当前年龄,请运行:

SELECT 
    object_name = Object_Name(ind.object_id),
    IndexName = ind.name,
    StatisticsDate = STATS_DATE(ind.object_id, ind.index_id)
FROM SYS.INDEXES ind
order by STATS_DATE(ind.object_id, ind.index_id) desc

(+1) 谢谢Andomar。保持自动更新统计信息有什么缺点吗...还是我们可以默认保持它?我怎么知道它是否在我的系统上?(抱歉,我不是管理员,但我有所有的访问权限) - noob.spt
@noob2487:自动更新统计信息可能会影响性能。例如,如果您在更新大表时有很高的负载,则更新统计信息会增加更多负载。不过,在99%的情况下,启用自动更新是一个绝佳的选择。 - Andomar
明白了。在我的情况下,我需要加载大约10万条记录,这些记录在清空这些表后立即加载。因此最好禁用AutoUpdate。有没有办法找出它是否已启用或禁用? - noob.spt

7
统计数据直到再次需要时才会自动更新,也就是说,TRUNCATE 不会自动更新它们。因此答案是“否”。
最初的答案是“是”,因为它不是 TRUNCATE 的一部分自动进行的。这取决于您如何理解问题 :-)
请记住,当查询需要时(例如行数更改),统计数据会自动更新。来自 BOL 中的“索引统计信息

每当查询执行计划中使用的统计信息未通过当前统计信息的测试时,都会启动统计信息更新

一种验证方法是使用 STATS_DATE...
SELECT
   name AS index_name, 
   STATS_DATE(object_id, index_id)
FROM
   sys.indexes 
WHERE
   object_id = OBJECT_ID('MyTruncatedTable')


编辑:我想确保一下 :-)

你会发现统计数据仅由SELECT语句更新,而不是INSERT、DELETE或TRUNCATE。

IF OBJECT_ID('dbo.foo') IS NOT NULL DROP TABLE dbo.foo
CREATE TABLE dbo.foo (
    bar int NOT NULL IDENTITY (1, 1) PRIMARY KEY,
    thing int NOT NULL
)
CREATE INDEX IX_thing ON dbo.foo (thing)

INSERT dbo.foo (thing) SELECT c1.object_id FROM sys.columns c1, sys.columns c2
SELECT
   name AS index_name, 
   STATS_DATE(object_id, index_id) AS AfterLoad
FROM sys.indexes WHERE object_id = OBJECT_ID('dbo.foo')

SELECT DISTINCT thing FROM dbo.foo ORDER BY thing DESC
SELECT
   name AS index_name, 
   STATS_DATE(object_id, index_id) AS AfterFirstQuery
FROM sys.indexes WHERE object_id = OBJECT_ID('dbo.foo')

DELETE TOP (50000) dbo.foo
SELECT
   name AS index_name, 
   STATS_DATE(object_id, index_id) AS AfterDelete
FROM sys.indexes WHERE object_id = OBJECT_ID('dbo.foo')

SELECT DISTINCT thing FROM dbo.foo ORDER BY thing DESC
SELECT
   name AS index_name, 
   STATS_DATE(object_id, index_id) AS After2ndQuery
FROM sys.indexes WHERE object_id = OBJECT_ID('dbo.foo')

TRUNCATE TABLE dbo.foo
SELECT
   name AS index_name, 
   STATS_DATE(object_id, index_id) AS AfterTruncate
FROM sys.indexes WHERE object_id = OBJECT_ID('dbo.foo')

SELECT DISTINCT thing FROM dbo.foo ORDER BY thing DESC
SELECT
   name AS index_name, 
   STATS_DATE(object_id, index_id) AS After3rdQuery
FROM sys.indexes WHERE object_id = OBJECT_ID('dbo.foo')

@gbn:我按照原样执行了这个查询。它已经运行了12分钟了。它被卡在某个锁里了吗?应该需要多长时间才能完成?我猜现在需要杀掉它了。 - noob.spt
抱歉,在我的虚拟机SQL 2008实例上运行了10秒。不过我的数据库是空的,但给出了238,000行数据,所以可能只是行数太多了... - gbn
不需要,引擎会在下一个SELECT语句中意识到这一点。 - gbn
你能稍微修改一下你的回答来适应问题吗?开头的“Yes”看起来不太对。请更新一下,以便我把它标记为答案。谢谢。 - noob.spt
1
非常感谢。只是想确保将来参考这个问题的人不会感到困惑 :) - noob.spt
显示剩余3条评论

2

由于你没有数据,因此在插入数据之前它是毫无意义的,然后你会寻求更新统计数据。

不要忘记你可以自动更新统计数据,也可以每日/每周运行和更新统计作业...

如果仍然存在重大问题,请截断表格,然后更新表格上的统计数据。


(+1) 是的,那是正确的。由于没有数据,统计数据将毫无意义,或者可以说它们将是“不正确”的。因此,我担心一旦开始向表中插入数据,这些不正确的统计数据会如何影响性能。 - noob.spt
引擎会查看统计数据进行规划。只有当它尝试运行并发现没有数据时,它才会认为精心制定的计划“无意义”。因此,统计数据对于空表非常重要:它们告诉优化器该表为空。 - Remus Rusanu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接