2009-11-08 3 views
11

Я ищу способ получить средневзвешенное значение из двух строк данных с тем же числом столбцов, где среднее значение выглядит следующим образом (заимствование записи в формате Excel):Средневзвешенное значение в T-SQL (например, SUMPRODUCT Excel)

(A1*B1)+(A2*B2)+...+(An*Bn)/SUM(A1:An) 

Первая часть отражает те же функции, что и функция SUMPRODUCT() Excel.

Мой улов заключается в том, что мне нужно динамически указывать, какая строка усредняется с весами, из какой строки берутся весы, а также диапазон дат.

EDIT: Это проще, чем я думал, потому что Excel заставлял меня думать, что мне нужен какой-то стержень. Мое решение до сих пор так: ответ

select sum(baseSeries.Actual * weightSeries.Actual)/sum(weightSeries.Actual) 
from (
    select RecordDate , Actual 
    from CalcProductionRecords 
    where KPI = 'Weighty' 
) baseSeries inner join (  
    select RecordDate , Actual 
    from CalcProductionRecords 
    where KPI = 'Tons Milled' 
) weightSeries on baseSeries.RecordDate = weightSeries.RecordDate 
+0

Как диапазон дат войти в него? Сколько столбцов - несколько или много? Исправлено ли количество столбцов? –

+0

@martin, только один столбец. Раньше он был одним на KPI, но это было не весело. Диапазон дат за отчетный период. – ProfK

+0

Является ли вышеприведенное заявление рассмотренным CTE? Если не так, как вы можете превратить это в CTE? Кто угодно? – PositiveGuy

ответ

13

Quassnoi показывает, как сделать SUMPRODUCT, и с помощью ИНЕК позволит ограничить поле Date ...

SELECT 
    SUM([tbl].data * [tbl].weight)/SUM([tbl].weight) 
FROM 
    [tbl] 
WHERE 
    [tbl].date >= '2009 Jan 01' 
    AND [tbl].date < '2010 Jan 01' 

Более сложная часть - это то, где вы хотите «динамически указать», какое поле [данные] и какое поле [вес]. Короткий ответ заключается в том, что вам реально придется использовать Dynamic SQL. Что-то вдоль линий:
- Создать строку шаблона
- Заменить все экземпляры [TBL] .data с полем соответствующие данные
- Заменить все экземпляры [TBL] .Weight с соответствующим полем веса
- Выполнить строку

Динамический SQL, однако, несет в себе собственные накладные расходы. Являются ли запросы относительно нечастыми, или время выполнения самого запроса относительно велико, это может быть неважно. Однако, если они являются общими и короткими, вы можете заметить, что использование динамического sql вводит заметные служебные данные. (Не говоря уже быть осторожным атак SQL-инъекции и т.д.)

EDIT:

В вашем примере вы Lastest выделить три поля:

  • RecordDate
  • КПЭ
  • Actual

Когда [KPI] является «Весом» Y ", затем [Фактический] Используемый весовой коэффициент.
Когда [KPI] является «Tons Milled», тогда [Actual] - данные, которые вы хотите скопировать.


Некоторые вопросы у меня есть следующие:

  • Есть ли какие-либо другие поля?
  • Есть ли когда-либо ТОЛЬКО один раз в день за KPI?

Причина, по которой я хочу сказать, что вы хотите, чтобы JOIN вы делали, это только 1: 1.(Вы не хотите 5 Actuals присоединения с 5 Массы, давая 25 resultsing записей)

Несмотря на это, небольшое упрощение вашего запроса, конечно, возможно ...

SELECT 
    SUM([baseSeries].Actual * [weightSeries].Actual)/SUM([weightSeries].Actual) 
FROM 
    CalcProductionRecords AS [baseSeries] 
INNER JOIN 
    CalcProductionRecords AS [weightSeries] 
     ON [weightSeries].RecordDate = [baseSeries].RecordDate 
