在自主開發的多個系統中都用到了分類演算法,每個資料都可能被分到幾個類別中,對於詳細的分值可以簡單的放在一個冗餘表,但是在原始資料表中還是要記錄每條資料所屬的類別。這就有幾個比較棘手的問題,一是對於類別的查詢,二是對於類別的新增。有以下幾個解決方法:

  1)每個類別都記錄成一個欄位來標記是否屬於:這樣查詢的時候後面要跟where條件,如果是查詢多個類別就要帶幾個條件,查詢效率一般。但新增類別的時候就比較麻煩,既要修改資料庫欄位,又要修改程式。 

  2)把類別都記錄在一個欄位:這樣新增類別簡單了,但是查詢的時候就不太方便,如果每次值查詢一個類別,那麼用like還是可以接受,但如果同時查詢多個類別,那sql就要寫好幾個like,這個效率自然更不能接受。有個折衷的辦法,就是把這些類別按一定順序排放,這樣查詢的時候一個like還可以處理。

  3)同樣類別還是記錄在一個欄位:這個還可以利用全文索引來查詢,這樣也可以對該欄位進行復合查詢,但是mysql自帶的fulltext索引只能用於myisam,如果用lucene或sphinx就需要額外配置,稍微麻煩點。

    上面幾個方式我想了下感覺都不是很滿意,今天突然想到之前做的演算法題中有個常用的技巧,就是利用二機制來解決問題。對於類別是否屬於就是一個0或1的標誌,多個類別就記錄多位即可。這個問題就迎刃而解了。

    假設目前分類有CBA三個類別,至於為什麼倒過來,後面會有說明。現在有四條資料,所屬類別分別為001, 010, 100, 101,最後一個資料是為了表示有的會被分到多個類別。

    類別的查詢方法:既然轉換成二進位制,自然是考慮使用位運算,這裡以mysql的函式語法為例。假設要查詢A類的資料,那麼條件為categories & b`001`,這樣資料1和資料4就會匹配上。查詢屬於多個類別的也很簡單,查詢屬於A或B類,條件為categories &  b`011`。查詢A且B類,條件為categories &  b`011` = b`011`。

    類別的新增方法:由於位運算的低位始終在後面,所以新新增的類別就要加到高位上,比如增加D類別,原資料分類就相當與預設都沒分到D類中,原始資料也不用做任何修改。

    這個方法對於類別是否屬於的判斷非常高效,計算機只要做一次運算就能拿到結果,但mysql對於位數也是有限制,64位系統下最多隻能有64位長度,使用時注意一下。將十進位制轉換成二進位制形式可以使用函式bin(n)。