[英]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')
如您所見,突出了兩個問題:
TOP 1
語句捕獲了,但是按條件設置,它仍然是無條件的)-我不確定這是否確實是您的查詢中的問題。 雖然會在我的:)。 SUBSTRING
( SUBSTRING
)。 這是非常低效的。 如果需要一次性加速,只需對每個表進行查詢,嘗試將中間結果存儲在臨時表中,在這些臨時表上創建索引,然后使用臨時表執行更新。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.