2009-11-08 23 views
11

Poszukuję sposobu na wyliczenie średniej ważonej z dwóch wierszy danych z taką samą liczbą kolumn, w której średnia jest następująca (zapożyczanie notacji w programie Excel):Średnia ważona w T-SQL (np. SUMPRODUCT programu Excel)

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

Pierwsza część odzwierciedla takie same funkcje, jak funkcja SUMPRODUCT() programu Excel.

Moim połowem jest to, że muszę dynamicznie określać, który wiersz jest uśredniany za pomocą odważników, z którego rzędu pochodzą ciężary i zakres dat.

EDYCJA: To jest łatwiejsze niż myślałem, ponieważ Excel powodował, że myślę, że wymagałem pewnego rodzaju przestawienia. Moje rozwiązanie do tej pory jest w następujący sposób: Odpowiedź

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

jaki zakres dat przyjść do niego? Ile kolumn - kilka lub wiele części? Czy ustalono liczbę kolumn? –

+0

@martin, tylko jedna kolumna. Kiedyś był jeden na KPI, ale to nie było zabawne. Zakres dat dotyczy okresu raportowania. – ProfK

+0

Czy powyższe stwierdzenie jest uważane za CTE? Jeśli nie, to jak możesz zmienić to w CTE? Ktoś? – PositiveGuy

Odpowiedz

13

Quassnoi pokazuje jak wykonać SUMPRODUCT, a przy użyciu WHERE pozwoliłoby na ograniczenie przez pole data ...

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

Bardziej złożona część to miejsce, w którym chcesz "dynamicznie określić", jakie pole to [dane], a jakie pole [waga]. Krótka odpowiedź brzmi, że realistycznie musisz skorzystać z Dynamic SQL. Coś wzdłuż linii:
- Tworzenie ciąg szablon
- Wymienić wszystkie instancje [TBL] .data z pola odpowiednie dane
- Wymienić wszystkie instancje [TBL] .weight z odpowiednim polu wagi
- Wykonaj jednak ciąg znaków dynamiczny SQL, jednak wykonuje swój własny narzut. Czy zapytania są stosunkowo rzadkie lub czas wykonania samego zapytania jest stosunkowo długi, może to nie mieć znaczenia. Jeśli jednak są one powszechne i krótkie, można zauważyć, że użycie sql dynamicznego wprowadza istotne obciążenie. (Nie wspominając uważając ataków SQL injection, itp)

EDIT:

W swojej najnowszej przykład wyróżnić trzy pola:

  • RecordDate
  • KPI
  • Rzeczywisty

Gdy [KPI] to "Waga Y ", a następnie [Rzeczywisty] Współczynnik ważenia do użycia.
Gdy [KPI] to "Tony zmielone", wówczas [Rzeczywiste] to Dane, które chcesz zebrać.


Kilka pytań mam to:

  • Czy są jakieś inne pola?
  • Czy jest tylko JEDNA rzeczywisty na dzień na KPI?

Powodem, dla którego pytam, że chcesz upewnić się, że DOŁĄCZ występuje, jest tylko 1: 1.(Nie chcesz 5 Rzeczywiste łączenia z 5 Wag, dając rekordy 25 resultsing)

Niezależnie nieznaczne uproszczenie zapytaniu jest z pewnością możliwe ...

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' 

zakomentowanym potrzebne tylko wtedy, gdy linia potrzebujesz dodatkowych predykatów, aby zapewnić relację 1: 1 między danymi a wagami.


Jeśli nie można guarnatee tylko jedna wartość na bieżąco, i nie ma żadnych innych pól, aby dołączyć, możesz zmodyfikować sub_query wersja nieznacznie opiera ...

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 

Zakłada to, że AVG wagi jest poprawny, jeśli istnieje wiele wag na ten sam dzień.


EDIT: Ktoś po prostu głosowali na to tak myślałem, że poprawić ostateczną odpowiedź :)

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 

ten sposób unika JOIN i również tylko skanuje tabelę raz.

Opiera się na tym, że wartości NULL są ignorowane podczas obliczania wartości AVG().

+0

@Dems, wydaje mi się, że widziałem rzeczy zbyt skomplikowane, ponieważ dynamicznie dostarczane wartości są wartościami pól, a nie nazwami, tak jak to już zmieniłem powyżej. – ProfK

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

Zakładasz, że wartości pochodzą z dwóch różnych kolumn. W rzeczywistości pochodzą z tej samej kolumny w różnych zestawach rekordów. – ProfK

+0

Czy mógłbyś wtedy opublikować przykładowe dane? – Quassnoi

1

Jeśli mam zrozumieć problem spróbuj tego

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 
Powiązane problemy