在执行计算时如何遍历SQL中的值?

时间:2019-03-07 13:18:06

标签: sql sql-server recursion common-table-expression

我这里有一个复杂的场景:

我有以下查询,该查询使我对表中指定的卷具有某种主动性。如果音量为1000,而更改为-500,则新音量为500。速率相同。这是更新新数量,新费率,新需求小时数和新需求FTE的查询。

select 
x.mdc_code, x.cal_month, x.cal_year, x.activity,  y.scenario_id, 
 y.initiative_id, x.volume, x.rate, y.metric_to_be_applied, y.change,

CASE WHEN y.metric_to_be_applied = 'Volume'
THEN x.volume + y.change end as New_Volume_VolumeChange ,
CASE WHEN y.metric_to_be_applied = 'Volume'
THEN x.rate end as New_Rate_VolumeChange,
CASE WHEN y.metric_to_be_applied = 'Volume'
THEN (x.volume + y.change)/x.rate end as New_DemandHours_VolumeChange,
CASE WHEN y.metric_to_be_applied = 'Volume'
THEN ((x.volume + y.change)/x.rate)/176 end as New_DemandFTE_VolumeChange,
CASE WHEN y.metric_to_be_applied = 'Rate'
THEN x.volume end as New_Volume_RateChange ,
CASE WHEN y.metric_to_be_applied = 'Rate'
THEN x.rate + y.change end as New_Rate_RateChange,
CASE WHEN y.metric_to_be_applied = 'Rate'
THEN (x.volume)/(x.rate + y.change) end as New_DemandHours_RateChange,
CASE WHEN y.metric_to_be_applied = 'Rate'
THEN ((x.volume)/(x.rate+ y.change))/176 end as New_DemandFTE_RateChange
from
(SELECT  [id] as activity_id
  ,[mdc_code]
  ,[function_name]
  ,[cal_month]
  ,[cal_year]
  ,[model_type]
  ,[activity]
  ,[activity_type]
  ,[product_category]
  ,[project_category]
  ,[segment]
  ,[volume]
  ,[demand_fte]
  ,[demand_hours]
  ,[rate]
  ,[sub_division_name]
  ,[division_name]
  ,[myp_year]
  ,[client_count]
  ,[calls]
  ,[loguser]
  ,[logdate]
  ,[IsProd]
  ,[version_id]
FROM [myp_activity]
)x

join

(
SELECT [id]
  ,[scenario_id]
  ,[initiative_id]
  ,[initiative_name]
  ,[comments]
  ,[recommended_by]
  ,[mdc_code]
  ,[function_name]
  ,[cal_month]
  ,[cal_year]
  ,[model_type]
  ,[activity]
  ,[metric_to_be_applied]
  ,[change_type]
  ,[change]
  ,[sub_division_name]
  ,[division_name]
  ,[myp_year]
  ,[loguser]
  ,[logdate]
  ,[IsProd]
  ,[version_id]
  ,[initiative_type]
  FROM [myp_initiatives] where initiative_id=10001
 ) y
on x.mdc_code = y.mdc_code
and x.cal_month = y.cal_month
and x.cal_year = y.cal_year
and x.activity = y.activity

此查询的输出如下:

enter image description here

还有4个未在图片中列出的列(New_Volume_RateChange,New_Rate_RateChange,New_DemandHours_RateChange,New_DemandFTE_RateChange)。

我需要对此数据应用主动10002。计划10002的详细信息显示在计划表中,该表已用于加入。现在,输出已对其应用了主动权10001。 例如: 如果Initiative 10001在其中一个行中将Volume列为500,而Initiative 2更改为-300,则该行中的Volume列应具有200作为输出。 另外,由于主动10002是最后应用的主动,所以Initiative_id应该从10001更改为10002。

同样,如果我们列出了要在倡议10002上应用的倡议10003,那么输出应列出10003作为倡议。

因此,应该对从上述查询生成的数据应用“主动性10002”,“ 10003”等。

在所有这些情况下,两个表(启动和活动)的连接条件将与查询中列出的条件相同。

我该如何实现?

