簡體   English   中英

如何提高SQL查詢性能

[英]How to improve SQL Query Performance

我具有以下數據庫結構(簡化):

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


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


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

每個客戶訂單都有一個發票,每個發票可以有多個付款。 ExternalStoreOrderNumber是對我們從中導入訂單的外部合作伙伴商店的訂單的引用,以及該導入發生時的時間戳的ApprovalDate

現在我們有一個問題,就是我們匯入了錯誤的商品,需要根據以下邏輯將一些付款更改為其他發票(幾筆錢,所以手工做得太快了):
搜索訂單發票,該發票的外部編號與當前編號相同,但以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行)在測試系統上啟動了該查詢,並且該查詢自16h開始運行-我在查詢中做錯了什么嗎?或者是否可以加快查詢速度?
它不是必須非常快,因為它是一項一次性的任務,但是對我來說似乎要花幾個小時,而且由於我想學習下次(希望不會發生),我想獲得一些改進的反饋...

您不妨取消該查詢。 您的更新子查詢與要更新的表完全不相關。 從外觀上看,完成后,每個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(例如Oracle)所支持的較少。 這是因為,對於Payments中的一行(更新目標),我希望您可以看到很明顯,它可以從所有笛卡爾聯接中選擇II.Id。 您將獲得一個隨機的II.Id。

我認為,如果我了解您的查詢正確,這樣的事情會更有效。 由於我是手動編寫但未運行,因此可能存在一些語法錯誤。

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

嘗試使用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. 付款和發票之間無可辯駁的聯接(當然,您已經被TOP 1語句捕獲了,但是按條件設置,它仍然是無條件的)-我不確定這是否確實是您的查詢中的問題。 雖然會在我的:)。
  2. 條件中包含的10000個字符的列上的SUBSTRINGSUBSTRING )。 這是非常低效的。

如果需要一次性加速,只需對每個表進行查詢,嘗試將中間結果存儲在臨時表中,在這些臨時表上創建索引,然后使用臨時表執行更新。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM