为啥这个 Sql 语句(有 2 个表连接)需要 5 分钟才能完成?
Posted
技术标签:
【中文标题】为啥这个 Sql 语句(有 2 个表连接)需要 5 分钟才能完成?【英文标题】:Why does this Sql Statement (with 2 table joins) takes 5 mins to complete?为什么这个 Sql 语句(有 2 个表连接)需要 5 分钟才能完成? 【发布时间】:2010-11-16 00:19:12 【问题描述】:更新:下面添加了 3 个更新
以下 sql 语句需要 5 分钟才能完成。我只是。不。得到。它 :( 第一个表中有 6861534 行。第二个表少一点.. 第三个表(包含 4 个 GEOGRAPHY FIELDS)与第一个表相同。
第三张表中的那些GEOGRAPHY
字段......它们不应该与sql语句混淆......应该吗?可能是因为表太大(由于GEOGRAPHY
字段)以至于它具有巨大的页面大小或其他什么..从而破坏了 COUNT 所做的表扫描?
SELECT COUNT(*)
FROM [dbo].[Locations] a
inner join [dbo].[MyUSALocations] b on a.LocationId = b.LocationId
inner join [dbo].[GeographyBoundaries] c on a.locationid = c.LocationId
更新
根据要求,这里是有关 GeographyBoundaries 表的更多信息...
/****** Object: Index [PK_GeographyBoundaries] Script Date: 11/16/2010 12:42:36 ******/
ALTER TABLE [dbo].[GeographyBoundaries] ADD CONSTRAINT [PK_GeographyBoundaries] PRIMARY KEY CLUSTERED
(
[LocationId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
更新 #2 - 添加非聚集索引后
添加非聚集索引后,现在下降到 4 秒!这太棒了。但是为什么?
什么Zee Frak?
更新 3 - 更多有趣和令人困惑的信息!
现在,当我只做 ONE join 并强制 INDEX .. 它回到 5 分钟。我这样做是为了
确保 MyUSALocations 表没有与联接搞混。 确保 PK 正在做奇怪的事情。.
SELECT COUNT(*)
FROM [dbo].[Locations] a
INNER JOIN [dbo].[GeographyBoundaries] c
WITH (INDEX(PK_GeographyBoundaries)) ON a.locationid = c.LocationId
【问题讨论】:
所有 LocationId 字段都是整数吗? varchar(5000)?其他? GeographyBoundaries 上的索引是否与表数据物理位于同一文件组中? @Tahbaza :是的,所有身份都是 INT。这都在一个文件组中,或者默认标准是什么。我没有对默认的 Create New DB 进行任何调整。 【参考方案1】:这是不对的。
我有两种可能:
1) 表格上的统计信息已过时。重建索引并更新统计信息。
2)正如您所说,地理表记录很大,跨越许多页面(不是一个记录跨越多个页面,因为它不能,但记录接近 8K 标记)。在这种情况下,有趣的是,在聚集索引上创建另一个非聚集索引可能会有所帮助。
更新
我很高兴它成功了。现在解释一下。
首先,如果某些事情不太正确并且执行计划看起来很奇怪,请始终查看统计信息并重建索引。
为聚集索引创建非聚集索引通常不会带来任何好处,但是当表有很多记录并且记录接近其 8K 限制时,它会很有帮助。如您所知,SQL 在去磁盘加载记录时,会加载一个 8K 的页面。以类似的方式进入索引,它将加载一个 8K 页面。现在索引是一个 4 字节整数,这意味着加载 2000 条记录的 ID,而如果它使用聚集索引,它将加载少量记录(请记住,我们需要的只是 JOIN 位的 ID)。现在这是一个二分搜索,我不希望它只提供一点帮助。所以也许其他的东西不太对劲,但是没有看过系统很难猜到。
【讨论】:
好的,我更新了耗时 32 秒的统计数据。现在我再次运行查询......到目前为止最多 1 分钟并且仍在继续......所以我不确定这是否有帮助(很大)。 老兄! WTFFFFFFFFFF!!!!!!!!!!通过添加一个新的非聚集索引,它现在是 4 秒长(这是我所期望的)。你说对了!呃……但是为什么呢?!【参考方案2】:如果没有看到一些数据,我无法确定,但您 97% 的成本是您对 GeographyBonudaries 表的聚集索引扫描。 LocationID 是 GeographyBoundaries 表的主键吗?
您可以尝试创建一个仅包含 locationID 的新表,然后加入该表而不是 GeographyBoundaries 表,以查看地理字段是否是您的罪魁祸首。只需确保将所有索引都设置为相同,以确保将苹果与苹果进行比较。我猜这些领域会拖慢你的速度。
如果这样做确实加快了速度,并且您需要经常进行计数,请考虑保留此新表并定期刷新它,具体取决于数据更新的频率以及您需要计数的准确程度在任何给定的时间点。
出于好奇,您的 PK_GeographyBoundaries 的定义是什么?
【讨论】:
我已根据要求更新了最初的帖子,以包含有关 GeographyBoundaires 表的更多信息。 嗯,好的。提供的 SQL 刚刚有 count(*)!【参考方案3】:我会说大部分工作是 I/O。查询计划似乎表明 I/O 成本相对于 CPU 成本较高。例如,在 PK_GeographyBoundries 上,估计的 CPU 成本约为 7.5,估计的 I/O 成本约为 1730,这是估计算子成本的很大一部分。
统计数据可能已过时,但似乎并没有太大帮助。无论如何,它必须扫描整个表。如果它存储在更少的页面上,它可能会受益于对非聚集索引的扫描,但无论如何你都有很多 I/O。而且,最好的索引是 LocationID,它聚集在两个表上。您已经获得了最好的索引。
如果提高性能很重要,那么您必须寻找加速物理 I/O 的方法。
我不确定地理列是否会影响性能。查询计划显示行大小很小(每个 11 个字节)。
【讨论】:
总之,我的高清很烂,我应该考虑升级? @Pure Krome:可能不是你的高清很烂。要提高 I/O 性能,您可以做两件事:1) 减少 I/O 或 2) 更快地执行 I/O。看来您的更新 2 演示了 #1。非聚集索引执行较少 I/O 的原因是它占用的空间较少(仅 locationID,没有 geography)。扫描更少的页面所需的时间更少。【参考方案4】:好吧,您不需要涉及“位置”表,USA_Loacations 和地理边界之间的直接连接就足够了。优化器已经注意到了这一点并分解了 loacations 表,但是如果您直接加入它们,它可能会选择更好的加入策略。
【讨论】:
我需要加入 parent Locations 表,因为它有一个字段 (NAME NVARCHAR(100)),这是我需要的。以上是关于为啥这个 Sql 语句(有 2 个表连接)需要 5 分钟才能完成?的主要内容,如果未能解决你的问题,请参考以下文章