Perché il piano di esecuzione della query SELECT COUNT () include la tabella unita a sinistra?


9

In SQL Server 2012 ho una funzione con valori di tabella con join a un'altra tabella. Devo contare il numero di righe per questa "funzione con valori di tabella". Quando ispeziono il piano di esecuzione, posso vedere la tabella dei join di sinistra. Perché? In che modo la tabella unita a sinistra può influenzare il numero di righe restituite? Mi aspetto che il motore db non debba valutare la tabella dei giunti a sinistra nella query conteggio SELECT (..).

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

Il piano di esecuzione:

inserisci qui la descrizione dell'immagine

La funzione con valori di tabella:

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
)

AGGIORNARE:

Aggiungo un indice univoco per seguire l'idea di Rob Farley:

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

E indicizzato suggerito da DB Engine:

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

([OwnerId] ASC) INCLUDE ([Id], [okresId], [obecId], [pobvodId]) GO

Per semplicità rimuovo la condizione

WHERE Person.ConfirmStatus = 1

dalla funzione di valore tabellare sopra.

Ora il piano di esecuzione è molto più semplice ma tocca ancora la tabella ExternFile:

inserisci qui la descrizione dell'immagine

Forse il server sql non è abbastanza intelligente?

Risposte:


12

Se ForeignId, ForeignTable, IsMainnon si sa che * è univoco in ExternFile, il QO dovrà includere quella tabella per calcolare il conteggio. Ogni volta che più righe corrispondono, il conteggio sarà interessato.

Partecipa alla semplificazione nella
progettazione di SQL Server per la semplificazione (registrazione di SQLBits)


* L'ottimizzatore attualmente non riconosce gli indici univoci filtrati come unici

AGGIORNAMENTO (tramite OP) : la soluzione consiste nel modificare la riga nella query da LEFT JOIN (che può produrre più righe):

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

applicare ESTERNO con TOP (che produce una riga e non influisce su COUNT)

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

La query ora è più efficace. Non è stato possibile aggiungere un indice univoco, poiché i valori non erano univoci, erano unici solo per la combinazione nella condizione e questo non è considerato unico come menzionato sopra.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.