我尝试过CTE,但无法做到。我对如何执行计算(CASE WHEN语句)感到困惑。

2 个答案:

答案 0 :(得分:1)

编辑

这几乎完全重写了我以前的答案

仍然,如前所述,我将失去所有括号...它们只会使您的代码更加不可读。当列名与SQL中的保留字冲突时,您才真正需要它们。

接下来,您不必从selectselect进行别名...并且您不必担心会在中间结果中获取额外的数据。当您进行某种汇总或计算时,您实际上只应该select中的select。一个很好的例子是您的y ...您可以在其中计算计算的基本因子...然后完全摆脱CASE s ...就像这样:

select 
  x.mdc_code, x.cal_month, x.cal_year, x.activity,  y.scenario_id, 
  y.initiative_id, x.volume, x.rate, y.metric_to_be_applied, y.change,

  x.volume + applied_volume as New_Volume_VolumeChange ,
  x.rate * applied_volume as New_Rate_VolumeChange,
  (x.volume + applied_volume)/x.rate as New_DemandHours_VolumeChange,
  ((x.volume + applied_volume)/x.rate)/176 as New_DemandFTE_VolumeChange,
  x.volume + applied_rate as New_Volume_RateChange ,
  x.rate * applied_rate as New_Rate_RateChange,
  (x.volume)/(x.rate + applied_rate) as New_DemandHours_RateChange,
  ((x.volume)/(x.rate+ applied_rate))/176 as New_DemandFTE_RateChange
from
  myp_activity x
  inner join
  ( select 
      iif( metric_to_be_applied = 'Volume', change, null ) as applied_volume,
      iif( metric_to_be_applied = 'Rate', change, null ) as applied_rate,
      mdc_code, cal_month, cal_year, activity, scenario_id, initiative_id, metric_to_be_applied, change
    from 
      myp_initiatives
  ) as y
on 
    x.mdc_code = y.mdc_code
    and x.cal_month = y.cal_month
    and x.cal_year = y.cal_year
    and x.activity = y.activity
where 
  y.initiative_id=10001

从概念上讲,这是完全适当的,但是,就我所知,您的要求意味着不能将简单的CTE应用于该问题。这是因为没有办法重复我可以制定的计划。相反,让我提供一个功能,该功能可以按照您需要的方式(我认为)汇总更改。

但是,请注意一些重要的事项。我不知道您的列的数据类型,因此此函数将所有内容声明为int我确定是不正确的。您必须编辑数据类型以匹配真正的数据。

