MySQL 4.1 使用 UTF8 所遭遇的問題及解決方法

最近換成使用 MySQL 4.1 的人越來越多了,在許多地方也看到了哀嚎聲四起,遇到的狀況不外乎是原本的資料突然都變成不正常了,所以在這邊提供正解。

事實上這個問題不光是使用 UTF8 會有,照理說使用其他語系也會有相同的問題,例如如果我們原本用 GB2312 來存資料,結果現在升級後應該也會有問題。解決之道也很簡單:資料庫是什麼編碼,之後 Client 端程式就用什麼編碼和它溝通。

那要怎麼知道資料庫原本是什麼編碼呢?我在 commit 給 plog 中 adodb 的 patch 中包含了一段檢查 database encoding 的 code:


function _getDbDefaultEncoding($argDatabasename){
   if (!$argDatabasename) {
     return false;
   }

   // We use a SHOW CREATE DATABASE command to
   // show the original SQL character set when DB was
   // created.
   $result = mysql_query("SHOW CREATE DATABASE
        $argDatabasename", $this->_connectionID);
   if (mysql_num_rows($result) < 0 ) {
      // The specified db name is wrong!
      return false;
   }
   $dbInfo = mysql_fetch_row($result);
   $pattern = '/40100 DEFAULT CHARACTER SET (w+) /';
   if ( (preg_match($pattern, $dbInfo[1], $match) > 0) ) {
      return $match[1];
   }
   return false;
}

這個函式會檢查當初資料庫建立的編碼方式,並回傳回來,這樣可以確保我們不是冒然把 MySQL client 的 encoding 設定成 UTF-8 (這是目前看到大多數人的解法)。

接下來,在 SelectDB() 函式中,做以下修改:


function SelectDB($dbName)
{
  $this->databaseName = $dbName;
  if ($this->_connectionID) {
+   $dbEncoding =
+       $this->_getDbDefaultEncoding($dbName);
+   if ($dbEncoding) {
+     mysql_query("SET NAMES $dbEncoding",
+         $this->_connectionID);
+   }
     return @mysql_select_db($dbName, $this->_connectionID);
  }
  else return false;

這樣應該不管原本的編碼為何,讀和寫都不會有問題;唯一要注意的是,如果你的 MySQL 不是 4.1 以上,有些指令並不支援,所以這段 patch 不適用;我也替 pLog 寫了一個 isSupportUtf8() 的 function call, 可以用來檢查是否是 4.1 以上的版本,不過就不再這邊多加贅述了。

P.S. 請注意,如果你沒有學過 PHP,請不要問我該怎麼加入這段程式碼。請尋求你所使用的軟體之開發者的協助。