Quassnoi 的回答显示了如何进行 SumProduct,并且使用 WHERE 子句将允许您通过日期字段进行限制...
SELECT
SUM([tbl].data * [tbl].weight) / SUM([tbl].weight)
FROM
[tbl]
WHERE
[tbl].date >= '2009 Jan 01'
AND [tbl].date < '2010 Jan 01'
更复杂的部分是您要“动态指定”什么字段是 [data] 以及什么字段是 [weight]。简短的回答是,实际上您必须使用动态 SQL。类似于:
- 创建字符串模板
- 将 [tbl].data 的所有实例替换为适当的数据字段
- 将 [tbl].weight 的所有实例替换为适当的权重字段
- 执行字符串
然而,动态 SQL 也有它自己的开销。是查询相对不频繁,还是查询本身的执行时间比较长,这可能无关紧要。但是,如果它们很常见且很短,您可能会注意到使用动态 sql 会带来显着的开销。 (更别提小心SQL注入攻击等了)
编辑:
在您的最新示例中,您突出显示了三个字段:
当 [KPI] 为“权重 Y”时,则 [实际] 要使用的权重因子。
当 [KPI] 为“Tons Milled”时,[Actual] 是您要聚合的数据。
我的一些问题是:
- 还有其他字段吗?
- 每个 KPI 每个日期是否只有一个实际值?
我问的原因是你想确保你所做的 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'
仅当您需要额外的谓词来确保数据和权重之间的 1:1 关系时才需要注释掉的行。
如果您不能保证每个日期只有一个值,并且没有任何其他字段可以加入,您可以稍微修改基于 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 是有效的。
编辑:有人刚刚投了赞成票,所以我想我会改进最终答案:)
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,也只扫描表一次。
它依赖于在计算AVG() 时忽略NULL 值这一事实。