[英]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.