【问题标题】:SQL : Weird query performance after "select into" a new tableSQL:“选择”新表后的奇怪查询性能
【发布时间】:2012-12-27 00:09:14
【问题描述】:

我遇到了如下奇怪的情况:

我在名为“Table1”的数据库中有一个巨大的表。 然后,我使用以下代码复制完全相同的表。

Select * into Table2 from Table1

在那之后, 我发现查询性能差异很大。

Select count (distinct ID) from Table1 大约需要 2 分钟才能完成。 (旧表)

与此同时, Select count (distinct ID) from Table2 只需大约 10 秒即可完成(新表)

顺便说一句,我发现数据在“选择进入”之后已经在新表中重新排序。 此外,在“选择”新表之前,Table1(旧表)中添加了一列 (即 Alter a table ,将 col1 添加为 col2。)

那么,这是怎么发生的呢?

(注意:问题的原始版本说新表是慢表。这是一个错误。另外,它没有提到对 Table1 的数据操作)


对更多信息请求的回应

这是 Sebastian 代码的结果。

SELECT  QUOTENAME(OBJECT_SCHEMA_NAME(t.object_id)) + '.' + QUOTENAME(t.name) tbl,
        s.name stats_name,
        cols.cols,
        t.create_date table_date,
        STATS_DATE(s.object_id, s.stats_id) AS statistics_date,
        s.auto_created,
        s.user_created,
        s.no_recompute,
        s.has_filter,
        s.filter_definition
FROM    sys.tables t
LEFT OUTER JOIN sys.stats s
        ON s.object_id = t.object_id