create function AppliedInitiatives() returns  
@t table
( 
  mdc_code int, --> note: you have to change all these types to match your data
  cal_month int, 
  cal_year int, 
  cal_activity int, 
  scenario_id int,
  initiative_id int,
  volume int,
  rate int,
  metric_to_be_applied nvarchar( 32 ),
  change int,
  New_Volume_VolumeChange int,
  New_Rate_VolumeChange int,
  New_DemandHours_VolumeChange int,
  New_DemandFTE_VolumeChange int,
  New_Volume_RateChange int,
  New_Rate_RateChange int,
  New_DemandHours_RateChange int,
  New_DemandFTE_RateChange int
) as 
begin

  declare
    @applied_volume int, --> note: you have to change all these types to match your data
    @applied_rate int,
    @mdc_code int, 
    @cal_month int, 
    @cal_year int, 
    @cal_activity int, 
    @scenario_id int,
    @initiative_id int,
    @previous_initiative_id int,
    @volume int,
    @rate int,
    @metric_to_be_applied nvarchar( 32 ),
    @change int

  declare c cursor for 
  select 
    iif( metric_to_be_applied = 'Volume', change, null ) as applied_volume,
    iif( metric_to_be_applied = 'Rate', change, null ) as applied_rate,
    mdc_code, cal_month, cal_year, activity, scenario_id, initiative_id, metric_to_be_applied, change
  from 
    myp_initiatives
  order by
    initiative_id;

   open c
   while ( 1 = 1 )
   begin
    fetch next from c into
      @applied_volume,
      @applied_rate,
      @mdc_code, 
      @cal_month, 
      @cal_year, 
      @cal_activity, 
      @scenario_id,
      @initiative_id,
      @metric_to_be_applied,
      @change
      if ( @@fetch_status != 0 ) break

     if ( @previous_initiative_id is null )
     begin
      insert @t select
        @mdc_code, @cal_month, @cal_year, @cal_activity,  @scenario_id, 
        @initiative_id, @volume, @rate, @metric_to_be_applied, @change,

        x.volume + @applied_volume                as New_Volume_VolumeChange ,
        x.rate * @applied_volume                  as New_Rate_VolumeChange,
        (x.volume + @applied_volume)/x.rate       as New_DemandHours_VolumeChange,
        ((x.volume + @applied_volume)/x.rate)/176 as New_DemandFTE_VolumeChange,
        x.volume + @applied_rate                  as New_Volume_RateChange ,
        x.rate * @applied_rate                    as New_Rate_RateChange,
        (x.volume)/(x.rate + @applied_rate)       as New_DemandHours_RateChange,
        ((x.volume)/(x.rate+ @applied_rate))/176  as New_DemandFTE_RateChange
      from
        myp_activity x
      where
        x.mdc_code = @mdc_code
        and x.cal_month = @cal_month
        and x.cal_year = @cal_year
        and x.activity = @cal_activity
      select @previous_initiative_id = @initiative_id
    end
    else
    begin
      insert @t select
        @mdc_code, @cal_month, @cal_year, @cal_activity,  @scenario_id, 
        @initiative_id, @volume, @rate, @metric_to_be_applied, @change,

        New_Volume_VolumeChange + @applied_volume                               as New_Volume_VolumeChange ,
        New_Rate_VolumeChange * @applied_volume                                 as New_Rate_VolumeChange,
        (New_Volume_VolumeChange + @applied_volume)/New_Rate_VolumeChange       as New_DemandHours_VolumeChange,
        ((New_Volume_VolumeChange + @applied_volume)/New_Rate_VolumeChange)/176 as New_DemandFTE_VolumeChange,
        New_Volume_VolumeChange + @applied_rate                                 as New_Volume_RateChange ,
        New_Rate_VolumeChange * @applied_rate                                   as New_Rate_RateChange,
        (New_Volume_VolumeChange)/(New_Rate_VolumeChange + @applied_rate)       as New_DemandHours_RateChange,
        ((New_Volume_VolumeChange)/(New_Rate_VolumeChange+ @applied_rate))/176  as New_DemandFTE_RateChange
      from
        @t x
      where
        x.mdc_code = @mdc_code
        and x.cal_month = @cal_month
        and x.cal_year = @cal_year
        and x.cal_activity = @cal_activity
        and x.initiative_id = @previous_initiative_id
      select @previous_initiative_id = @initiative_id
    end  
  end

  return 

end

这里发生的事情是该函数首先将第一组值写入表@t,然后通过将结果应用于每个上一个项目并写入来插入更新后的值那些放入@t中。最后,它返回表。

定义了此功能后,您只需说:

select
  * --> note: would be better to list columns...but I'm being lazy for clarity
from
  AppliedInitiatives()

如果有人可以将其重写为cte,那对我来说也是一种学习经历!

答案 1 :(得分:0)

尽管您可能会或可能不会获得包括修改后的代码版本的答案,但是您可能希望查看下面的简化查询,以了解解决您的“运行值”问题的一种方法。

DECLARE @Initiative TABLE (IntiativeID INT, AddToValue INT, Code NVARCHAR(10))
DECLARE @Data TABLE(DataID INT, Value INT, Code NVARCHAR(10))

INSERT @Data VALUES(1,10,'A'),(2,10,'B'),(3,10,'A')
INSERT @Initiative VALUES(1,5,'A'),(2,5,'A'),(4,5,'B'),(800,5,'C'),(24,5,'D')


SELECT 
    DataId,
    StartValue = Value,
    LastIntiativeID = MAX(IntiativeID),
    EndValue = Value + SUM(AddToValue)
FROM 
    @Data D 
    LEFT JOIN  @Initiative I ON D.Code = I.Code
GROUP BY 
    DataID,Value
ORDER BY
    DataID
相关问题