J'ai du mal à comprendre où l'estimation d'une rangée vient dans un plan d'exécution.
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]
Mon problème est autour de Nodeid 26, l'opérateur scalaire:
Je ne suis pas sûr de la manière dont l'estimation de la rangée de 5 est générée - cela semble ensuite en cascade dans le plan de la plupart des autres opérateurs - les opérateurs de boucle imbriqués estimés compte plus que le plan semble tout indiquer à environ 5 ans. 35k réel.
Pourquoi l'opérateur scalaire serait-il nourri d'une estimation de ~ 14 000 lignes, puis estimer une sortie de 5? Est-ce un problème ou un hareng rouge? Est-ce quelque chose à voir avec les conversions qu'il effectue? Je peux comprendre que l'affectation d'une jointure, mais pourquoi cela affecterait-il la production de la conversion?
Pourquoi l'opérateur scalaire serait-il nourri d'une estimation de ~ 14 000 lignes, puis estimer une sortie de 5? Est-ce un problème ou un hareng rouge?
Ceci est contre-intuitif, mais une conséquence naturelle de la manière dont l'optimiseur de requêtes explore l'espace plan. Comme il génère des alternatives neuves, logiquement équivalentes, d'un opérateur de plan ou d'un sous-arbre particulier, il peut être nécessaire de tirer une nouvelle estimation de la cardinalité.
Étant donné que l'estimation est un processus statistique, rien ne garantit que les estimations dérivées sur des arbres équivalents logiquement (mais physiquement différents) produiront le même nombre, en fait dans la majorité des cas, ils ne le feront pas. Il n'y a normalement aucun moyen évident de préférer une estimation sur une autre.
Lorsque l'optimisation atteint son point de fin, les meilleures alternatives physiques trouvées sont "cousues ensemble" pour former le plan final. Ce plan peut avoir des "incohérences" en conséquence, simplement parce que les estimations ont été calculées sur différentes structures logiques à des moments différents. Par exemple, un scalaire de calcul peut avoir commencé comme un agrégat logique, qui a ensuite été simplifié.
J'ai écrit plus à ce sujet dans mon article vues indexées et statistiques .
Si vous soupçonnez que la cardinalité mal estimée affecte le choix du plan (de manière importante), vous pouvez choisir de diviser manuellement la requête ou d'utiliser des allusions. Matérialiser le petit groupe intermédiaire à ou autour du nœud 27 dans une table temporaire peut bien améliorer la qualité du plan, car l'optimiseur peut voir une cardinalité précise à ce point et créer des statistiques automatiques. L'auteur de requêtes peut également choisir d'ajouter indexation à la table temporaire.
Est-ce quelque chose à voir avec les conversions qu'il effectue? Je peux comprendre que l'affectation d'une jointure, mais pourquoi cela affecterait-il la production de la conversion?
Pas généralement, non, bien qu'il soit préférable d'éviter les conversions dans la mesure du possible. Certes de conversion peut affecter l'estimation de la cardinalité, mais il y a peu d'indication que c'est la cause ici.