【问题标题】:Select Timeout With 2 Parameters使用 2 个参数选择超时
【发布时间】:2010-07-27 16:14:27
【问题描述】:

我有以下视图,称为 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 会超时而没有结果。 以下是获取超时的查询:

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

【问题讨论】:

  • 你看过选择查询的执行计划吗?它有时会给出关于缺失索引的好提示
  • 我赞同 Martin Smith 的评论,将视图引用替换为它们包含的实际查询,然后优化查询。从程序/OO 编程的角度来看,这是违反直觉的,但这是一个基于 SET 的领域......

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


【解决方案1】:

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

Article、Client、Contract、Goods、GoodsArticle、GoodsEvent、Site、SiteIn、StockType、StockTypeContract

所有这些实际上是你的结果所必需的,还是其中任何一个只是你碰巧加入的观点的产物?

这 10 个表的计划中有 25 个根节点,因此肯定有一些表被多次访问,这似乎是一种非常浪费的方式。

您可以在这部分计划中看到(加起来高达 40% 的成本)GoodsEvent 似乎被访问了 3 次。我很确定,如果你摆脱那些观点,你将能够巩固这一点。

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')

【讨论】:

  • 你建议我做什么?之所以调用这些视图,是因为它们过滤了有关客户可以获得哪些商品以及它们生成了哪些事件的信息。
  • 我们建议您停止使用浏览期。
  • @Hallaghan 目前查询计划非常复杂。我怀疑如果您重新开始并针对基表编写查询,您将能够简化很多事情。在不知道您的数据和所需语义的情况下,我很难提出具体的改进建议。
【解决方案2】:

如果你想要性能,视图不应该引用其他视图。这只是彻头彻尾的糟糕设计。您不应该使用视图来执行此操作。当您这样做时,它必须首先完全实现这些视图,然后才能创建记录集。因此,对于 200 条最终记录,您可能需要调用数十亿美元。这会极大地减慢速度,我发现当人们使用这种技术时,如果您从视图一直到底部,您通常会多次从同一个表中调用相同的数据。不要以这种方式使用视图。如果必须使用视图,则直接访问表,不要调用其他视图。这是一条你不想走的路,我们几乎失去了数百万美元的客户,因为有人设计了这种方式,而不是使用良好的数据访问方法。

这是一个有保证的,无法修复它的性能问题,这将导致您的数据库最终停止。这是糟糕的设计阶段,必须尽快进行更改。

【讨论】:

    【解决方案3】:

    呃哦……

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

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

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

    编辑:其他想法..

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

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

    【讨论】:

    • 好吧,我并没有完全设计这个视图或项目,我只是在使用这个数据库修复这个应用程序的错误。回到绘图板这里不再是一个选择,至少他们是这么说的。
    • 我不同意 - 如有必要加入,因为这是规范化数据库所需要的。但是,将视图相互叠加肯定会导致性能和维护不佳 - 很好。
    • 我同意小马们的观点,但我不是这里的负责人。我只能做我被告知的事情。我只是对应用程序进行维护。
    • @Hallaghan - 当您查看查询计划时,同一个表是否被多次访问?如果是这样,您可以重写查询以查看没有视图的基表吗? stackoverflow.com/questions/3222542/…
    • 我在主帖中包含了上面的实际执行计划。
    猜你喜欢
    • 1970-01-01
    • 2018-05-12
    • 1970-01-01
    • 2013-02-18
    • 1970-01-01
    • 2023-03-05
    • 2020-08-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多