Берем старый добрый MSSQL 2000 и прогоняем вот такой запрос по базе Northwind:
Code: Select all
declare @City varchar(100)
set @City = 'Sao Paulo'
select distinct emp.EmployeeID,emp.LastName,emp.FirstName,emp.Title,emp.HireDate
from Employees emp
inner join Orders o on o.EmployeeID = emp.EmployeeID
inner join Customers c on c.CustomerID = o.CustomerID and c.City = @City
inner join [Order Details] od on od.OrderID = o.OrderID
and od.Discount > (select avg(Discount) from [Order Details])
и в плане присутствует совершенно законный bookmark lookup. По моим представлениям ситуация следующая: Сначала идет выборка Customer'ов по City, потом отбираются все заказы, в которых присутствуе нужный CustomerID, а затем надо отобрать всех Employeer'ов по EmployeeID совпадающим с таким же полем в уже отобранных заказах. Но к этому моменту EmployeeID в отобранных заказах не известен, за ним и устраивается bookmark lookup.
Спасает от bookmark lookup, естественно, построение дополнительного составного индекса по Orders(CustomerID, EmployeeID), но вопрос не в этом.
Когда я прогнал этот запрос в Юконе, то никакгого bookmark lookup'а я не обнаружил. А именно план запроса примерно таков:
Code: Select all
|--Stream Aggregate(GROUP BY:([emp].[EmployeeID]) DEFINE:([emp].[LastName]=ANY([Northwind].[dbo].[Employees].[LastName] as [emp].[LastName]), [emp].[FirstName]=ANY([Northwind].[dbo].[Employees].[FirstName] as [emp].[FirstName]), [emp].[Title]=ANY([Northw
|--Nested Loops(Inner Join, OUTER REFERENCES:([o].[OrderID], [Expr1011]))
|--Nested Loops(Inner Join, OUTER REFERENCES:([o].[EmployeeID]))
| |--Sort(ORDER BY:([o].[EmployeeID] ASC))
| | |--Nested Loops(Inner Join, OUTER REFERENCES:([o].[OrderID]) WITH PREFETCH)
| | |--Nested Loops(Inner Join, OUTER REFERENCES:([c].[CustomerID]))
| | | |--Nested Loops(Inner Join)
| | | | |--Compute Scalar(DEFINE:([Expr1011]=CASE WHEN [Expr1019]=(0) THEN NULL ELSE [Expr1020]/CONVERT_IMPLICIT(float(53),[Expr1019],0) END))
| | | | | |--Stream Aggregate(DEFINE:([Expr1019]=Count(*), [Expr1020]=SUM([Northwind].[dbo].[Order Details].[Discount])))
| | | | | |--Clustered Index Scan(OBJECT:([Northwind].[dbo].[Order Details].[PK_Order_Details]))
| | | | |--Index Seek(OBJECT:([Northwind].[dbo].[Customers].[City] AS [c]), SEEK:([c].[City]=CONVERT_IMPLICIT(nvarchar(100),[@City],0)) ORDERED FORWARD)
| | | |--Index Seek(OBJECT:([Northwind].[dbo].[Orders].[CustomerID] AS [o]), SEEK:([o].[CustomerID]=[Northwind].[dbo].[Customers].[CustomerID] as [c].[CustomerID]) ORDERED FORWARD)
| | |--Compute Scalar(DEFINE:([o].[EmployeeID]=[Northwind].[dbo].[Orders].[EmployeeID] as [o].[EmployeeID]))
| | |--Clustered Index Seek(OBJECT:([Northwind].[dbo].[Orders].[PK_Orders] AS [o]), SEEK:([o].[OrderID]=[Northwind].[dbo].[Orders].[OrderID] as [o].[OrderID]) ORDERED FORWARD)
| |--Clustered Index Seek(OBJECT:([Northwind].[dbo].[Employees].[PK_Employees] AS [emp]), SEEK:([emp].[EmployeeID]=[Northwind].[dbo].[Orders].[EmployeeID] as [o].[EmployeeID]) ORDERED FORWARD)
|--Clustered Index Seek(OBJECT:([Northwind].[dbo].[Order Details].[PK_Order_Details] AS [od]), SEEK:([od].[OrderID]=[Northwind].[dbo].[Orders].[OrderID] as [o].[OrderID]), WHERE:(CONVERT_IMPLICIT(float(53),[Northwind].[dbo].[Order Details].[Di
Вот я и не могу понять, что в данном случае делает оптимизатор и что означает Compute Scalar(DEFINE:([o].[EmployeeID]=[Northwind].[dbo].[Orders].[EmployeeID] as [o].[EmployeeID]))
Я конечно не знаю в тех ли попугаях меряется стоимость запроса, но этот план гораздо эффективнее чем в восьмерке...
Все здорово, но вот этот запрос:
Code: Select all
declare @region varchar(100)
set @region = 'Eastern'
declare @OrderEnd datetime,@OrderBeg datetime
set @OrderBeg = '1996-03-19 00:00:00.000'
set @OrderEnd = '1998-05-19 00:00:00.000'
select distinct emp.EmployeeID,emp.LastName,emp.FirstName,emp.Title,emp.HireDate
from Employees emp
inner join EmployeeTerritories et on emp.EmployeeID = et.EmployeeID
inner join Territories t on t.TerritoryID = et.TerritoryID
inner join Region r on r.RegionID = t.RegionID and r.RegionDescription = @region
inner join Orders o on o.EmployeeID = emp.EmployeeID and o.OrderDate between @OrderBeg and @OrderEnd
Порождает план уже хуже чем в восьмерке, а именно, присутствуют два table scan, которых восьмерка успешно избегала. Ну и стоимость естественно больше.
Причем похоже тут Юкон руководствуется теми же принципами, что и в предыдущем случае..
Это из-за того, что альфа?
P. S.
На самом деле еще много вопросов по Юкону, Tengiz, Вас можно терзать или это еще совсем закрытая информация?