我在应用程序中使用的视图很大。我认为我已经缩小了性能问题,但是不确定如何解决。视图的简化版本如下所示:
SELECT ISNULL(SEId + '-' + PEId, '0-0') AS Id,
*,
DATEADD(minute, Duration, EventTime) AS EventEndTime
FROM (
SELECT se.SEId, pe.PEId,
COALESCE(pe.StaffName, se.StaffName) AS StaffName, -- << Problem!
COALESCE(pe.EventTime, se.EventTime) AS EventTime,
COALESCE(pe.EventType, se.EventType) AS EventType,
COALESCE(pe.Duration, se.Duration) AS Duration,
COALESCE(pe.Data, se.Data) AS Data,
COALESCE(pe.Field, se.Field) AS Field,
pe.ThisThing, se.OtherThing
FROM PE pe FULL OUTER JOIN SE se
ON pe.StaffName = se.StaffName
AND pe.Duration = se.Duration
AND pe.EventTime = se.EventTime
WHERE NOT(pe.ThisThing = 1 AND se.OtherThing = 0)
) Z
这可能并不能说明查询结构的全部原因,但也许可以给您一个想法-该视图将两个我设计不佳的非常糟糕的表连接在一起,并尝试从中综合一些信息。
因此,由于这是应用程序中使用的视图,因此在尝试优化时将其包装在另一个SELECT中,如下所示:
SELECT * FROM (
-- … above code …
) Q
WHERE StaffName = 'SMITH, JOHN Q'
因为该应用程序正在搜索结果中的特定人员。
问题似乎是该COALESCE(pe.StaffName, se.StaffName) AS StaffName
部分,而我是从的视图中选择的StaffName
。如果将其更改为pe.StaffName AS StaffName
or se.StaffName AS StaffName
,性能问题将消失(但请参见下面的更新2)。但这不会这样做,因为其中一侧或另一侧FULL OUTER JOIN
可能会丢失,因此一个或另一个字段可能为NULL。
我可以重构这个替换为COALESCE(…)
其他东西,将其重写为子查询吗?
其他说明:
- 我已经添加了一些索引来解决其余查询的性能问题-如果没有的
COALESCE
话,它很快。 - 令我惊讶的是,即使
WHERE
包括包装子查询和语句,查看执行计划也不会引发任何标志。我在分析器中的子查询总费用为0.0065736
。mph 执行需要四秒钟。 - 将应用程序更改为不同的查询
(例如,返回可能会奏效,但作为最后的手段-我真的希望我可以优化视图而不必诉诸应用程序。pe.StaffName AS PEStaffName, se.StaffName AS SEStaffName
和执行WHERE PEStaffName = 'X' OR SEStaffName = 'X'
) - 为此,存储过程可能更有意义,但是应用程序是使用Entity Framework构建的,我无法弄清楚如何使它与返回表类型的SP完美配合(完全是另一个主题)。
指标
到目前为止,我添加的索引如下所示:
CREATE NONCLUSTERED INDEX [IX_PE_EventTime]
ON [dbo].[PE] ([EventTime])
INCLUDE ([StaffName],[Duration],[EventType],[Data],[Field],[ThisThing])
CREATE NONCLUSTERED INDEX [IX_SE_EventTime]
ON [dbo].[SE] ([EventTime])
INCLUDE ([StaffName],[Duration],[EventType],[Data],[Field],[OtherThing])
更新资料
嗯……我尝试模拟上面的严重更改,但没有帮助。即,在) Z
上面之前,我添加了AND (pe.StaffName = 'SMITH, JOHN Q' OR se.StaffName = 'SMITH, JOHN Q')
,但是性能是相同的。现在我真的不知道从哪里开始。
更新2
@ypercube关于需要完全连接的评论使我意识到我的综合查询遗漏了一个可能重要的组件。虽然是的,但我需要完全连接,我在上面做的测试通过删除COALESCE
和测试连接的一侧是否为非null值,将使完全连接的另一侧不相关,优化器可能正在使用此连接事实加快了查询速度。另外,我已经更新了示例,以表明它StaffName
实际上是联接键之一,这可能与问题有很大关系。我现在也倾向于他的建议,那就是将其分为三路并举而不是完全加入,这可能是答案,并且将简化COALESCE
我正在做的s的工作量。现在尝试。
KeyField
,都INCLUDE
对该StaffName
字段和其他几个字段进行索引。我可以在问题中发布索引定义。我正在测试服务器上进行这项工作,因此我可以添加您认为可能有助于尝试的任何索引!
WHERE pe.ThisThing = 1 AND se.OtherThing = 0
取消联接FULL OUTER
并使查询等同于内部联接的条件。您确定需要完全加入吗?
INNER JOIN
,LEFT JOIN
带有WHERE IS NULL
check,带有IS NULL的RIGHT JOIN),然后UNION ALL
分为三个部分。这样就不需要使用COALESCE()
它,并且可能(可能)帮助优化程序找出重写。