选择具有2个参数的超时

时间:2010-07-27 16:14:28

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

我有以下View,名为ViewGoods:

SELECT     
G.Gid, 
SI.[$Id] AS FirstSiteInId, 
SI.Date AS FirstSiteInDate, 
SI.Comments AS FirstSiteInComments, 
S.[$Id] AS FirstSiteId, 
S.[$Refex] AS FirstSiteRefex, 
SI.Client AS ClientId, 
C.[$Refex] AS ClientRefex, 
CASE WHEN SI.Contract IS NULL THEN (SELECT Contract.[$Id]
                                    FROM StockType AS ST 
                                    INNER JOIN StockTypeContract AS STC ON ST.[$Id] = STC.[$ParentId] 
                                    INNER JOIN Contract ON STC.Contract = Contract.[$Id]
                                    WHERE ST.[$Id] = VGST.StockType 
                                    AND SI.Date >= STC.StartDate)
                                    ELSE SI.Contract END AS Contract, 
CASE WHEN SI.Contract IS NULL THEN (SELECT Contract.[$Refex]
                                    FROM StockType AS ST 
                                    INNER JOIN StockTypeContract AS STC ON ST.[$Id] = STC.[$ParentId] 
                                    INNER JOIN Contract ON STC.Contract = Contract.[$Id]
                                    WHERE ST.[$Id] = VGST.StockType 
                                    AND SI.Date >= STC.StartDate) 
                                    ELSE CT.[$Refex] END AS ContractRefex, 
CASE WHEN COALESCE (Q.Quantity, 0) > 0 THEN L.SiteId ELSE NULL END AS SiteId, 
CASE WHEN COALESCE (Q.Quantity, 0) > 0 THEN L.SiteRefex ELSE NULL END AS SiteRefex, 
CASE WHEN COALESCE (Q.Quantity, 0) > 0 THEN L.Lid ELSE NULL END AS Lid, 
ISNULL(W.Weight, VGSA.Weight * Q.Quantity) AS Weight, 
COALESCE (Q.Quantity, 0) AS Quantity, 
VGSA.Article, 
VGSA.ArticleName, 
VGST.StockType, 
VGST.StockTypeRefex
FROM dbo.Goods AS G 
INNER JOIN dbo.SiteIn AS SI ON G.SiteIn = SI.[$Id] 
INNER JOIN dbo.Client AS C ON C.[$Id] = SI.Client 
INNER JOIN dbo.Site AS S ON SI.Site = S.[$Id] 
LEFT OUTER JOIN dbo.Contract AS CT ON SI.Contract = CT.[$Id] 
LEFT OUTER JOIN dbo.ViewGoodsLocation AS L ON G.Gid = L.Gid 
LEFT OUTER JOIN dbo.ViewGoodsWeight AS W ON G.Gid = W.Gid 
LEFT OUTER JOIN dbo.ViewGoodsQuantity AS Q ON G.Gid = Q.Gid 
LEFT OUTER JOIN dbo.ViewGoodsSingleArticle AS VGSA ON G.Gid = VGSA.Gid 
LEFT OUTER JOIN dbo.ViewGoodsStockType AS VGST ON VGST.Gid = G.Gid

使用参数Client或参数Lid查询View时,一切都运行良好。 但是,如果我尝试将它们中的两个混合在一起,则View会超时而没有结果。 以下是获取超时的查询:

SELECT [t0].[Gid], [t0].[FirstSiteInId], [t0].[FirstSiteInDate], [t0].[FirstSiteInComments], [t0].[FirstSiteId], [t0].[FirstSiteRefex], [t0].[ClientId], [t0].[ClientRefex], [t0].[Contract], [t0].[ContractRefex], [t0].[SiteId], [t0].[SiteRefex], [t0].[Lid], [t0].[Weight], [t0].[Quantity], [t0].[Article], [t0].[ArticleName], [t0].[StockType], [t0].[StockTypeRefex]
FROM [ViewGoods] AS [t0]
WHERE ([t0].[Lid] IS NOT NULL) AND (([t0].[ClientId]) = 70)

