列列表中带有 , * 的 SELECT 语句比不带 * 的相同语句更快

Joh*_* N. 10 sql-server execution-plan index-tuning sql-server-2014

情况

\n

当使用具有定义的列集的 SELECT 语句查询数据库时,大约 21 秒内就会收到结果。

\n

, *如果定义的列集列表末尾有一个附加星号 ( ),则查询将在 2 秒内返回结果。

\n

查询执行计划

\n

执行计划差异很大。

\n

您可以通过 PasteThePlan 中的链接找到良好的实际查询执行计划不良的实际查询执行计划。

\n

列列表中包含 , * 的语句(在末尾)

\n
\n            SELECT    -- DISTINCT -- 27.04.2020\n                \'SchuelerKlasse\' AS EcoQuery,\n                VX_PERSON.PER_MAN_ID, VX_PERSON.PER_ID, VX_PERSON.PER_NAME, VX_PERSON.PER_VORNAME, VX_PERSON.PER_LB_PER_ID, \n                VX_PERSON.PER_GESCHLECHT, VX_PERSON.PER_GEBURTSDATUM, VX_PERSON.PER_TELP, VX_PERSON.PER_MAILP, VX_PERSON.PER_NATP, VX_PERSON.PER_VERSICHERTENNUMMER, VX_PERSON.PER_LAND,\n                VX_ADRESSE.ADR_STRASSE, VX_ADRESSE.ADR_PLZ, VX_ADRESSE.ADR_ORT,\n                VX_KLASSE.KL_CODE, VX_KLASSE.KL_BEZEICHNUNG,\n                VX_KLASSEABSCHNITTSCHUELER.KAS_ANMELDE_STATUS, \n                VX_KLASSEABSCHNITTSCHUELER.KAS_ANMELDETYP, VX_KLASSEABSCHNITTSCHUELER.KAS_ABSCHNITTSNR,\n                VX_KLASSE_ZEITRAUM.KLZ_IS_ABSCHLUSSKLASSE, VX_KLASSE_ZEITRAUM.KLZ_ZR_NR,\n                VX_ZEITRAUM.ZR_BEGINN, VX_ZEITRAUM.ZR_ENDE\n                ,\'\' AS FA_CODE\n                ,\'\' AS FA_BEZ_STP, \'\' AS FA_BEZ_STP_LANG\n                , \'\' AS EcoOrig_FA_CODE, \'\' AS EcoOrig_FA_BEZ_STP, \'\' AS EcoOrig_FA_BEZ_STP_LANG\n                , VX_ANGEBOT.ANG_BEGINN\n            \n ,* \n\n            FROM \n                ECOLST.VX_KLASSE_ZEITRAUM, \n                ECOLST.VX_PERSON, \n                ECOLST.VX_KLASSE, \n                ECOLST.VX_KLASSEABSCHNITTSCHUELER, \n                ECOLST.VX_ZEITRAUM, \n                ECOLST.VX_ADRESSE \n                , ECOSYS.T_KLASSE\n                , ECOLST.VX_ANGEBOT\n\n            WHERE  \n                    VX_KLASSE_ZEITRAUM.klz_kl_id = VX_KLASSE.kl_id \n                AND VX_KLASSE_ZEITRAUM.klz_zr_id = VX_ZEITRAUM.zr_id \n                AND VX_KLASSEABSCHNITTSCHUELER.kas_ang_id = VX_KLASSE.kl_ang_id \n                AND VX_KLASSEABSCHNITTSCHUELER.kas_zr_id = VX_ZEITRAUM.zr_id \n                AND VX_KLASSEABSCHNITTSCHUELER.kas_per_id = VX_PERSON.per_id \n                AND VX_KLASSEABSCHNITTSCHUELER.kas_kl_id = VX_KLASSE.kl_id \n                AND VX_KLASSEABSCHNITTSCHUELER.KAS_ANMELDE_STATUS LIKE \'De%\'  -- LIKE \'Definitiv%\'\n                AND VX_PERSON.per_id = VX_ADRESSE.adr_per_id \n                AND VX_PERSON.per_man_id = VX_KLASSE.kl_man_id\n                AND VX_KLASSE.KL_ANG_ID = VX_ANGEBOT.ANG_ID\n                AND VX_KLASSE.KL_MAN_ID = 15 \n                AND VX_KLASSE.KL_ID = T_KLASSE.KL_ID\n                AND T_KLASSE.KL_STATUS_ID = 491   -- d.h. TS_CODE.CODE_UP_BEZEICHNUNG = \'AKTIV\'\n            \n\n                AND VX_KLASSE.KL_KLASSENTYP_ID IN (742,743,1235,1926,2075,2076,2078,2079,2080,2081,2086,2103,2118,2119,2122,2152,2252,2308,2416)\n        \n\n                AND VX_PERSON.PER_NP = 1   -- Nat\xc3\xbcrliche Person\n                AND LEN(LTRIM(RTRIM(VX_PERSON.PER_VORNAME))) > 0        -- TRIM() kann erst ab SQL Server 2017 verwendet werden\n                AND LEN(LTRIM(RTRIM(VX_PERSON.PER_NAME))) > 0           -- TRIM() kann erst ab SQL Server 2017 verwendet werden\n        \n AND VX_ZEITRAUM.zr_beginn <= CONVERT(DATETIME, \'20.05.2023\', 104) \n AND VX_ZEITRAUM.zr_ende   >= CONVERT(DATETIME, \'14.02.2023\', 104) \n AND VX_PERSON.per_man_id IN ( 15 ) \n\n                --AND VX_Person.PER_ID IN  (233777,233779)\n        \n
Run Code Online (Sandbox Code Playgroud)\n

