Estou lutando para entender de onde vem uma estimativa de linha em um plano de execução.
declare
@BatchKey INT = 1, @ParentBatchKey INT = 1,
@QuoteRef varchar(50) = 'Q00018249',
@MpanRef varchar(50) = '1425431100004'
SELECT DISTINCT
ISNULL(c.ContractReference,-1) AS [ContractReference] ,
ISNULL(d_cd.ContractDetailsKey,-1) AS [ContractDetailsKey] ,
-1 AccountManagerKey,
-1 SegmentationKey,
ISNULL(d_tpi.TpiKey,-1) AS [TpiKey] ,
ISNULL(d_cu.CustomerKey,-1) AS [CustomerKey] ,
ISNULL(d_p.ProductKey,-1) AS [ProductKey] ,
-1 as PayPointKey,
-1 AS [GspBandingKey], --Not used in Junifer ESOB
ISNULL(d_pps.[ProductPricingStructureKey],-1) AS [ProductPricingStructureKey],
ISNULL(d_tou.TouBandingKey,-1) AS [PricingStructureBandingKey],
-1 AS [VolumePointCategoryKey],
ISNULL(d_ppc.PowerPeriodCategoryKey,-1) AS [PowerPeriodCategoryKey],
ISNULL(d_pcat.[PriceComponentAggregationTypeKey],-1) AS [PriceComponentAggregationTypeKey],
-1 AS [MarginRateBandingKey], --Not used in Junifer ESOB
-1 AS [DuosUrcBandingKey], --Not used in Junifer ESOB
-1 AS [ConsumptionToleranceKey],
ISNULL(d_mp.MeterPointKey,-1) AS [MeterPointKey] ,
ISNULL(d.DateKey,-1) AS [ForecastDateKey] ,
-1 AS [ForecastEFADateKey],
ISNULL(d_cw.DateKey,-1) AS [ContractWonDateKey] ,
ISNULL(f.SiteVolumeKwh,0) AS [SiteVolume] ,
ISNULL(f.GspVolumeKwh,0) AS [GspVolume] ,
ISNULL(f.NbpVolumeKwh,0) AS [NbpVolume],
@BatchKey,
@ParentBatchKey,
CAST(f.ForecastKey as NVARCHAR(100)) AS [SourceId]
FROM
[Electricity].[Forecast] f
INNER JOIN Electricity.ContractMeterPoint cmp ON cmp.MeterPointKey = f.MeterPointKey and cmp.ContractKey = f.ContractKey
INNER JOIN Electricity.Contract c on c.ContractKey = cmp.ContractKey
INNER JOIN Electricity.MeterPoint mp ON mp.MeterPointKey = cmp.MeterPointKey
--INNER JOIN Electricity.ContractMeterPoint cmp ON cmp.MeterPointKey = mp.MeterPointKey and cmp.ContractKey = c.ContractKey
INNER JOIN Electricity.ProductBundle pb ON c.ProductBundleKey = pb.ProductBundleKey
LEFT JOIN Electricity.Quote q ON c.QuoteKey = q.QuoteKey
LEFT JOIN Gdf.Tender t ON q.TenderKey = t.TenderKey
LEFT JOIN Gdf.Customer cu ON q.CustomerKey = cu.CustomerKey
LEFT JOIN Electricity.ProductBundleAggregationType pbat ON pbat.ProductName = pb.BundleName
LEFT JOIN Dimensional_DW.DimensionElectricity.Product d_p ON d_p.ProductDurableKey = pb.ProductBundleKey
LEFT JOIN Dimensional_DW.Dimension.Tpi d_tpi ON d_tpi.TpiDurableKey = c.TpiKey
LEFT JOIN Dimensional_DW.DimensionElectricity.ProductPricingStructure d_pps ON d_pps.ProductPricingStructureDurableKey = f.PriceStructureKey
LEFT JOIN Dimensional_DW.DimensionElectricity.TouBanding d_tou ON d_tou.TouBandingDurableKey = f.PriceRateKey
LEFT JOIN Dimensional_DW.DimensionElectricity.MeterPoint d_mp ON d_mp.MeterPointDurableKey = cmp.MeterPointKey
LEFT JOIN Dimensional_DW.DimensionElectricity.PriceComponentAggregationType d_pcat ON d_pcat.[TnuosAggregationType] =pbat.[TNUoSAggType] AND d_pcat.[DuosAggregationType] =pbat.[DUoSFixedAvailAggType] AND d_pcat.[DuosUrcAggregationType] =pbat.[DUoSURCAggType] AND d_pcat.[BsuosAggregationType] =pbat.[BSUoSAggType] AND d_pcat.[ROAggregationType] =pbat.[ROAggType]
LEFT JOIN Dimensional_DW.Dimension.Date AS d ON d.DateKey = CAST(CONVERT(NVARCHAR(8), f.DeliveryDate, 112) AS INT)
LEFT JOIN Dimensional_DW.Dimension.Date AS d_cw ON d_cw.DateKey = CAST(CONVERT(NVARCHAR(8), c.QuoteWonDate, 112) AS INT)
LEFT JOIN Dimensional_DW.DimensionElectricity.PowerPeriodCategory d_ppc ON d_ppc.HhPeriod = f.Period
LEFT JOIN Dimensional_DW.Dimension.Customer d_cu ON d_cu.CustomerDurableKey = cu.CustomerKey
LEFT JOIN Dimensional_DW.DimensionElectricity.ContractDetails d_cd ON d_cd.ContractDetailsDurableKey = c.ContractKey
WHERE 1=1
and c.ContractReference = @QuoteRef
and c.QuoteWonDate IS NOT NULL
and c.QuoteKey <> -1
--(SELECT distinct C.ContractKey FROM Electricity.Contract WHERE ContractReference = @QuoteRef and c.QuoteWonDate IS NOT NULL and c.QuoteKey <> -1)
--(SELECT distinct C1.ContractKey FROM Electricity.Contract c1 WHERE c1.ContractReference = @QuoteRef and c1.QuoteWonDate IS NOT NULL and c1.QuoteKey <> -1)
and mp.MpanID = @MpanRef
--and c.ContractKey = 18235
--and d.DateKey = 20180522
order by [ForecastDateKey]
Meu problema é em torno do nodeId 26, o operador escalar:
Não tenho certeza de como a estimativa de linha de 5 está sendo gerada - isso parece cascatear o plano para a maioria dos outros operadores - as contagens de execução estimadas dos operadores de loop aninhado mais abaixo no plano parecem indicar ~ 5 estimado, então ~ 35k reais.
Por que o operador escalar seria alimentado com uma estimativa de ~14.000 linhas e, em seguida, estimaria uma saída de 5? Isso é um problema ou um arenque vermelho? Tem alguma coisa a ver com as conversões que está realizando? Eu posso entender isso afetando uma junção, mas por que isso afetaria a saída da conversão?
Isso é contra-intuitivo, mas uma consequência natural da maneira como o otimizador de consulta explora o espaço do plano. À medida que gera novas alternativas logicamente equivalentes para um determinado operador de plano ou subárvore, pode ser necessário derivar uma nova estimativa de cardinalidade.
Como a estimativa é um processo estatístico, não há garantia de que estimativas derivadas de árvores logicamente equivalentes (mas fisicamente diferentes) produzirão o mesmo número; na verdade, na maioria dos casos, não. Normalmente, não há uma maneira óbvia de preferir uma estimativa a outra.
Quando a otimização atinge seu ponto final, as melhores alternativas físicas encontradas são 'costuradas' para formar o plano final. Esse plano pode ter 'inconsistências' como resultado, simplesmente porque as estimativas foram calculadas em diferentes estruturas lógicas em momentos diferentes. Por exemplo, um Compute Scalar pode ter começado como um agregado lógico, que foi posteriormente simplificado.
Eu escrevi mais sobre isso em meu artigo Visualizações e estatísticas indexadas .
Se você suspeitar que a estimativa incorreta de cardinalidade está afetando a escolha do plano (de maneira importante), você pode optar por dividir a consulta manualmente ou usar dicas. A materialização do pequeno conjunto intermediário no nó 27 ou ao redor dele em uma tabela temporária pode melhorar a qualidade do plano, pois o otimizador pode ver a cardinalidade precisa nesse ponto e criar estatísticas automáticas. O escritor de consulta também pode optar por adicionar indexação à tabela temporária.
Normalmente não, não, embora seja melhor evitar conversões sempre que possível. Certamente as conversões podem afetar a estimativa de cardinalidade, mas há pouca indicação de que seja a causa aqui.