【发布时间】:2011-03-27 14:29:53
【问题描述】:
我有两个索引视图,v_First 和 v_Second。当我有一个仅基于其中一个视图进行过滤的WHERE 子句时,这些视图效果很好,但是一旦我有了基于这两个视图的过滤条件,我就会得到两次聚集索引扫描,结果性能很差。
我的查询是:
SELECT * FROM dbo.v_First (NOEXPAND)
JOIN dbo.v_Second (NOEXPAND)
ON dbo.v_First.id = dbo.v_Second.id
WHERE
dbo.v_First.Firstname = 'JUSTIN'
OR dbo.v_Second.Surname = 'JUSTIN'
如果我注释掉上述两个 WHERE 子句中的任何一个,我就会得到查找并执行查询,因此我知道我单独定义了正确的索引。
为什么在基于多个索引视图进行过滤时查询不执行,我该如何解决?
(抱歉,我不能发布执行计划,反正它们都是微不足道的 - 只需对两个各自的视图聚集索引和合并连接进行两次聚集索引扫描)
更新:
v_First 列:
- ID(bigint,聚集索引)
- 名字(varchar(254),非聚集索引)
v_Second 列:
- ID(bigint,聚集索引)
- 姓氏(varchar(254),非聚集索引)
所有索引只包含一列。
更新,第二次:
我发现如果将OR 子句更改为AND 子句,则查询执行良好。我还发现,如果我将查询更改为使用 UNION 语句而不是 OR,则查询执行良好:
SELECT * FROM dbo.v_First (NOEXPAND)
JOIN dbo.v_Second (NOEXPAND)
ON dbo.v_First.ID = dbo.v_Second.ID
WHERE dbo.v_First.Firstname = 'JUSTIN'
UNION SELECT * FROM dbo.v_First (NOEXPAND)
JOIN dbo.v_Second (NOEXPAND)
ON dbo.v_First.ID = dbo.v_Second.ID
WHERE dbo.v_Second.Surname = 'JUSTIN'
据我所知,这两个查询应该是等价的?
最后,我还发现使用子查询代替也有一个奇怪的效果,下面的查询执行得很好:
SELECT * FROM dbo.v_First (NOEXPAND)
-- JOIN dbo.v_Second (NOEXPAND)
-- ON dbo.v_First.ID = dbo.v_Second.ID
WHERE dbo.v_First.ID IN
(
SELECT ID FROM dbo.v_Second (NOEXPAND)
WHERE dbo.v_Second.Surname = 'JUSTIN'
)
OR dbo.v_First.Firstname = 'JUSTIN'
但是,如果我取消注释 JOIN(以便我可以从查询结果中的第二个表中获取列),那么我会在 v_Second 聚集索引上进行表扫描(但请注意,这仍然比原始查询,因为它只涉及 1 次扫描,而不是 2 次)。
我很困惑——这是怎么回事?看来我可以通过“重构”我的查询来解决这些问题,但是我担心我不明白这里发生了什么——我宁愿避免做出我不完全理解的更改。
【问题讨论】:
-
@OMG - 几乎只是创建索引视图绝对必要的列和索引 - 我已经更新了我的问题。
-
即使只有 ID、Firstname、Surname 的表在 ID 上聚集并分别在 Firstname 和 Surname 上建立索引,沿着查询行进行的自联接也会导致类似的“糟糕”外观查询计划+慢执行。引入与第一个表具有相同架构和相同索引的第二个表不会改进查询。很有趣。
-
一个问题:基表中有多少行?
-
@gbn - 基表中有大约 3,000,000 行,但是在每个视图中这些行被过滤到大约 1,000,000。然而,这只是用于调查可能的性能问题的数据样本 - 实际数据集很可能在基表中超过 1200 万条记录。
标签: sql sql-server tsql sql-server-2008 query-optimization