2012-10-02 84 views
0

我有以下DB結構(簡體):如何提高SQL查詢性能

Payments 
---------------------- 
Id  | int 
InvoiceId | int 
Active | bit 
Processed | bit 


Invoices 
---------------------- 
Id    | int 
CustomerOrderId | int 


CustomerOrders 
------------------------------------ 
Id      | int 
ApprovalDate    | DateTime 
ExternalStoreOrderNumber | nvarchar 

每個客戶訂單都有發票,每張發票可以有多個付款。 ExternalStoreOrderNumber是來自我們從外部合作伙伴商店導入訂單的訂單的參考,以及導入發生時的時間戳ApprovalDate

現在我們根據以下邏輯有,我們有一個錯誤的進口的需要更改爲其他發票的某些款項(幾個hundert,這樣太馬赫做手工)問題:
搜索訂單的發票它具有與當前數字相同的外部數字,但以0開頭而不是當前數字。

爲了做到這一點,我創建了以下查詢:

UPDATE DB.dbo.Payments 
    SET InvoiceId= 
     (SELECT TOP 1 I.Id FROM DB.dbo.Invoices AS I 
      WHERE I.CustomerOrderId= 
       (SELECT TOP 1 O.Id FROM DB.dbo.CustomerOrders AS O 
        WHERE O.ExternalOrderNumber='0'+SUBSTRING(
         (SELECT TOP 1 OO.ExternalOrderNumber FROM DB.dbo.CustomerOrders AS OO 
         WHERE OO.Id=I.CustomerOrderId), 1, 10000))) 
    WHERE Id IN (
     SELECT P.Id 
      FROM DB.dbo.Payments AS P 
      JOIN DB.dbo.Invoices AS I ON I.Id=P.InvoiceId 
      JOIN DB.dbo.CustomerOrders AS O ON O.Id=I.CustomerOrderId 
     WHERE P.Active=0 AND P.Processed=0 AND O.ApprovalDate='2012-07-19 00:00:00' 

現在我使用實時數據開始測試系統上查詢(〜每個表250.000行),因爲16小時,它現在正在運行 - 我在查詢中做了一些完全錯誤的事情,還是有辦法加快它的速度?
由於這是一次性任務,因此不需要很快,但幾個小時對我來說似乎很長,並且我希望下一次我希望學習(希望不會發生),我希望有一些反饋如何改進...

回答

3

你可能會殺死查詢。您的更新子查詢與正在更新的表完全不相關。從它的外觀來看,當它完成時,每一個dbo.payments記錄都會具有相同的值。

要打破您的查詢,您可能會發現子查詢自行運行良好。

SELECT TOP 1 I.Id FROM DB.dbo.Invoices AS I 
      WHERE I.CustomerOrderId= 
       (SELECT TOP 1 O.Id FROM DB.dbo.CustomerOrders AS O 
        WHERE O.ExternalOrderNumber='0'+SUBSTRING(
         (SELECT TOP 1 OO.ExternalOrderNumber FROM DB.dbo.CustomerOrders AS OO 
         WHERE OO.Id=I.CustomerOrderId), 1, 10000)) 

這總是一個很大的擔心。

接下來就是它爲表中的每條記錄逐行運行。

您也可以通過選擇從哪裏... id從一個涉及自己的聯合雙向支付。您可以使用以下模式在JOIN子句中引用更新表:

UPDATE P 
.... 
    FROM DB.dbo.Payments AS P 
    JOIN DB.dbo.Invoices AS I ON I.Id=P.InvoiceId 
    JOIN DB.dbo.CustomerOrders AS O ON O.Id=I.CustomerOrderId 
WHERE P.Active=0 AND P.Processed=0 AND O.ApprovalDate='2012-07-19 00:00:00' 

繼續前進,另一個錯誤是使用沒有ORDER BY的TOP。這是要求隨機結果。如果你知道只有一個結果,你甚至不需要TOP。在這種情況下,也許你可以從很多可能的匹配中隨機選擇一個。既然你有三個級別的TOP(1)沒有ORDER BY,你可能只是將它們混合起來(加入),並在它們之間取一個TOP(1)。這將使它看起來像這樣

SET InvoiceId= 
    (SELECT TOP 1 I.Id 
    FROM DB.dbo.Invoices AS I 
    JOIN DB.dbo.CustomerOrders AS O 
     ON I.CustomerOrderId=O.Id 
    JOIN DB.dbo.CustomerOrders AS OO 
     ON O.ExternalOrderNumber='0'+SUBSTRING(OO.ExternalOrderNumber,1,100) 
      AND OO.Id=I.CustomerOrderId) 

但是,正如我在很早的時候提到的,這根本不與主要的FROM子句相關。我們將整個搜索移到主查詢中,以便我們可以使用基於JOIN的集合操作,而不是逐行子查詢。

在我展示最終查詢(完全評論)之前,我認爲你的SUBSTRING應該解決這個邏輯but starts with 0 instead of the current digit。但是,如果這意味着我如何閱讀它,這意味着對於訂單號「5678」,您正在查找「0678」,這也意味着SUBSTRING應該使用2,10000而不是1,10000

UPDATE P 
SET InvoiceId=II.Id 
FROM DB.dbo.Payments AS P 
-- invoices for payments 
JOIN DB.dbo.Invoices AS I ON I.Id=P.InvoiceId 
-- orders for invoices 
JOIN DB.dbo.CustomerOrders AS O ON O.Id=I.CustomerOrderId 
-- another order with '0' as leading digit 
JOIN DB.dbo.CustomerOrders AS OO 
    ON OO.ExternalOrderNumber='0'+substring(O.ExternalOrderNumber,2,1000) 
-- invoices for this other order 
JOIN DB.dbo.Invoices AS II ON OO.Id=II.CustomerOrderId 

-- conditions for the Payments records 
WHERE P.Active=0 AND P.Processed=0 AND O.ApprovalDate='2012-07-19 00:00:00' 

值得注意SQL Server允許UPDATE ..FROM ..JOIN其較少受到其他DBMS,例如支持甲骨文。這是因爲對於Payments中的單行(更新目標),我希望你能看到很明顯它可以有許多II.Id的選擇,從所有的笛卡爾連接中進行選擇。 你會得到一個隨機可能的II.Id.

+0

非常感謝 - 您的查詢在<1s中完成了這項工作,並感謝您的解釋,我也學到了很多東西。 – ChrFin

0

我認爲這樣的事情會更有效率,如果我理解你的查詢的權利。正如我手寫它並沒有運行它,它可能有一些語法錯誤。

UPDATE DB.dbo.Payments 
set InvoiceId=(SELECT TOP 1 I.Id FROM DB.dbo.Invoices AS I 
     inner join DB.dbo.CustomerOrders AS O ON I.CustomerOrderId=O.Id 
     inner join DB.dbo.CustomerOrders AS OO On OO.Id=I.CustomerOrderId 
     and O.ExternalOrderNumber='0'+SUBSTRING(OO.ExternalOrderNumber, 1, 10000))) 