-- AND [weightSeries].someOtherID = [baseSeries].someOtherID 
WHERE 
    [baseSeries].KPI = 'Tons Milled' 
    AND [weightSeries].KPI = 'Weighty' 

Закомментированный из линии только необходимы, если вам нужны дополнительные предикаты, чтобы обеспечить соотношение 1: 1 между вашими данными и весами.


Если вы не можете guarnatee только одно значение даты, и не имеют каких-либо других областях, чтобы присоединиться, вы можете изменить ваш sub_query на основе версии немного ...

SELECT 
    SUM([baseSeries].Actual * [weightSeries].Actual)/SUM([weightSeries].Actual) 
FROM 
(
    SELECT 
     RecordDate, 
     SUM(Actual) 
    FROM 
     CalcProductionRecords 
    WHERE 
     KPI = 'Tons Milled' 
    GROUP BY 
     RecordDate 
) 
    AS [baseSeries] 
INNER JOIN 
(
    SELECT 
     RecordDate, 
     AVG(Actual) 
    FROM 
     CalcProductionRecords 
    WHERE 
     KPI = 'Weighty' 
    GROUP BY 
     RecordDate 
) 
    AS [weightSeries] 
     ON [weightSeries].RecordDate = [baseSeries].RecordDate 

Это предполагает, что AVG веса действителен, если в тот же день имеется несколько весов.


EDIT: Кто-то голосовал за это, так я думал, что улучшить окончательный ответ :)

SELECT 
    SUM(Actual * Weight)/SUM(Weight) 
FROM 
(
    SELECT 
     RecordDate, 
     SUM(CASE WHEN KPI = 'Tons Milled' THEN Actual ELSE NULL END) AS Actual, 
     AVG(CASE WHEN KPI = 'Weighty'  THEN Actual ELSE NULL END) AS Weight 
    FROM 
     CalcProductionRecords 
    WHERE 
     KPI IN ('Tons Milled', 'Weighty') 
    GROUP BY 
     RecordDate 
) 
    AS pivotAggregate 

Это позволяет избежать JOIN, а также только сканирует таблицу один раз.

Он полагается на то, что значения NULL игнорируются при расчете AVG().

+0

@Dems, кажется, я воспринимаю вещи как слишком сложные, потому что динамически поставленные значения являются значениями полей, а не именами, поскольку я исправил выше. – ProfK

10
SELECT SUM(A * B)/SUM(A) 
FROM mytable 
+0

Вы принимаете значения из двух разных столбцов. Они действительно происходят из одной колонки в разных наборах записей. – ProfK

+0

Не могли бы вы разместить некоторые образцы данных? – Quassnoi

1

Если я понимаю проблему, то попробуйте это

SET DATEFORMAT dmy 
    declare @tbl table(A int, B int,recorddate datetime,KPI varchar(50)) 
    insert into @tbl 
     select 1,10 ,'21/01/2009', 'Weighty'union all 
     select 2,20,'10/01/2009', 'Tons Milled' union all 
     select 3,30 ,'03/02/2009', 'xyz'union all 
     select 4,40 ,'10/01/2009', 'Weighty'union all 
     select 5,50 ,'05/01/2009', 'Tons Milled'union all 
     select 6,60,'04/01/2009', 'abc' union all 
     select 7,70 ,'05/01/2009', 'Weighty'union all 
     select 8,80,'09/01/2009', 'xyz' union all 
     select 9,90 ,'05/01/2009', 'kws' union all 
     select 10,100,'05/01/2009', 'Tons Milled' 

    select SUM(t1.A*t2.A)/SUM(t2.A)Result from 
        (select RecordDate,A,B,KPI from @tbl)t1 
     inner join(select RecordDate,A,B,KPI from @tbl t)t2 
     on t1.RecordDate = t2.RecordDate 
     and t1.KPI = t2.KPI 
Смежные вопросы