【问题标题】:Condition within aggregate query vs math聚合查询与数学中的条件
【发布时间】:2020-11-24 06:54:02
【问题描述】:

我有这两个查询示例,差异很小,我认为这将是性能优化,但没有区别。小的变化是,在其中一个查询中,聚合中有条件逻辑,而在另一个查询中,我使用简单的数学来获得相同的结果。我原以为条件逻辑对于 RDMS 引擎来说比数学逻辑更难使用。但是它们显示了相同的计划和基本相同(我认为由于温暖的chache而略有变化)io统计和执行时间。

SELECT
    fact_hourly.dim_timeseries_key,
    fact_hourly.dim_date_key,
    SUM(fact_hourly.energy) sum_energy,
    SUM( IIF(load_type.is_power_demand_high_load_06_22=1,energy,0) ) sum_hl_energy,
    fact_hourly.dim_sources_key
    --@v_dss_update_time
  FROM core.[fact_hourly] fact_hourly
  LEFT JOIN core.[ds_hours_load_type] load_type 
   on load_type.dim_date_key = fact_hourly.dim_date_key
   and load_type.hour_zero_indexed = DATEPART(HOUR,fact_hourly.value_timestamp)
   WHERE fact_hourly.dim_timeseries_key = 727949
  GROUP BY fact_hourly.dim_timeseries_key,fact_hourly.dim_date_key,fact_hourly.dim_sources_key

SELECT
    fact_hourly.dim_timeseries_key,
    fact_hourly.dim_date_key,
    SUM(fact_hourly.energy) sum_energy,
    SUM( energy*load_type.is_power_demand_high_load_06_22 ) sum_hl_energy,
    fact_hourly.dim_sources_key
    --@v_dss_update_time
  FROM core.[fact_hourly] fact_hourly
  LEFT JOIN core.[ds_hours_load_type] load_type 
   on load_type.dim_date_key = fact_hourly.dim_date_key
   and load_type.hour_zero_indexed = DATEPART(HOUR,fact_hourly.value_timestamp)
   WHERE fact_hourly.dim_timeseries_key = 727949
  GROUP BY fact_hourly.dim_timeseries_key,fact_hourly.dim_date_key,fact_hourly.dim_sources_key

【问题讨论】:

  • 如果要修复性能,可以将DATEPART(HOUR,fact_hourly.value_timestamp) 移动到计算列,并改用计算列。
  • 如果你想比较数学和IIF之间的差异,你应该将鼠标悬停在执行计划中的运算符上并比较两个查询的数量。 (1 个执行计划的屏幕截图对此无济于事)。您可以与brentozar.com/pastetheplan 分享您的执行计划
  • 如果你得到毫秒级的执行时间,查询性能真的是个大问题吗?
  • @AnnL.:以毫秒为单位的执行时间的原因是由于 timeseries_key 上的 where 子句条件。主要是为了快速得到结果,看看查询之间有没有大的不同。
  • @PrebenHuybrechts:我会试试你的建议,谢谢!这里还有两个执行计划的链接:brentozar.com/pastetheplan/?id=SyOEQB9Zvbrentozar.com/pastetheplan/?id=Hyk9XB9Zw

标签: sql sql-server sql-execution-plan


【解决方案1】:

我将重点关注与实际执行计划的差异。 自适应连接右侧的所有内容都完全相同(包括自适应连接)。
两个查询都获得/请求了相同的内存。

当使用“数学”查询而不是“IIF”查询时,会有一个额外的计算标量。 进行数学运算的计算标量有 4 毫秒的额外 CPU 时间
让它变得更贵,但只是一点点。

<RunTimeCountersPerThread 
    Thread="0" 
    ActualRows="13944" 
    Batches="16" 
    ActualEndOfScans="0" 
    ActualExecutions="1" 
    ActualExecutionMode="Batch" 
    ActualElapsedms="4" 
    ActualCPUms="4" 
    ActualScans="0" 
    ActualLogicalReads="0" 
    ActualPhysicalReads="0" 
    ActualReadAheads="0" 
    ActualLobLogicalReads="0" 
    ActualLobPhysicalReads="0" 
    ActualLobReadAheads="0"/>

当查看“额外的数学计算标量”时,我们可以看到它也受到implicit_conversion 的影响。目前这可能不是一个大问题,但可能会让你花几毫秒的时间。当它阻止使用正确的索引时,它会变得更加成问题,而事实并非如此。

CONVERT_IMPLICIT(numeric(3,0),[ELSA].[core].[ds_hours_load_type].[is_power_demand_high_load_06_22] as [load_type].[is_power_demand_high_load_06_22],0

这个(额外的计算标量和隐式转换)成本被传递给下一个执行标量和哈希匹配以及左侧的所有其他运算符。如果您查看估计的子树成本,谁有更多的估计工作(query bucks

计算标量子树成本差异

  • 数学:1,02816
  • IIF:1,02801

HashMatch 子树成本差异

  • 数学:1,04213
  • IIF:1,04182

在深入研究执行计划的 XML 时。我们看到了“IIF”查询的一些额外内容。

<WaitStats>
  <Wait WaitType="PAGEIOLATCH_SH" WaitTimeMs="24" WaitCount="1"/>
  <Wait WaitType="RESERVED_MEMORY_ALLOCATION_EXT" WaitTimeMs="1" WaitCount="164"/>
</WaitStats>

意味着“IIF”查询正在从磁盘中获取,并且还在等待获取一些内存。 我假设您首先执行“IIF”,然后执行“Math”。使缓冲池中的所有页面都可用于第二个查询。

【讨论】:

    【解决方案2】:

    SQL 查询的性能基本上与数据移动有关,而不是与列上的琐碎操作有关。 LEFT JOINGROUP BY 需要读取无数行并对其进行处理。这就是费用。

    CASE 表达式和 * 之间或 MAX()AVG() 之间的性能略有不同。但是,与从磁盘读取数据、将其加载到数据页、匹配不同表中的数据以及移动数据以获取位于同一位置的键值(用于聚合)所需的工作相比,这些差异微不足道。

    当然,也有例外。一些函数非常昂贵,并且确实对查询性能有影响。对于用户定义的函数和处理长字符串的函数来说,这通常是正确的。

    但是您的两个查询的数据转换组件是相同的(相同的FROMWHEREGROUP BY 子句)。所以你应该期望两者的性能会非常相似。

    【讨论】:

    • 在这种情况下,选择部分的差异似乎是查询引擎工作量的微不足道的部分。
    猜你喜欢
    • 2018-12-04
    • 2021-12-02
    • 2021-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-22
    • 1970-01-01
    相关资源
    最近更新 更多