FROM DB.dbo.Payments 
      JOIN DB.dbo.Invoices AS I ON I.Id=Payments.InvoiceId and 
      Payments.Active=0 
      AND Payments.Processed=0 
      AND O.ApprovalDate='2012-07-19 00:00:00' 
      JOIN DB.dbo.CustomerOrders AS O ON O.Id=I.CustomerOrderId 
+0

感謝您的輸入,但新的InvoiceID將始終爲空(錯誤允許爲nto)。 – ChrFin

0

嘗試使用JOIN重寫。這將突出一些問題。以下功能是否也會一樣? (該查詢有所不同,但我想這是你想要做什麼大概)

UPDATE Payments 
    SET InvoiceId= I.Id 
FROM DB.dbo.Payments 
CROSS JOIN DB.dbo.Invoices AS I 
INNER JOIN DB.dbo.CustomerOrders AS O 
    ON I.CustomerOrderId = O.Id 
INNER JOIN DB.dbo.CustomerOrders AS OO 
    ON O.ExternalOrderNumer = '0' + SUBSTRING(OO.ExternalOrderNumber, 1, 10000) 
    AND OO.Id = I.CustomerOrderId 
WHERE P.Active=0 AND P.Processed=0 AND O.ApprovalDate='2012-07-19 00:00:00') 

正如你看到的,有兩個問題比較突出:

  1. 的undonditional付款和發票之間的連接(當然,你已經通過TOP 1聲明發現了這種情況,但是在集合上它仍然是無條件的) - 我不確定這是否真的成爲查詢中的問題。將在我的雖然:)。
  2. 加入一個10000字符的列(SUBSTRING),體現在一個條件中。這是非常低效的。

如果你需要一個一次性的加速,只需要在每個表的查詢,儘量存放在-結果之間,在臨時表中,創建這些臨時表的索引,並使用臨時表來執行更新。