No SQL Server 2012, tenho uma função com valor de tabela com junção a outra tabela que preciso contar o número de linhas para esta 'função com valor de tabela'. Quando inspeciono o plano de execução, posso ver a tabela de junção à esquerda. Por quê? Como a tabela unida à esquerda pode influenciar o número de linhas retornadas? Eu esperaria que o mecanismo de banco de dados não precisasse avaliar a tabela de articulação esquerda na consulta SELECT count(..).
Select count(realtyId) FROM [dbo].[GetFilteredRealtyFulltext]('"praha"')
O plano de execução:
A função com valor de tabela:
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
)
ATUALIZAR:
Eu adiciono um índice exclusivo para seguir a ideia de Rob Farley:
Create unique nonclustered index ExternFileIsMainUnique ON ExternFile(ForeignId) WHERE IsMain = 1 AND ForeignTable = 5
E indexado sugerido pelo DB Engine:
CREATE NONCLUSTERED INDEX [RealtyOwnerLocation] ON [dbo].[Realty]
( [OwnerId] ASC ) INCLUDE ( [Id], [okresId], [obecId], [pobvodId]) GO
Para simplificar, removo a condição
WHERE Person.ConfirmStatus = 1
da função de valor tabelada acima.
Agora o plano de execução é muito mais simples, mas ainda toca na tabela ExternFile:
Talvez o servidor SQL não seja inteligente o suficiente?
Se
ForeignId, ForeignTable, IsMain
não for conhecido* por ser exclusivo emExternFile
, o QO precisará incluir essa tabela para calcular a contagem. Sempre que houver correspondência de várias linhas, a contagem será afetada.Simplificação de junção no SQL Server
Projetando para simplificação (gravação de SQLBits)
* O otimizador atualmente não reconhece índices únicos filtrados como únicos
ATUALIZAÇÃO (por OP) : A solução é alterar a linha na consulta de LEFT JOIN (que pode produzir várias linhas):
para OUTER APPLY com TOP (que produz uma linha e não afeta COUNT)
A consulta agora é mais eficaz. Não foi possível adicionar um índice único, porque os valores não eram únicos, eles eram únicos apenas para combinação na condição e isso não é considerado único como mencionado acima.