聚合查询与数学中的条件

Condition within aggregate query vs math

我有这两个查询示例,它们之间的差别很小,我认为可以优化性能,但没有区别。小的变化是,在其中一个查询中,聚合中有条件逻辑,而在另一个查询中,我使用简单的数学运算来获得相同的结果。我原以为条件逻辑对于 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

SQL 查询的性能基本上涉及数据移动,而不涉及对列的琐碎操作。 LEFT JOINGROUP BY 需要读取无数行并处理它们。那就是开销。

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

当然也有例外。有些功能相当昂贵, do 对查询性能有影响。 user-defined 函数和处理长字符串的函数通常如此。

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

我将重点关注与实际执行计划的差异。 Adaptive join右边的一切都完全一样(包括adaptive join)。
两个查询 got/requested 相同的内存。

当使用“数学”查询而不是“IIF”查询时,有一个额外的计算标量。 进行数学运算的计算标量有额外的 CPU 时间 4ms
让它更贵,但只是略微增加。

<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”,然后执行“数学”。使缓冲池中的所有页面都可用于第二次查询。