年初的時候一直在做一個網站MSSQL2000 -> MySQL5的遷移工作,因為采用了不同的程序系統,所以主要問題在數據的遷移。由於2個系統數據庫結構差異非常大,不方便采取SQL SERVER裡導入MYSQL的ODBC數據源的功能(也不推薦這麼做,字段類型等不同點會搞死人的~),因此就在WINDOWS下自己寫PHP腳本從SQLSERVER裡讀數據,然後按照新系統的需要處理之後插入MYSQL裡面,靈活也比較方便。實際過程主要有下面幾個問題:
1、數據庫的連接,主要是連接SQL SERVER。主要有3種方法:
1.1 利用PHP中的mssql_系列函數,這個與使用mysql_系列函數類似,不過要打開php.ini中相關擴展(extension=php_mssql.dll)。
1.2 利用ODBC連接,由於抽象了具體數據庫,所以沒有辦法利用數據表字段名=>數組鍵名的特性,在針對具體應用時不是很方便,代碼形式:
$conn = odbc_connect("datasource","username","password");
$sql = "select * from news";
$cur= odbc_exec($conn, $sql);
while(odbc_fetch_row($cur)){
$field1 = odbc_result($cur,1);
$field2 = odbc_result($cur,2);
//do something
}
1.3 使用PDO - PHP5中加入數據對象抽象層,作為官方推出的數據訪問接口,優點有很多,比如支持參數綁定以防止SQL注入;對於不同數據庫加載不同驅動即可,程序代碼是一致的,便於移植等等,相信應該是大勢所趨。不過由於用了PHP5全新的面向對象特性,需要PHP5的支持,5.1可以直接使用,5需要裝PECL,另外還要修改PHP.ini,增加:extension=php_pdo_mysql.dll和extension=php_pdo_mssql.dll,實際代碼如下:
try {
$DBH=new PDO("mssql:dbname=XXX;host=localhost",
"root", "password");//Connect to DB
} catch (PDOException $e) {
print "Error!: " . $e->getMessage();//Error Message
die();
}
$stmt = $DBH->prepare("SELECT * FROM news");//Stmt Handle $stmt
if ($stmt->execute()) {
while ($row = $stmt->fetch()) {
//do something
}
}
$stmt2 = $mssql->prepare("INSERT INTO news
(title,author) VALUES (:title, :author)");
$stmt2->bindParam(':title', $title);
$stmt2->bindParam(':author', $author);
$stmt2->execute();
$DBH = null;// Close Connection這裡要提醒下的是MSSQL裡面是沒有MYSQL中LIMIT這個語法的。
2、TEXT字段被截斷的問題。
上面嘗試了3種連接數據庫方法,是因為當初連上MSSQL後SELECT出來的數據總是只有4K長度,以為是連接方式限制導致的,所以換了幾種都是這樣,最後查了資料才知道,是php.ini裡面這2句配置的問題:
; Valid range 0 - 2147483647. Default = 4096.
mssql.textlimit = 4096
; Valid range 0 - 2147483647. Default = 4096.
mssql.textsize = 4096
把4096改成-1(代表無限制)即可,也可以使用mssql_query("SET TEXTSIZE 65536");來實現。
3.兩種數據庫字段類型不同的問題有2個地方需要講一下,一個是字段支持最大長度要注意,以免插入數據庫時候被截斷,另外一個就是日期格式的問題了,我比較喜歡用UNIX時間戳。在連MSSQL時候可以用 "select unix_timestamp(created) from news" 來實現MSSQL裡面DATETIME到MYSQL裡時間戳的轉換。不過遷移時候一次要提取表中所有字段,像上面這種方法就沒有簡單的"select * from news"簡潔,需要羅列所有字段。實際可以直接SELECT出來,得到的是一個字符串,比如在MSSQL裡面是2006-01-01 12:01,取出來的字符串是"2006 一月 01 12:01"(有些奇怪,不知道為什麼會產生中文)。用下面這個函數可以轉換成時間戳:
function ConvertTime($timestring){
if($timestring == null){
return 0;
}
$time = explode(" ",$timestring);
$year = $time[0];
switch ($time[1]){
case "一月":$month = "1";break;
case "二月":$month = "2";break;
case "三月":$month = "3";break;
case "四月":$month = "4";break;
case "五月":$month = "5";break;
case "六月":$month = "6";break;
case "七月":$month = "7";break;
case "八月":$month = "8";break;
case "九月":$month = "9";break;
case "十月":$month = "10";break;
case "十一月":$month = "11";break;
case "十二月":$month = "12";break;
default:break;
}
$day = $time[2];
$h = 0;
$m = 0;
$s = 0;
if(!empty($time[3])){
$time2 = explode(":",$time[3]);
$h = $time2[0];
$i = $time2[1];
}
//return date("Y-m-d H:i:s",mktime($h,$i,$s,$month,$day,$year));
return mktime($h,$i,$s,$month,$day,$year);
}最後轉換腳本寫完了可以在CMD窗口裡面用php.exe abc.php來執行,這種方式是沒有超時時間的,適合遷移大批量數據。
基本上就是這些內容,希望對大家有幫助。
Update(06/05/05):關於時間戳的轉換,在php.ini中加上
mssql.datetimeconvert = Off
後就能得到類似2006-01-01 12:01不帶有中文的格式了。