2010-03-15 13 views
5

Ho una tabella di grandi dimensioni (di siti) con diverse colonne numeriche, ad esempio attraverso f. (Queste sono le classifiche di siti di organizzazioni diverse, come alexa, google, quantcast, ecc. Ognuno ha una gamma e un formato diversi, sono disconnessioni dirette dai DB esterni.)mysql: media su più colonne in una riga, ignorando i valori nulli

Per molti record, uno o più di queste colonne è nullo, perché il DB esterno non ha dati per questo. Coprono tutti diversi sottoinsiemi del mio DB.

Voglio che la colonna t sia la loro media ponderata (ognuno di a..f ha pesi statici che assegno), ignorando i valori nulli (che possono verificarsi in uno di essi), eccetto il null se sono tutti nulli .

Preferirei farlo con un semplice calcolo SQL, piuttosto che farlo nel codice app o usando un enorme blocco nidissimo se gestibile per gestire ogni permutazione dei valori nulli. (Dato che ho un numero crescente di colonne su cui aggiungere più sorgenti esterne DB, questo sarebbe esponenzialmente più brutto e soggetto a bug.)

Io userei AVG ma è solo per gruppo, e questo è w/in un record. I dati sono semanticamente annullabili e non voglio fare la media in un valore "medio" al posto dei valori nulli; Voglio solo contare le colonne per le quali ci sono i dati.

C'è un buon modo per farlo?

Idealmente, quello che voglio è qualcosa come UPDATE sites SET t = AVG(a*@a_weight,b*@b_weight,...) dove qualsiasi valore nullo viene semplicemente ignorato e nessun raggruppamento sta accadendo.

EDIT: Quello che ho finito per usare, basati su van e aggiungendo in corrette medie ponderate (supponendo che a è già stato normalizzato come necessario, in questo caso ad un galleggiante 0-1 (1 = migliore):

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

risposta

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) 
      ) 

si potrebbe utilizzare COALESCE anche nelle altre parti, ma questo non sarà gestire il caso quando si ha un rating con valore di 0 correttamente perché esso sarà escluso. la clausola WHERE evita DivideByZero, ma potrebbe essere necessario avere ulteriori istruzioni UPDATE per gestire questo caso , se non ci sono voti per la voce.

+0

Penso che IFNULL sia un'alternativa più chiara a COALESCE, ma equivalente. IF (a IS NULL, 0,1) è similmente più semplice del tuo CASE. Altrimenti, penso che questo faccia tutto ciò che volevo - fondamentalmente stai azzerando le colonne nulle e portandole fuori dal denominatore, che è la cosa più intelligente da fare e qualcosa che dovrei aver pensato. :-P – Sai

Problemi correlati