2013-11-03 51 views
0

我目前正在Delphi開發一個應用程序,它使用SQL來挖掘第三方開票系統的後端,因此我們可以擴展它的報告功能。我認爲自己在編程的delphi方面相當精通,但SQL對我來說是新的,所以在這個論壇和其他資源的巨大幫助下,我設法教會了自己,而不是我認爲我能夠做到的。複雜查詢 - 連接會比子查詢更快嗎?

大部分數據都是從幾張表中抽出來的(我沒有這方面的問題,所以我不會阻塞那些細節的帖子),但是我有一個問題得到的成本價格。它存儲在一個表中,可以跟蹤每個產品的歷史成本價格(16'000+),可能有數百條記錄,但是我只需要每張產品與發票日期最接近的成本(< =) 。 下面是函數:

CREATE FUNCTION dbo.CostAtDate (@costdate AS datetime , @product AS int) 
RETURNS decimal(18,2) 
AS 
BEGIN 
DECLARE @result decimal(18,2) 
SET @result = (
    Select Top 1 
    BASE_InventoryCostLogDetail.AverageCostAfter 
    From 
    BASE_InventoryCostLogDetail 
    Where 
    CreatedDttm < @costdate And CreatedDttm > DATEADD(month,-1,@costDate) And 
    ProdId = @product 
    Order By 
    CreatedDttm Desc) 

RETURN @result 
END 

這裏是查詢之一(有幾個不同的,但都在同一基本結構爲基礎):

Select 
    BASE_Customer.Name, 
    SO_SalesOrder.OrderNumber, 
    SO_SalesOrderInvoice_Line.Description, 
    SO_SalesOrderInvoice_Line.UnitPrice, 
    Case SO_SalesOrderInvoice_Line.ItemTaxCodeId 
    When '100' Then (SO_SalesOrderInvoice_Line.UnitPrice/11) * 10 
    Else SO_SalesOrderInvoice_Line.UnitPrice End As exgst, 
    SO_SalesOrderInvoice_Line.QuantityUom, 
    SO_SalesOrderInvoice_Line.QuantityDisplay, 
    Case SO_SalesOrderInvoice_Line.QuantityUom 
    When 'cases.' Then dbo.CostAtDate(SO_SalesOrder.OrderDate, 
    SO_SalesOrderInvoice_Line.ProdId) * BASE_Product.SoUomRatioStd 
    Else dbo.CostAtDate(SO_SalesOrder.OrderDate, 
    SO_SalesOrderInvoice_Line.ProdId) End As cost, 
    Case SO_SalesOrderInvoice_Line.QuantityUom 
    When 'cases.' Then ((dbo.CostAtDate(SO_SalesOrder.OrderDate, 
    SO_SalesOrderInvoice_Line.ProdId) * BASE_Product.SoUomRatioStd)/11) * 10 
    Else (dbo.CostAtDate(SO_SalesOrder.OrderDate, 
    SO_SalesOrderInvoice_Line.ProdId)/11) * 10 End As exgstcost, 
    BASE_Product.SoUomRatioStd, 
    BASE_Product.Name As Name1, 
    SO_SalesOrder.OrderDate 
From 
    BASE_Customer Inner Join 
    SO_SalesOrder On SO_SalesOrder.CustomerId = BASE_Customer.CustomerId 
    Inner Join 
    SO_SalesOrderInvoice_Line On SO_SalesOrderInvoice_Line.SalesOrderId = 
    SO_SalesOrder.SalesOrderId Inner Join 
    BASE_Product On SO_SalesOrderInvoice_Line.ProdId = BASE_Product.ProdId 
Where 
    SO_SalesOrder.OrderDate Between '20131028' And '20131029' 

現在能正常工作時,我只在選定範圍內有幾張發票,但考慮到它每個記錄至少要調用三次函數,當我在超過一天的時間內生成報告時,性能會降低(我們通常需要報告兩週時間)。

不幸的是,鑑於它是第三方產品(任何人誰好奇inFlow庫存),我不能改變表結構。

是否有任何方式使用更高效的聯接,派生表(我理解這個概念,但從來沒有這樣做過),甚至重寫整個查詢,這將大大提高性能?

+2

sqlfiddle ...... –

+0

如果從查詢中刪除函數,性能是否會顯着不同? – Andrew

+0

@Andrew如果我刪除函數並將其替換爲無,那麼是的它是非常快的(172毫秒沒有功能,11.03秒的功能)。但是,如果我將函數替換爲該函數有效地執行的子查詢,那麼執行時間將回落到幾十秒。 –

回答

0

看樣子我已經成功地解決我自己的問題,它只是花了一點點研究,橫向思維,很多失敗的嘗試和罵人的話的(哦,這麼多罵人的話!)

我玩弄在這個程序的delphi一邊添加額外的步驟,根據我需要的日期範圍從成本價格表中選擇,然後重新編寫我的原始查詢,以合併新加入的表格。然而,它並沒有那麼有趣解決一個問題,如果你沒有學到任何新的技能;-)。

答案:TVF-表值函數。 經過很多關於替代方式的研究,我偶然發現了這些TVF。進一步的調查似乎顯示出,由於方式的優化處理標量函數,而不是TVF的,他們極大地加快在某些應用中,所以我決定重新寫我的功能,例如:

CREATE FUNCTION dbo.CostAtDate (@costdate AS datetime , @product AS int) 
RETURNS table 
AS 
Return (
    Select Top 1 
    BASE_InventoryCostLogDetail.AverageCostAfter 
    From 
    BASE_InventoryCostLogDetail 
    Where 
    CreatedDttm < @costdate And CreatedDttm > DATEADD(month,-1,@costDate) And 
    ProdId = @product 
    Order By 
    CreatedDttm Desc) 

而不是調用它的傳統方式

dbo.CostAtDate(SO_SalesOrder.OrderDate, SO_SalesOrderInvoice_Line.ProdId)

我重新吉格舞的所有引用我的查詢到:

(Select * from dbo.CostAtDate(SO_SalesOrder.OrderDate, SO_SalesOrderInvoice_Line.ProdId))

測試它我發現性能顯着提高(對於65k +記錄爲4秒,與之前的函數相比,即使預期的結果集大約爲10k記錄,通常會在幾分鐘後超時)。)

我確信很多人都知道更好的方法,但目前這個效果很好....我自己發現了這一切:讚揚我!