de Quassnoi muestra cómo hacer el sumproduct, y el uso de una cláusula WHERE permitiría restringir por un campo Fecha ...
SELECT
SUM([tbl].data * [tbl].weight)/SUM([tbl].weight)
FROM
[tbl]
WHERE
[tbl].date >= '2009 Jan 01'
AND [tbl].date < '2010 Jan 01'
La parte más compleja es donde desea "especificar dinámicamente" qué campo es [datos] y qué campo es [peso]. La respuesta corta es que, de manera realista, tendrías que hacer uso de Dynamic SQL. Algo a lo largo de las líneas de:
- Crear una cadena de plantilla
- Sustituir todas las instancias de [FALLO] .data con el campo de los datos apropiados
- Sustituir todas las instancias de [FALLO] .Weight con el campo peso adecuado
- Ejecute la cadena
SQL dinámico, sin embargo, tiene su propia sobrecarga. Si las consultas son relativamente infrecuentes, o el tiempo de ejecución de la consulta en sí es relativamente largo, esto puede no importar. Sin embargo, si son comunes y cortos, es posible que observe que el uso de sql dinámico presenta una sobrecarga notable. (Por no mencionar teniendo cuidado de los ataques de inyección SQL, etc.)
EDIT:
En su ejemplo lastest resalta tres campos:
Cuando el [KPI] es "Peso Y ", luego [Real] el factor de ponderación a usar.
Cuando [KPI] es "Toneladas", entonces [Real] es el Dato que desea agregar.
Algunas de las preguntas que tengo son:
- ¿Hay otros campos?
- ¿Solo hay UNO real por fecha por KPI?
La razón por la que pregunto es por que desea asegurarse de que la UNIÓN que usted hace sea solo 1: 1.(Usted no quiere unirse a 5 datos reales con 5 pesos, dando registros 25 resultsing)
En cualquier caso, una ligera simplificación de la consulta es ciertamente posible ...
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'
La línea comentada a cabo sólo es necesario si necesita predicados adicionales para asegurar una relación 1: 1 entre sus datos y los pesos.
Si no puede guarnatee solo un valor por fecha, y no tiene ningún otro campo para unirse en, puede modificar su versión basada sub_query poco ...
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
Esto supone que la AVG del peso es válida si hay pesos múltiples para el mismo día.
EDIT: Alguien acaba de votar para esto, así que pensé en mejorar la respuesta final :)
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
Esto evita la unión y también sólo analiza la mesa una vez.
Se basa en el hecho de que NULL
valores se ignoran al calcular el AVG()
.
¿De qué manera el intervalo de fechas entre en ella? ¿Cuántas columnas, algunas o muchas? ¿Se ha corregido el número de columnas? –
@martin, solo una columna. Solía ser uno por KPI, pero eso no fue divertido. El rango de fechas es para un período de informe. – ProfK
¿La declaración anterior se considera un CTE? Si no, ¿cómo podrías convertir eso en un CTE? ¿Nadie? – PositiveGuy