OUTER APPLY (
              SELECT  STUFF((SELECT ',' + c.name
                             FROM   sys.stats_columns sc
                             JOIN   sys.columns c
                                    ON sc.column_id = c.column_id
                                       AND sc.object_id = c.object_id
                             WHERE  sc.object_id = s.object_id
                                    AND sc.stats_id = s.stats_id
                             ORDER BY sc.stats_column_id
                      FOR   XML PATH(''),
                                TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '') cols
            ) cols
--Update Table Name(s) here:      
WHERE   t.OBJECT_ID IN ( OBJECT_ID('[Sales].[SpecialOffer]'),
        OBJECT_ID('[Sales].[SalesOrderDetail]') );

SELECT  name,
        compatibility_level,
        is_auto_close_on,
        is_auto_shrink_on,
        state_desc,
        is_auto_create_stats_on,
        is_auto_update_stats_on,
        is_auto_update_stats_async_on
FROM sys.databases
WHERE database_id = DB_ID();

实际上,我将新表复制到另一个数据库。 而表名其实就是ID2000

上图参考“表1”(数据库1) 下图参考“Table2”(数据库2)


好吧,由于 XML 代码太长,这里是遵循 Hamlet 建议的替代打印输出。 我用 SET SHOWPLAN_ALL ON GO 而不是粘贴所有 XML 代码。希望对你有帮助。

红色代表“表1”计划,黑色代表“表2”。 图片中的文字有点小,但是通过增加这个页面大小来放大它会简单地放大它。

非常感谢!!


SELECT * FROM sys.dm_db_index_physical_stats(db_id(),object_id('YourTable'),NULL,NULL,'Detaile‌​d') 的结果。

确实,两张表之间存在巨大差异。 同理,红色为“表1”,另一种为“表2”

这个问题很烦人,让我发疯,因为我一直在问自己是否应该重建所有表。 :(

其实很奇怪,注意record_count是不同的。 但是,当我重新检查时 select COUNT (ID) from id2000 ,(即计算此表上的总数据行数) 两个结果都是2324798,也就是Table_2的record_count

此外,“Table2”是由“select * into”语句创建的,我想两者应该是相同的,但现在我很困惑。

上表是来自 Sebastian 代码的代码(Running stat)的结果


SELECT * FROM sys.dm_db_index_physical_stats(db_id(),object_id('YourTable'),NULL,NULL,'Detaile‌​d') 的结果。

确实,两张表之间存在巨大差异。 同理,红色为“表1”,另一种为“表2”

这个问题很烦人,让我发疯,因为我一直在问自己是否应该重建所有表。 :(

其实很奇怪,注意record_count是不同的。 但是,当我重新检查时 select COUNT (ID) from id2000 ,(即计算此表上的总数据行) 两个结果都是2324798,也就是Table_2的record_count

此外,“Table2”是由“select * into”语句创建的,我想两者应该是相同的,但现在我很困惑。

【问题讨论】:

  • 你在 table1 中有索引,而它们在 table2 中不存在?
  • 我检查了,两个表上都没有索引。 :(
  • table1有什么限制吗?
  • Nop :( ,Table1 只是一个非常简单的表,从 txt 文件中以常规方式导入,无需执行任何进一步操作。我唯一修改的是添加一个列ALTER TABLE Table1 ADD age AS 2012-birthyear
  • 再次检查。没有任何主键、外键、索引、约束集...

标签: sql sql-server sql-server-2008


【解决方案1】:

好的,既然我们已经弄清楚旧表是慢表而不是新表,那么一切都表明转发记录的数量非常多是罪魁祸首。

要删除转发的记录,您可以使用此查询:

ALTER TABLE dbo.Table2 REBUILD;

向堆中添加一列很可能会导致每一行频繁移动,从而导致大量转发记录。 sys.dm_db_index_physical_stats DMV 返回的列 forwarded_records_count 显示转发的数量 - 在您的情况下几乎所有行。

SELECT * INTO 不会复制转发指针,而是重新组织它。因此,您确实看到了性能差异。

虽然我们谈论的是转发,但在大多数情况下,在表上拥有一个聚集索引是一个非常好的主意。这样就避免了这样的问题。

在您的情况下,ID 列似乎是集群主键的候选者(如果它是唯一的),但我需要了解有关该模型的更多信息才能在此处为您提供建议。

【讨论】:

  • +1 完全同意。如果您希望保留您的临时诊断查询,您可能应该将它们合并到此答案中,尽管它们可能会被删除,但目前不是答案。
  • 是的,我同意你所说的。添加集群索引是一个好主意,我计划在未来这样做。集群PK在这里甚至是一个更好的选择,但我必须在未来详细计划它。事实上,由于表量很大,我还在做数据清洗。同样,我认为你的回答非常好,这个问题被认为已经解决了。
  • 衷心感谢 Sebastian、Martin 和其他人的帮助。也是在我还是 Stack Overflow 的菜鸟时维护此页面的版主。
【解决方案2】:

另一个尝试:请运行此程序并发布文本和查询结果。与往常一样,请务必将 Table1 和 Table2 替换为真实姓名。在这种情况下,您还需要替换数据库名称。

SET STATISTICS IO ON;
SET STATISTICS TIME ON;
GO
SELECT COUNT(DISTINCT ID) FROM DB1.dbo.Table1
GO
SELECT COUNT(DISTINCT ID) FROM DB2.dbo.Table2
GO
SELECT COUNT(DISTINCT ID) FROM DB1.dbo.Table1
GO
SELECT COUNT(DISTINCT ID) FROM DB2.dbo.Table2
GO
SELECT COUNT(DISTINCT ID) FROM DB1.dbo.Table1
GO
SELECT COUNT(DISTINCT ID) FROM DB2.dbo.Table2
GO
SET STATISTICS TIME OFF;
SET STATISTICS IO OFF;
GO
SELECT * FROM sys.dm_db_index_operational_stats(DB_ID('DB1'),OBJECT_ID('DB1.dbo.Table1'),NULL,NULL);
SELECT * FROM sys.dm_db_index_operational_stats(DB_ID('DB2'),OBJECT_ID('DB2.dbo.Table2'),NULL,NULL);

【讨论】:

  • 好的,我将把它和前面的 XML 部分一起发布。
  • @YinYeeLeong - 顺便说一句,请编辑您的问题以提供结果,而不是将其添加为另一个答案。
  • 您还想要 XML 部分吗?因为我们不认为可以从那里发现问题。
【解决方案3】:

我认为这是由过时的统计数据引起的。但是我们需要更多关于您的环境的信息。你能运行这两个查询并发布结果吗?确保使用两个表的名称而不是提供的两个表的名称。

SELECT  QUOTENAME(OBJECT_SCHEMA_NAME(t.object_id)) + '.' + QUOTENAME(t.name) tbl,
        s.name stats_name,
        cols.cols,
        t.create_date table_date,
        STATS_DATE(s.object_id, s.stats_id) AS statistics_date,
        s.auto_created,
        s.user_created,
        s.no_recompute,
        s.has_filter,
        s.filter_definition
FROM    sys.tables t
LEFT OUTER JOIN sys.stats s
        ON s.object_id = t.object_id
OUTER APPLY (
              SELECT  STUFF((SELECT ',' + c.name
                             FROM   sys.stats_columns sc
                             JOIN   sys.columns c
                                    ON sc.column_id = c.column_id
                                       AND sc.object_id = c.object_id
                             WHERE  sc.object_id = s.object_id
                                    AND sc.stats_id = s.stats_id
                             ORDER BY sc.stats_column_id
                      FOR   XML PATH(''),
                                TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '') cols
            ) cols
--Update Table Name(s) here:      
WHERE   t.OBJECT_ID IN ( OBJECT_ID('[Sales].[SpecialOffer]'),
        OBJECT_ID('[Sales].[SalesOrderDetail]') );

SELECT  name,
        compatibility_level,
        is_auto_close_on,
        is_auto_shrink_on,
        state_desc,
        is_auto_create_stats_on,
        is_auto_update_stats_on,
        is_auto_update_stats_async_on
FROM sys.databases
WHERE database_id = DB_ID();

【讨论】:

  • 已将此查询移至问题中,因此认为此答案不再有任何用途?
  • @MartinSmith,虽然此查询没有提供希望的答案,但它是解决此问题过程中的重要一步。所以我不会感谢你删除它。
  • 我也不感谢所有没有评论的反对票。
  • @SebastianMeine - Stack Overflow 是一个问答网站,而不是论坛。答案应该是问题的完整答案。如果您认为这是一个重要步骤,那么请务必将其合并到一个完整完整的连贯答案中。将随机查询分散在不同的答案中只是噪音,而不是堆栈溢出的工作方式。
  • @SebastianMeine 很多人会使用聊天室进行来回讨论。这比通过答案中的 cmets 完成要容易得多。 chat.stackoverflow.com/rooms/21467/questions-chat
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-09
  • 1970-01-01
  • 2015-01-06
  • 1970-01-01
  • 2019-08-07
  • 1970-01-01
相关资源
最近更新 更多