《SQL Server 2005技术内幕:T-SQL查询》上的一处错误
《SQL Server 2005技术内幕:T-SQL查询》一书的第7章,“使用TOP和Apply解决常见问题”一节中,介绍了多种方式获得每个销售员的最新订单。
其中第三种方式是这样的。书上只是说这是个“糟糕计划”,其实是糟糕T-SQL代码造成的。而这个方案其实可以优化成为作者给出的三个用于SQL Server 2000的方案中最好的一个。
SELECT OrderID, CustomerID, E.EmployeeID, OrderDate, RequiredDate
FROM dbo.Employees AS E
JOIN dbo.Orders AS O1
ON OrderID IN
(SELECT TOP(3) OrderID
FROM dbo.Orders AS O2
WHERE O2.EmployeeID = E.EmployeeID
ORDER BY OrderDate DESC, OrderID DESC);
FROM dbo.Employees AS E
JOIN dbo.Orders AS O1
ON OrderID IN
(SELECT TOP(3) OrderID
FROM dbo.Orders AS O2
WHERE O2.EmployeeID = E.EmployeeID
ORDER BY OrderDate DESC, OrderID DESC);
有看出问题吗?再看看它上面的图。
注意第二个Nested Loops上有一个感叹号。知道是什么原因了吗?如果还没有看出问题的话,可以参考书上271页,讲述联接的部分。
这个警告是“没有联接谓词”,说明白些,就是指上面的SQL代码中的JOIN没有相关的ON子句。注意不是说没有ON子句,是说没有与JOIN相关的ON子句。
没有ON子句的JOIN就变成了CROSS JOIN。能不慢吗?
大家有兴趣可以自己想一想如何优化这个SQL。我在AdventureWorks上做的测试,运行速度提高近100倍。
答案如下:
SELECT OrderID, CustomerID, E.EmployeeID, OrderDate, RequiredDate
FROM dbo.Employees AS E
JOIN dbo.Orders AS O1
ON O1.EmployeeID = E.EmployeeID AND OrderID IN
(SELECT TOP(3) OrderID
FROM dbo.Orders AS O2
WHERE O2.EmployeeID = E.EmployeeID
ORDER BY OrderDate DESC, OrderID DESC);
FROM dbo.Employees AS E
JOIN dbo.Orders AS O1
ON O1.EmployeeID = E.EmployeeID AND OrderID IN
(SELECT TOP(3) OrderID
FROM dbo.Orders AS O2
WHERE O2.EmployeeID = E.EmployeeID
ORDER BY OrderDate DESC, OrderID DESC);
其执行计划如下图所示:
书中有错误是很正常的。读一本书,其实可以把找出错误的个数,作为衡量书是不是真正读懂的一个标准。我自己读书就有一个习惯,把书的第一个空白页当勘误表。一般发现翻译错误、字词遗漏什么的也不值当发上来,不过这个错误感觉就比较严重了。