问题

\n

*一般建议是在定义列列表时不要使用,但就我而言,, *在末尾添加到列列表可以显着加快查询速度。(从 21 秒减少到 2 秒)

\n

实际执行计划中没有缺失的索引建议。

\n

, *我认为这与在语句中使用时返回的特定列有关,这些列可能包含在查询优化器认为有用的索引中,但我不确定如何精确定位这些列。

\n
    \n
  1. 我必须创建哪些索引才能说服 SQL Server 运行不包含列列表的性能不佳的语句,以使用与包含列列表中的附加项的高性能, *语句类似的计划?, *

    \n
  2. \n
  3. 我是否必须分析良好执行计划中使用的所有索引并创建减少的索引(省略某些列),以便查询优化器会考虑对语句使用类似的良好, *执行计划而不需要额外的?

    \n
  4. \n
\n
\n

根据建议尝试解决方案

\n
    \n
  1. OPTION (MIN_GRANT_PERCENT = 10, MAX_GRANT_PERCENT = 15)

    \n

    应用上述解决方案只能暂时提高性能约 1 小时。之后查询恢复到错误的执行计划。我不知道为什么...

    \n
  2. \n
  3. 数据库兼容级别

    \n

    使用以下命令将数据库的兼容性级别更改为 110 (SQL Server 2012),导致上述查询的性能不断提高,而无需,*在列列表中添加 。

    \n
    USE [master]\nGO\nALTER DATABASE [ECOWEBBSP] SET COMPATIBILITY_LEVEL = 110\nGO\n
    Run Code Online (Sandbox Code Playgroud)\n

    兼容性级别为 110 的查询执行计划表明,查询优化器在检索数据时选择了完全不同的方法,并且在分配正确的内存量 (110 MB) 方面没有任何问题。

    \n
  4. \n
\n

跟进问题

\n

将兼容性级别设置为 110 是我唯一的选择吗?

\n

附加反馈

\n

fi_kla_is_abschlussklasseErik的回答中提到的不祥功能是由视图VX_KLASSE_ZEITRAUM.KLZ_IS_ABSCHLUSSKLASSE中的列触发的ECOLST.VX_KLASSE_ZEITRAUM。基础表在检索数据时调用标量值函数。该函数本身返回 0 或 1,具体取决于学生是否属于毕业班 (1) 或不是毕业班 (0)。

\n

但是,在兼容性级别设置为 110 (SQL Server 2012) 的情况下运行时,该函数似乎对查询的持续时间没有太大影响。有关详细信息,请参阅兼容性级别为 110 的查询执行计划

\n

Eri*_*ing 13

虽然这两个查询计划有很多差异,但在较慢的计划中遇到的问题主要发生在由于内存授予大小不足而导致的溢出操作中:

坚果

至少还有另一起泄漏事件,但影响较小。

更快的计划不会溢出的原因是它获得了更高的内存授予,SQL Server 根据通过内存消耗运算符(如排序和哈希)的行数和每行的估计大小来估计内存授予。

这是内存授予信息:

坚果

在这种复杂性的查询中很难获得准确的基数估计,但您可以尝试:

  • 更新统计数据
  • 使用旧版 CE:OPTION(QUERYTRACEON 9481)
  • 使用#temp 表分解查询
  • 使用 MIN_GRANT_PERCENT 提示获得更高的内存授予

可能值得注意的是,您有一个标量 UDF 禁止此查询的并行性,以及一些可能导致估计错误的非 SARGable 谓词。

功能:

坚果

查询反模式:

AND LEN(LTRIM(RTRIM(VX_PERSON.PER_VORNAME))) > 0
AND LEN(LTRIM(RTRIM(VX_PERSON.PER_NAME))) > 0
Run Code Online (Sandbox Code Playgroud)

可以简化为查找单个字符通配符:

AND VX_PERSON.PER_VORNAME LIKE '_%'
AND VX_PERSON.PER_NAME LIKE '_%'
Run Code Online (Sandbox Code Playgroud)

或者只是寻找非空字符串:

AND VX_PERSON.PER_VORNAME <> ''
AND VX_PERSON.PER_NAME <> ''
Run Code Online (Sandbox Code Playgroud)