Pro*_*ofK 16 sql t-sql sql-server statistics
我正在寻找一种方法,从具有相同列数的两行数据中导出加权平均值,其中平均值如下(借用Excel表示法):
(A1*B1)+(A2*B2)+...+(An*Bn)/SUM(A1:An)
Run Code Online (Sandbox Code Playgroud)
第一部分反映了与Excel的SUMPRODUCT()函数相同的功能.
我的问题是,我需要动态指定哪个行使用权重进行平均,以及权重来自哪一行,以及日期范围.
编辑:这比我想象的要容易,因为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
Run Code Online (Sandbox Code Playgroud)
Mat*_*lie 19
Quassnoi的答案显示了如何进行SumProduct,并且使用WHERE子句将允许您通过Date字段限制...
SELECT
SUM([tbl].data * [tbl].weight) / SUM([tbl].weight)
FROM
[tbl]
WHERE
[tbl].date >= '2009 Jan 01'
AND [tbl].date < '2010 Jan 01'
Run Code Online (Sandbox Code Playgroud)
更复杂的部分是您要"动态指定"哪个字段是[数据]以及哪个字段是[权重].简短的回答是,实际上你必须使用动态SQL.以下内容:
- 创建一个字符串模板
- 用适当的数据字段
替换[tbl] .data的所有实例 - 用适当的权重字段替换[tbl] .weight的所有实例
- 执行字符串
但是,动态SQL带来了它自己的开销.查询是否相对不频繁,或者查询本身的执行时间相对较长,这可能无关紧要.但是,如果它们很常见且很短,您可能会注意到使用动态sql会引入明显的开销.(更不用说小心SQL注入攻击等)
编辑:
在最新的示例中,您将突出显示三个字段:
当[KPI]为"重量Y"时,则[实际]使用加权因子.
当[KPI]为"Tons Milled"时,[Actual]是您要汇总的数据.
我有一些问题是:
我之所以要求你确保你所做的JOIN只是1:1.(你不希望5个实际加入5个权重,给出25个结果记录)
无论如何,稍微简化一下你的查询肯定是可能的......
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'
Run Code Online (Sandbox Code Playgroud)
如果您需要其他谓词以确保数据与权重之间存在1:1的关系,则只需注释掉该行.
如果您不能每个日期只保证一个值,并且没有任何其他字段可以加入,您可以稍微修改基于子查询的版本...
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
Run Code Online (Sandbox Code Playgroud)
这假设如果同一天有多个权重,则权重的AVG有效.
编辑:有人投了这个,所以我想我会改进最后的答案:)
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
Run Code Online (Sandbox Code Playgroud)
这样可以避免JOIN,也只会扫描一次表.
它依赖于NULL
在计算时忽略值的事实AVG()
.