Как перебирать значения в SQL при выполнении расчетов? - PullRequest
0 голосов
/ 07 марта 2019

У меня есть немного сложный сценарий:

У меня есть следующий запрос, который дает мне эффект определенной инициативы на томах, указанных в таблицах. Если объем равен 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. Например: Если в Инициативе 10001 громкость указана в одной из строк как 500, а в инициативе 2 изменено значение -300, то для выходных данных в качестве столбца тома, указанного в этой конкретной строке, должно быть 200. Кроме того, init_id должен быть изменен на 10002 с 10001, поскольку инициатива 10002 является инициативой, которая была применена последней.

Опять же, если у нас есть Инициатива 10003, которая должна быть применена к Инициативе 10002, то в выходных данных должна быть 10003 в качестве инициативы.

Следовательно, к моим данным, которые были сгенерированы из вышеуказанного запроса, должны применяться инициативы 10002, 10003 и т. Д.

Во всех этих случаях критерии объединения двух таблиц (инициативы и действия) будут такими же, как указано в запросе.

Как мне этого добиться?

Я попробовал CTE, но не смог этого сделать. Я запутался в том, как выполнить вычисления (операторы CASE WHEN) для этого.

Ответы [ 2 ]

1 голос
/ 07 марта 2019

EDIT

Это почти полностью переписанный ответ, который я получил до

Тем не менее, как упоминалось ранее, я бы потерял все скобки ... они просто делают ваш код намного более нечитаемым. Они действительно нужны только тогда, когда имя столбца конфликтует с зарезервированным словом в SQL.

Далее вам не нужно select из select в псевдоним ... и вам не нужно беспокоиться о получении дополнительных данных в промежуточных результатах. На самом деле вы должны только 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, для меня это тоже был бы опыт обучения!

0 голосов
/ 07 марта 2019

Несмотря на то, что вы можете или не можете получить ответ, включая измененную версию вашего кода, вы, возможно, захотите взглянуть на упрощенный запрос ниже, чтобы понять один из способов решения проблемы «текущих значений», которая у вас есть.

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
...