2010-03-15 592 views
5

我有一個包含多個數字列的大型表格(稱爲站點) - 稱爲a至f。 (這些是來自不同組織的網站排名,如alexa,google,quantcast等等,每個組織都有不同的範圍和格式;它們是從外部DB直接轉儲的。)mysql:一行中的多個列的平均值,忽略空值

對於許多記錄,一個或多個這些列的值爲空,因爲外部數據庫沒有數據。它們都覆蓋我的數據庫的不同子集。

我希望列t是它們的加權平均值(每個a..f都有我分配的靜態權重),忽略空值(可能出現在它們中的任何一箇中),除非它們全爲空。

我寧願用簡單的SQL計算來完成此操作,而不是在應用程序代碼中執行此操作,或者使用一些巨大的醜陋嵌套if塊來處理空值的每個置換。 (鑑於我有越來越多的列要平均,因爲我添加了更多的外部數據源,這將成倍地變得更加醜陋,並且容易出錯。)

我會使用AVG,但那隻適用於group by,這是在一個記錄中。數據在語義上可以爲空,我不想用一些「平均」值代替空值。我只想計算那些數據在那裏的列。

有沒有很好的方法來做到這一點?

理想情況下,我想要的是類似於UPDATE sites SET t = AVG(a*@a_weight,b*@b_weight,...),其中任何空值都被忽略,並且不會發生分組。

編輯:我結束了有什麼用,根據麪包車的和添加在正確的加權平均值(假設a需要已經標準化,在這種情況下爲float 0-1(1 =越好):

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

回答

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

你可以使用COALESCE也是在其他地方,但是當你有與價值0正確的評級,這將無法處理的情況下,因爲它會被排除在外。該WHERE條款避免了DivideByZero,但您可能需要有額外的UPDATE聲明來處理這種情況,如果沒有評分的條目。

+0

我認爲IFNULL是COALESCE的一個更清晰的替代方案,但大致相當。 IF(一個IS NULL,0,1)與CASE相似。否則,我認爲這是我所想做的一切 - 基本上你將零列歸零並將其從分母中移出,這是明智的做法,也是我應該想到的。 :-P – Sai 2010-03-15 09:43:38