我哪里出错?

编辑:我在此处列出了实际执行计划http://pastebin.com/PMY0aLE1

3 个答案:

答案 0 :(得分:2)

从您发布的查询计划中,它似乎正在访问10个表

文章,客户,合同,商品,GoodsArticle,GoodsEvent,Site,SiteIn,StockType,StockTypeContract

您的结果是否真的需要所有这些,或者其中任何一个都只是您正在加入的视图中的人工制品?

计划中有25个根节点用于这10个表,所以肯定会有一些表被访问多次,这看起来非常浪费。

您可以在计划的这一部分中看到(最多可增加40%的成本),MoneyEvent似乎被访问了三次。我很确定你是否能够摆脱那些能够巩固这一点的观点。

Portion of plan http://img245.imageshack.us/img245/4105/executionplan.png

我认为目前这个计划正在做这样的事情

SELECT Query3.Gid, Query3.SiteId, Query3.Lid, Query3.Expr1017
FROM 
(
SELECT 
     Gid,
     SUM(CASE WHEN Type ='SO' THEN -Quantity ELSE Quantity END) AS Expr1017
FROM GoodsEvent
WHERE Type IN('AQ','SI','SO') AND IsDeleted = 0
GROUP BY Gid
) Query1
JOIN
(
SELECT 
     Gid,
     MAX(EventOn) AS Expr1014
FROM GoodsEvent
WHERE IsDeleted = 0
GROUP BY Gid
) Query2 ON Query1.GID = Query2.GID
JOIN
(
SELECT 
GoodsEvent.Gid, 
GoodsEvent.EventOn, 
GoodsEvent.SiteId, 
GoodsEvent.Lid
FROM GoodsEvent WHERE IsDeleted = 0
) Query3 ON Query3.gid=Query2.gid AND Query3.EventOn = Query2.Expr1014

可能值得测试这是否在语义上是等效的并且执行得更好

;WITH X AS
(
SELECT Gid,  
       SiteId, 
       Lid, 
       RANK() OVER (PARTITION BY Gid ORDER BY EventOn DESC) AS RN,
       Type
FROM GoodsEvent
WHERE IsDeleted = 0
) 
SELECT Gid,SiteId, Lid, 
       SUM(CASE WHEN Type ='SO' THEN -Quantity ELSE Quantity END) 
       OVER(PARTITION BY Gid) AS Expr1017,
FROM X WHERE RN=1 AND Type IN('AQ','SI','SO')

答案 1 :(得分:1)

如果您想要性能,则视图不应引用其他视图。这只是糟糕的设计。您不应该使用视图来执行此操作。执行此操作时,必须先完全实现这些视图,然后才能创建记录集。因此,对于可能200个最终记录,您可能需要调整几十亿。这会让事情变得非常缓慢,我发现当人们使用这种技术时,如果你一直按照视图一直到底,你经常会在同一个表中多次调用相同的数据。不要以这种方式使用视图。如果必须使用视图,则直接访问表,不要调用其他视图。这是一条你不想失望的道路,我们几乎失去了一个多元化的客户,因为有人设计了这种方式,而不是使用良好的数据访问方法。

这是有保证的,无法修复它会导致数据库最终戛然而止的性能问题。这是一个不好的设计时期,必须尽快改变。

答案 2 :(得分:0)

哦,哦......

我可以在您的视图定义中看到您正在加入至少9个其他数据结构,这些数据结构似乎也是视图(因此可能会有更多的表连接)。

这可能不是您想要听到的答案,但如果您需要将这么多数据结构加在一起,那么在设计时就会出现问题。

我的建议是你回到绘图板并重新考虑这个数据库的设计。

编辑:其他想法..

考虑到当您执行经常要求您连接多个表的查询时,这些是索引视图的候选者,即物化结构,它们实际上是表。定期执行大量连接操作会导致查询性能不佳,但可扩展性有限。

请记住,规范化是良好数据库设计的起点,它不一定是您的终点。