Cre*_*ian 4 join sql-server t-sql execution-plan
在一周内,我在批处理作业中遇到过几次糟糕的执行计划,为了避免强制执行计划,我继续添加本地连接提示(当这些连接类型是好的和坏的执行计划之间的区别时)。通过这种方式,我可以让 SQL Server 选择大部分计划,同时强制执行我知道的少数连接来完成查询。
在下面的执行计划中,我想强制连接类型有些相同,因此也会对这些使用本地连接提示。但是,我想知道我是否也能够触发执行计划中的其他操作,例如:
项目清单
这些操作是我可以选择的,还是取决于查询期间选择的连接类型/顺序?
这两个计划都是由从查询存储中提取的 XML 创建的。
好的执行计划:https : //www.brentozar.com/pastetheplan/?id=HyYMn7K2V
错误的执行计划:https : //www.brentozar.com/pastetheplan/?id=Hka6i7Yh4
我想强制连接类型有点相同,因此也会对这些使用本地连接提示
添加连接提示应该是最后的手段。应该有办法重写查询/添加索引以获得更一致的结果。
这些计划也是估计的执行计划,在这种情况下,只有您知道实际查询的执行情况。
如果问题是参数嗅探,那OPTION(RECOMPILE)
将是最简单的解决方案。
是LEFT JOIN
唯一用于过滤的吗?ANOT EXISTS
可能会更好地提前过滤。
说了这么多,鉴于所提供的信息有限,这里有一些可能的快速重写。
将 #1 重写LEFT JOIN
为NOT EXISTS
将OPTION(RECOMPILE)
被添加到获得基于提供的参数更好的估计。
INSERT INTO dbo.cte_MDTForsikringssum
SELECT DISTINCT
mdtp.AvtaleNummer
,mdtp.MedlemskapNummer
,mdtp.Dekningstype
,mdtp.StartAlder
,mdtp.OpphorsAlder
,mdtp.PeriodeStartDato AS GjelderFraDato
,NULL GjelderTilDato
,mdtp.AjourholdDato AS EndretDato
,CONVERT(DECIMAL(18,8), 0) AS Forsikringssum
,0 AS Avkortningsfaktor
,0 AS PensjonsgivendeGrunnlag
,0 AS Folketrygd
,mdtp.Kjorenr_k
FROM [BMPERSIST].vips.impMedlemskapDekningTrinnPremie_full mdtp
INNER JOIN
(SELECT
AvtaleNummer,
MedlemskapNummer,
Dekningstype,
StartAlder,
OpphorsAlder,
YEAR(PeriodeStartDato) AS PeriodeStartDatoAar,
MONTH(PeriodeStartDato) AS PeriodeStartDatoManed,
MAX(AjourholdDato) AS maxAjourholdDato
FROM [BMPERSIST].vips.impMedlemskapDekningTrinnPremie_full
WHERE --PeriodeStartDato < @dato--GETDATE()
( -- 27.03.2019 Endret WHERE betingelser lik neste step for å minske datamengden i TEMP tabell
(PeriodeStartDato BETWEEN @StartDato AND @SluttDato)
OR (Kjorenr_k = @kjorenr AND PeriodeStartDato < @dato)
)
AND KVID_Kontotype IN (189 --ArligInnskudd
,412,413 --AdmRes
,190,407,408,409,410,411,591) --Risiko
GROUP BY
AvtaleNummer,
MedlemskapNummer,
Dekningstype,
StartAlder,
OpphorsAlder,
YEAR(PeriodeStartDato),
MONTH(PeriodeStartDato)
) ajourholdD
ON ajourholdD.AvtaleNummer = mdtp.AvtaleNummer
AND ajourholdD.MedlemskapNummer = mdtp.MedlemskapNummer
AND ajourholdD.Dekningstype = mdtp.Dekningstype
AND ajourholdD.StartAlder = mdtp.StartAlder
AND ajourholdD.OpphorsAlder = mdtp.OpphorsAlder
AND ajourholdD.PeriodeStartDatoAar = YEAR(mdtp.PeriodeStartDato)
AND ajourholdD.PeriodeStartDatoManed = MONTH(mdtp.PeriodeStartDato)
AND ajourholdD.maxAjourholdDato = mdtp.AjourholdDato
WHERE mdtp.PeriodeStartDato <= @dato
AND NOT EXISTS
(
SELECT * FROM
dbo.cte_MDTForsikringssum dest
WHERE dest.AvtaleNummer = mdtp.AvtaleNummer
AND dest.MedlemskapNummer = mdtp.MedlemskapNummer
AND dest.Dekningstype = mdtp.Dekningstype
AND dest.StartAlder = mdtp.StartAlder
AND dest.OpphorsAlder = mdtp.OpphorsAlder
AND dest.GjelderFraDato = mdtp.PeriodeStartDato
AND dest.EndretDato = mdtp.AjourholdDato
)
OPTION(RECOMPILE);
Run Code Online (Sandbox Code Playgroud)
重写 #2 也删除了OR
使用UNION
INSERT INTO dbo.cte_MDTForsikringssum
SELECT DISTINCT
mdtp.AvtaleNummer
,mdtp.MedlemskapNummer
,mdtp.Dekningstype
,mdtp.StartAlder
,mdtp.OpphorsAlder
,mdtp.PeriodeStartDato AS GjelderFraDato
,NULL GjelderTilDato
,mdtp.AjourholdDato AS EndretDato
,CONVERT(DECIMAL(18,8), 0) AS Forsikringssum
,0 AS Avkortningsfaktor
,0 AS PensjonsgivendeGrunnlag
,0 AS Folketrygd
,mdtp.Kjorenr_k
FROM [BMPERSIST].vips.impMedlemskapDekningTrinnPremie_full mdtp
INNER JOIN
(
SELECT
AvtaleNummer,
MedlemskapNummer,
Dekningstype,
StartAlder,
OpphorsAlder,
YEAR(PeriodeStartDato) AS PeriodeStartDatoAar,
MONTH(PeriodeStartDato) AS PeriodeStartDatoManed,
MAX(AjourholdDato) AS maxAjourholdDato
FROM
(
SELECT
AvtaleNummer,
MedlemskapNummer,
Dekningstype,
StartAlder,
OpphorsAlder,
PeriodeStartDato,
AjourholdDato
FROM
[BMPERSIST].vips.impMedlemskapDekningTrinnPremie_full
WHERE --PeriodeStartDato < @dato--GETDATE()
( -- 27.03.2019 Endret WHERE betingelser lik neste step for å minske datamengden i TEMP tabell
(PeriodeStartDato BETWEEN @StartDato AND @SluttDato)
)
AND KVID_Kontotype IN (189 --ArligInnskudd
,412,413 --AdmRes
,190,407,408,409,410,411,591) --Risiko
UNION
SELECT
AvtaleNummer,
MedlemskapNummer,
Dekningstype,
StartAlder,
OpphorsAlder,
PeriodeStartDato,
AjourholdDato
FROM [BMPERSIST].vips.impMedlemskapDekningTrinnPremie_full
WHERE
(Kjorenr_k = @kjorenr AND PeriodeStartDato < @dato)
AND KVID_Kontotype IN (189 --ArligInnskudd
,412,413 --AdmRes
,190,407,408,409,410,411,591) --Risiko
) AS A
GROUP BY
AvtaleNummer,
MedlemskapNummer,
Dekningstype,
StartAlder,
OpphorsAlder,
YEAR(PeriodeStartDato),
MONTH(PeriodeStartDato)
)
ajourholdD
ON ajourholdD.AvtaleNummer = mdtp.AvtaleNummer
AND ajourholdD.MedlemskapNummer = mdtp.MedlemskapNummer
AND ajourholdD.Dekningstype = mdtp.Dekningstype
AND ajourholdD.StartAlder = mdtp.StartAlder
AND ajourholdD.OpphorsAlder = mdtp.OpphorsAlder
AND ajourholdD.PeriodeStartDatoAar = YEAR(mdtp.PeriodeStartDato)
AND ajourholdD.PeriodeStartDatoManed = MONTH(mdtp.PeriodeStartDato)
AND ajourholdD.maxAjourholdDato = mdtp.AjourholdDato
WHERE mdtp.PeriodeStartDato <= @dato
AND NOT EXISTS
(
SELECT * FROM
dbo.cte_MDTForsikringssum dest
WHERE dest.AvtaleNummer = mdtp.AvtaleNummer
AND dest.MedlemskapNummer = mdtp.MedlemskapNummer
AND dest.Dekningstype = mdtp.Dekningstype
AND dest.StartAlder = mdtp.StartAlder
AND dest.OpphorsAlder = mdtp.OpphorsAlder
AND dest.GjelderFraDato = mdtp.PeriodeStartDato
AND dest.EndretDato = mdtp.AjourholdDato
)
OPTION(RECOMPILE);
Run Code Online (Sandbox Code Playgroud)
重写 #3 添加一个额外的临时表来存储内连接
通过添加临时表来拆分查询,优化器可能会对最终查询进行更好的估计。
SELECT
AvtaleNummer,
MedlemskapNummer,
Dekningstype,
StartAlder,
OpphorsAlder,
YEAR(PeriodeStartDato) AS PeriodeStartDatoAar,
MONTH(PeriodeStartDato) AS PeriodeStartDatoManed,
MAX(AjourholdDato) AS maxAjourholdDato
INTO #TEMP
FROM
(
SELECT
AvtaleNummer,
MedlemskapNummer,
Dekningstype,
StartAlder,
OpphorsAlder,
PeriodeStartDato,
AjourholdDato
FROM
[BMPERSIST].vips.impMedlemskapDekningTrinnPremie_full
WHERE --PeriodeStartDato < @dato--GETDATE()
( -- 27.03.2019 Endret WHERE betingelser lik neste step for å minske datamengden i TEMP tabell
(PeriodeStartDato BETWEEN @StartDato AND @SluttDato)
)
AND KVID_Kontotype IN (189 --ArligInnskudd
,412,413 --AdmRes
,190,407,408,409,410,411,591) --Risiko
UNION
SELECT
AvtaleNummer,
MedlemskapNummer,
Dekningstype,
StartAlder,
OpphorsAlder,
PeriodeStartDato,
AjourholdDato
FROM [BMPERSIST].vips.impMedlemskapDekningTrinnPremie_full
WHERE
(Kjorenr_k = @kjorenr AND PeriodeStartDato < @dato)
AND KVID_Kontotype IN (189 --ArligInnskudd
,412,413 --AdmRes
,190,407,408,409,410,411,591) --Risiko
) AS A
INSERT INTO dbo.cte_MDTForsikringssum
SELECT DISTINCT
mdtp.AvtaleNummer
,mdtp.MedlemskapNummer
,mdtp.Dekningstype
,mdtp.StartAlder
,mdtp.OpphorsAlder
,mdtp.PeriodeStartDato AS GjelderFraDato
,NULL GjelderTilDato
,mdtp.AjourholdDato AS EndretDato
,CONVERT(DECIMAL(18,8), 0) AS Forsikringssum
,0 AS Avkortningsfaktor
,0 AS PensjonsgivendeGrunnlag
,0 AS Folketrygd
,mdtp.Kjorenr_k
FROM [BMPERSIST].vips.impMedlemskapDekningTrinnPremie_full mdtp
INNER JOIN
(
SELECT *
FROM #TEMP
) ajourholdD
ON ajourholdD.AvtaleNummer = mdtp.AvtaleNummer
AND ajourholdD.MedlemskapNummer = mdtp.MedlemskapNummer
AND ajourholdD.Dekningstype = mdtp.Dekningstype
AND ajourholdD.StartAlder = mdtp.StartAlder
AND ajourholdD.OpphorsAlder = mdtp.OpphorsAlder
AND ajourholdD.PeriodeStartDatoAar = YEAR(mdtp.PeriodeStartDato)
AND ajourholdD.PeriodeStartDatoManed = MONTH(mdtp.PeriodeStartDato)
AND ajourholdD.maxAjourholdDato = mdtp.AjourholdDato
WHERE mdtp.PeriodeStartDato <= @dato
AND NOT EXISTS
(
SELECT * FROM
dbo.cte_MDTForsikringssum dest
WHERE dest.AvtaleNummer = mdtp.AvtaleNummer
AND dest.MedlemskapNummer = mdtp.MedlemskapNummer
AND dest.Dekningstype = mdtp.Dekningstype
AND dest.StartAlder = mdtp.StartAlder
AND dest.OpphorsAlder = mdtp.OpphorsAlder
AND dest.GjelderFraDato = mdtp.PeriodeStartDato
AND dest.EndretDato = mdtp.AjourholdDato
)
OPTION(RECOMPILE);
DROP TABLE #TEMP;
Run Code Online (Sandbox Code Playgroud)
结束语
如果有更多的信息,比如表定义和一些示例数据,可以做更多的事情,这三个重写在编写时看起来是最快和最简单的胜利。
归档时间: |
|
查看次数: |
385 次 |
最近记录: |