使用AdventureWorks,下面列出的是For each Product get any 1 row of its associated SalesOrderDetail
的查询。
使用cross apply
需要14000毫秒。等效的row_number
版本只需要70毫秒(快200倍)。
cross apply
也比所有Products和SalesOrderDetails的简单inner join
慢,返回121317行(当受TOP 1限制时为266行)。
我更喜欢这种查询的cross apply
语法,因为它比row_number
版本更清晰。但显然cross apply
版本使用效率非常低的执行计划而且速度太慢而无法使用。
在我看来,查询无法按预期工作。运行这个简单的查询不应该花费14秒。我在其他情况下使用了cross apply
,从未遇到过这么慢的事情。我的问题是:这个使查询优化器混淆的特定查询怎么样?是否有任何查询提示可用于帮助它使用最佳执行计划?正如@pacreely所建议的,我已经为每个查询添加了统计信息。
--CROSS APPLY ~14000ms
SELECT P.ProductID
,P.Name
,P.ProductNumber
,P.Color
,SOD.SalesOrderID
,SOD.UnitPrice
,SOD.UnitPriceDiscount
,SOD.LineTotal
FROM Production.Product P
CROSS APPLY ( SELECT TOP 1
*
FROM Sales.SalesOrderDetail S
WHERE S.ProductID = P.ProductID ) SOD;
--ROW_NUMBER ~70ms
SELECT *
FROM ( SELECT P.ProductID
,P.Name
,P.ProductNumber
,P.Color
,SOD.SalesOrderID
,SOD.UnitPrice
,SOD.UnitPriceDiscount
,SOD.LineTotal
,ROW_NUMBER() OVER ( PARTITION BY P.ProductID ORDER BY P.ProductID ) RowNum
FROM Production.Product P
INNER JOIN Sales.SalesOrderDetail SOD ON SOD.ProductID = P.ProductID ) X
WHERE X.RowNum = 1;
--Simple INNER JOIN ~400ms (121317 rows)
SELECT P.ProductID
,P.Name
,P.ProductNumber
,P.Color
,SOD.SalesOrderID
,SOD.UnitPrice
,SOD.UnitPriceDiscount
,SOD.LineTotal
FROM Production.Product P
INNER JOIN Sales.SalesOrderDetail SOD ON SOD.ProductID = P.ProductID;
也许与此问题有关,没有SalesOrderDetail.LineTotal的cross apply
快10倍。
--CROSS APPLY (Without LineTotal) ~1200ms
SELECT P.ProductID
,P.Name
,P.ProductNumber
,P.Color
,SOD.SalesOrderID
,SOD.SalesOrderDetailID
,SOD.CarrierTrackingNumber
,SOD.OrderQty
,SOD.ProductID
,SOD.SpecialOfferID
,SOD.UnitPrice
,SOD.UnitPriceDiscount
,SOD.rowguid
,SOD.ModifiedDate
FROM Production.Product P
CROSS APPLY ( SELECT TOP 1
*
FROM Sales.SalesOrderDetail S
WHERE S.ProductID = P.ProductID ) SOD;
交叉应用统计信息
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
(266 row(s) affected)
Table 'SalesOrderDetail'. Scan count 1, logical reads 363114, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Product'. Scan count 1, logical reads 15, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
(1 row(s) affected)
SQL Server Execution Times:
CPU time = 15688 ms, elapsed time = 16397 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
Row_number统计信息:
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
(266 row(s) affected)
Table 'Product'. Scan count 9, logical reads 40, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'SalesOrderDetail'. Scan count 9, logical reads 1371, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Workfile'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
(1 row(s) affected)
SQL Server Execution Times:
CPU time = 360 ms, elapsed time = 266 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
答案 0 :(得分:2)
使用
运行查询SET STATISTICS IO ON
您会看到CROSS APPLY可能会生成更多的Read。 这是因为您正在对Sales.SalesOrderDetails表进行多次/重复读取
此外,不要假设RowNumber查询“更快”。 SQL已经确定它是一个昂贵的查询,所以它已经并行并使用多个处理器“快速但资源昂贵”。 使用
运行查询SET STATISTICS TIME ON
查看CPU时间而不是经过的时间,这将为您提供查询的真实速度。
当您查看每个查询的执行计划时,请检查Select组件的详细信息。查询有一个总成本度量。如果成本大于服务器的最大并行度(默认为5,除非您的DBA更改它),那么sql将生成并行查询计划以改善经过时间。
答案 1 :(得分:1)
尝试将四个语句的执行计划一起作为一个批处理,并查看报告的"相对于批处理的百分比"对于每一个都类似于你的时间。可能是您有一些过时的统计数据。
答案 2 :(得分:1)
感谢您的所有建议。正如pacreely所建议我检查并发现LineTotal实际上是一个计算列。所以有意义的是,当每行重复计算时,它会减慢一切。但是如上所示,即使没有LineTotal,它仍然太慢。然后引导我从bool operator ==(const node &X, constnode &Y)
{
return (X.X == X.Y && Y.X == Y.Y);
}
子句中删除除Id之外的所有列。最后,我添加了一个内连接来检索所有需要的列。此版本的cross apply
查询与cross apply
查询
row_number