Почему план выполнения запроса SELECT COUNT () включает левую объединенную таблицу?

9

В SQL Server 2012 у меня есть табличная функция с соединением с другой таблицей, мне нужно подсчитать количество строк для этой «табличной функции». Когда я проверяю план выполнения, я вижу левую таблицу соединений. Почему? Как левая объединенная таблица может влиять на количество возвращаемых строк? Я ожидаю, что движку БД не нужно оценивать левую объединенную таблицу в запросе SELECT count (..).

Select count(realtyId) FROM [dbo].[GetFilteredRealtyFulltext]('"praha"')

План выполнения:

введите описание изображения здесь

Табличная функция:

CREATE FUNCTION [dbo].[GetFilteredRealtyFulltext]
(@criteria nvarchar(4000))
RETURNS TABLE
AS
RETURN (SELECT 
realty.Id AS realtyId,
realty.OwnerId,
realty.Caption AS realtyCaption,
realty.BusinessCategory,
realty.Created,
realty.LastChanged,
realty.LastChangedType,
realty.Price,
realty.Pricing,
realty.PriceCurrency,
realty.PriceNote,
realty.PricePlus,
realty.OfferState,
realty.OrderCode,
realty.PublishAddress,
realty.PublishMap,
realty.AreaLand,
realty.AreaCover,
realty.AreaFloor,
realty.Views,
realty.TopPoints,
realty.Radius,
COALESCE(realty.Wgs84X, ruian_cobce.Wgs84X, ruian_obec.Wgs84X) as Wgs84X,
COALESCE(realty.Wgs84Y, ruian_cobce.Wgs84Y, ruian_obec.Wgs84Y) as Wgs84Y,
realty.krajId,
realty.okresId,
realty.obecId,
realty.cobceId,
IsNull(CONVERT(int,realty.Ranking),0) as Ranking,

realty.energy_efficiency_rating,
realty.energy_performance_attachment,
realty.energy_performance_certificate,
realty.energy_performance_summary,

Category.Id AS CategoryId,
Category.ParentCategoryId,
Category.WholeName,
okres.nazev AS okres,
ruian_obec.nazev AS obec,
ruian_cobce.nazev AS cobce,
ExternFile.ServerPath,
Person.ParentPersonId,
( COALESCE(ftR.Rank,0) + COALESCE(ftObec.Rank,0) + COALESCE(ftOkres.Rank,0) + COALESCE(ftpobvod.Rank,0)) AS FtRank

FROM realty
JOIN Category ON realty.CategoryId = Category.Id
LEFT JOIN ruian_cobce ON realty.cobceId = ruian_cobce.cobce_kod
LEFT JOIN ruian_obec ON realty.obecId = ruian_obec.obec_kod
LEFT JOIN okres ON realty.okresId = okres.okres_kod
LEFT JOIN ExternFile ON realty.Id = ExternFile.ForeignId AND ExternFile.IsMain = 1 AND ExternFile.ForeignTable = 5
INNER JOIN Person ON realty.OwnerId = Person.Id
Left JOIN CONTAINSTABLE(Realty, *, @criteria) ftR ON realty.Id = ftR.[Key] 
Left JOIN CONTAINSTABLE(ruian_obec, *, @criteria) ftObec ON realty.obecId = ftObec.[Key] 
Left JOIN CONTAINSTABLE(Okres, *, @criteria) ftOkres ON realty.okresId = ftOkres.[Key]
Left JOIN CONTAINSTABLE(pobvod, *, @criteria) ftpobvod ON realty.pobvodId = ftpobvod.[Key]
WHERE Person.ConfirmStatus = 1
AND ( COALESCE(ftR.Rank,0) + COALESCE(ftObec.Rank,0) + COALESCE(ftOkres.Rank,0) + COALESCE(ftpobvod.Rank,0))  > 0
)

ОБНОВИТЬ:

Я добавляю уникальный индекс, чтобы следовать идее Роба Фарли:

 Create unique nonclustered index ExternFileIsMainUnique ON ExternFile(ForeignId) WHERE IsMain = 1 AND ForeignTable = 5

И индексируется по предложению БД Engine:

CREATE NONCLUSTERED INDEX [RealtyOwnerLocation] ON [dbo].[Realty]

([OwnerId] ASC) ВКЛЮЧИТЬ ([Id], [okresId], [obecId], [pobvodId]) GO

Для простоты снимаю условие

WHERE Person.ConfirmStatus = 1

из табличной функции выше.

Теперь план выполнения намного проще, но он все еще касается таблицы ExternFile:

введите описание изображения здесь

Может быть, сервер sql недостаточно умен?

Томас Кубес
источник

Ответы:

12

Если ForeignId, ForeignTable, IsMainнеизвестно, * является ли он уникальным ExternFile, то QO необходимо будет включить эту таблицу для расчета количества. Каждый раз, когда совпадают несколько строк, это влияет на количество.

Объединение Упрощение в SQL Server
Designing для упрощения (запись SQLBits)


* В настоящее время оптимизатор не распознает отфильтрованные уникальные индексы как уникальные

UPDATE (по OP) : решение состоит в том, чтобы изменить строку в запросе с LEFT JOIN (которая может создать несколько строк):

LEFT JOIN ExternFile ON realty.Id = ExternFile.ForeignId AND ExternFile.IsMain = 1 AND ExternFile.ForeignTable = 5

на OUTTER APPLY с TOP (который производит одну строку и не влияет на COUNT)

OUTER APPLY (SELECT TOP (1) ServerPath FROM ExternFile WHERE ForeignId = realty.Id AND IsMain = 1 AND ForeignTable = 5) AS ExternFile

Запрос теперь более эффективен. Добавление уникального индекса не может быть выполнено, поскольку значения не были уникальными, они были уникальными только для комбинации в условии, и это не считается уникальным, как упомянуто выше.

Роб Фарли
источник