2010-03-15 35 views
5

Tengo una gran tabla (de sitios) con varias columnas numéricas, por ejemplo, a través de f. (Estas son clasificaciones de sitios de diferentes organizaciones, como alexa, google, quantcast, etc. Cada una tiene un rango y formato diferente; son descargas directas desde las DB externas).mysql: Promedio sobre varias columnas en una fila, ignorando nulos

Para muchos de los registros, uno o más de estas columnas es nulo, porque el DB externo no tiene datos para él. Todos cubren diferentes subconjuntos de mi DB.

Quiero que la columna t sea su promedio ponderado (cada uno de ... f tiene pesos estáticos que asigno), ignorando los valores nulos (que pueden ocurrir en cualquiera de ellos), excepto que son nulos si son todos nulos .

Preferiría hacer esto con un simple cálculo de SQL, en lugar de hacerlo en el código de la aplicación o usar un gran bloque anidado feo para manejar cada permutación de nulos. (Dado que tengo un número creciente de columnas para promediar a medida que agrego más fuentes de DB externas, esto sería exponencialmente más feo y propenso a errores.)

Usaría AVG pero eso es solo para grupos, y esto es w/en un registro. Los datos son semánticamente anulables, y no quiero promediar en algún valor "promedio" en lugar de los nulos; Solo quiero contar las columnas para las que hay datos.

¿Hay una buena manera de hacerlo?

Idealmente, lo que quiero es algo así como UPDATE sites SET t = AVG(a*@a_weight,b*@b_weight,...) donde los valores nulos simplemente se ignoran y no se produce ninguna agrupación.

EDIT: Lo que terminé usando, basada en la furgoneta de la adición y en los promedios ponderados correctos (suponiendo que a ya se ha normalizado, según sea necesario, en este caso a un flotador 0-1 (1 = mejor):

UPDATE sites 
SET t = (@a_weight * IFNULL(a, 0) + ...)/(IF(a IS NULL, 0, @a_weight) + ...) 
WHERE (IF(a IS NULL, 0, 1) + ...) > 0 

Respuesta

3
UPDATE sites 
     --// TODO: you might need to round it depending on your type 
SET  t =(COALESCE(a, 0) + 
      COALESCE(b, 0) + 
      COALESCE(c, 0) + 
      COALESCE(d, 0) + 
      COALESCE(e, 0) + 
      COALESCE(f, 0) 
      )/
      ((CASE WHEN a IS NULL THEN 0 ELSE 1 END CASE) + 
      (CASE WHEN b IS NULL THEN 0 ELSE 1 END CASE) + 
      (CASE WHEN c IS NULL THEN 0 ELSE 1 END CASE) + 
      (CASE WHEN d IS NULL THEN 0 ELSE 1 END CASE) + 
      (CASE WHEN e IS NULL THEN 0 ELSE 1 END CASE) + 
      (CASE WHEN f IS NULL THEN 0 ELSE 1 END CASE) 
      ) 
WHERE 0<>((CASE WHEN a IS NULL THEN 0 ELSE 1 END CASE) + 
      (CASE WHEN b IS NULL THEN 0 ELSE 1 END CASE) + 
      (CASE WHEN c IS NULL THEN 0 ELSE 1 END CASE) + 
      (CASE WHEN d IS NULL THEN 0 ELSE 1 END CASE) + 
      (CASE WHEN e IS NULL THEN 0 ELSE 1 END CASE) + 
      (CASE WHEN f IS NULL THEN 0 ELSE 1 END CASE) 
      ) 

usted podría utilizar COALESCE también en las otras partes, pero esto no va a manejar el caso cuando se tiene una clasificación con valor 0 adecuadamente porque va a ser excluido. la cláusula WHERE evita DivideByZero, pero es posible que necesite tiene declaración adicional UPDATE para manejar este caso , si no hay calificación para la entrada.

+0

Creo que IFNULL es una alternativa más clara que COALESCE, pero es equivalente. IF (a IS NULL, 0,1) es similarmente más simple que tu CASE. De lo contrario, creo que esto hace todo lo que quería, básicamente estás poniendo a cero las columnas nulas y quitándola del denominador, lo cual es lo más inteligente y algo en lo que debería haber pensado. :-PAG – Sai

Cuestiones relacionadas