php_mysql、php_mysqli 與 pdo_mysql 的區別與選擇


本文最后更新時間:2014-9-28

結論:

使用pdo、自動化SQL(db-sql-maker-php)、查詢用PDO::query()、格式化用PDOStatement::fetchAll()。

php代碼的可移植性——從mysql遷移到其他數據庫(比如pgsql)時,php代碼不用修改:

  使用自動化SQL 結果:php代碼是否可移植
php_mysqli
php_mysqli
pdo
pdo

風格:

  書寫風格 面向對象 拋異常
php_mysqli   函數名用下划線,比如fetch_all  可以oop,也可以opp 否,只能false
pdo 函數名駝峰,比如fetchAll oop 可以throw,也可false或false加warnnig
php_pgsql 函數名用下划線,比如pg_fetch_all opp 否,只能false

使用php_mysql、php_mysqli 還是 pdo?

官方文檔:http://cn.php.net/manual/zh/mysqli.overview.php

php_mysql已經廢棄,不再討論。

從風格上看:

php_mysqli 可以oop,卻不支持拋異常……這能算oop么……函數名使用下划線也不符合現在的書寫規范。

所以使用PDO——簡潔。

從可移植性上看:

如果是私有項目,只用mysql,以后絕對不會遷移到其他數據庫,則可以使用php_mysqli。但前公司因為性能從jsp遷移到php,誰能保證以后不會換數據庫呢?

如果是開源項目或者會被其他站長使用的項目,建議使用pdo。因為用pdo的函數操作數據庫,與mysql還是pgsql無關。這樣其他站長可以使用其他數據庫。

pdo與php_mysqli的區別是:pdo提供通用的功能,這樣才能支持各個不同的數據庫,所以無法支持mysql特有的功能,比如多語句執行。但考慮到一般用不到特殊功能,所以影響不大。

pdo代碼:

<?php
$dsn = 'mysql:dbname=test;host=localhost'; //mysql
$user = 'root'; //mysql
$password = '1'; //mysql
$db = new PDO($dsn, $user, $password); //mysql
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = 'INSERT INTO `user` (`id`, `name`, `desc`) VALUES (\'2\',\'lucy\',\'hello\');'; //mysql

$dsn = 'pgsql:host=localhost;port=5432;dbname=test;user=root;password=1'; //pgsql
$db = new PDO($dsn); //pgsql
$sql = 'INSERT INTO schema1.user ("id", "name", "desc") VALUES (\'2\',\'lucy\',\'hello\');'; //pgsql

$stmt = $db->query($sql); //這以下mysql和pgsql是一樣的了
$stmt->setFetchMode(PDO::FETCH_ASSOC);
$r = $stmt->fetchAll();
?>

 

可以看出,使用pdo,如果數據庫從mysql遷移到pgsql,只需要改dsn,而sql語法不同可以通過SQL Maker來自動生成。

注:PDO的出現是為了可移植,卻把一個簡單的dsn連接數據庫做的都不一樣,直接破壞了可移植性……讓人很懷疑PDO的成熟度……

 

而如果使用php_mysqli,是無法遷移到php_pgsql的,因為函數完全不同。如下:

<?php
$mysqli = new mysqli('localhost', 'root', '1', 'test');
//$a = "hello world\fjim\n\r";
//$sql = 'INSERT INTO `user` (`id`, `name`, `desc`) VALUES (\'2\',\'lucy\',' . $mysqli->real_escape_string($a) . '\');';
$sql = 'SELECT `id`, `name`, `desc` FROM `user` LIMIT 2';
$r = $mysqli->query($sql);
$arr = $r->fetch_all();
var_dump($arr);
exit;
?>
<?php
$db = pg_connect("host=localhost port=5432 dbname=test user=root password=1");
//$a = "hello world\fjim\n\r";
//$sql = "INSERT INTO schema1.user VALUES ('2','lucy','". pg_escape_string($a) . '\');';
$sql = 'SELECT "id", "name", "desc" FROM schema1.user LIMIT 2';
$r = pg_query($db, $sql);
$arr = pg_fetch_all($r);
var_dump($arr);
exit;
?>

 

php_mysqli 與 pdo 性能對比:

todo

 

PDO使用說明:

PDO::prepare()

官方文檔:http://www.php.net/manual/zh/pdo.prepare.php

適用:多次查詢使用相同的條件字段和結果字段。這時候比PDO::query()性能高。

轉義:value無需手動轉義,PDO會自動進行轉義,不用擔心SQL注入。(開啟query log可以看到語句的確已轉義)

缺點:由於標識符(列名表名)是手寫的SQL,要自己加引用符反勾號`。

PDO::prepare()模擬處理的時候不會通過數據庫,所以不知道語法錯誤,不會返回錯誤或拋異常。什么時候是真處理,什么時候是模擬處理,官方手冊中未找到說明。輸入任意的SQL都不出錯……所以目前不要指望prepare返回錯誤,PDOStatement::execute()時才會返回錯誤。http://www.php.net/manual/zh/pdo.prepare.php

 

PDO::query()

官方文檔:http://www.php.net/manual/zh/pdo.query.php

適用:單次查詢的語句。

 

PDOStatement::setFetchMode()

http://www.php.net/manual/zh/pdostatement.setfetchmode.php

http://www.php.net/manual/zh/pdo.constants.php

默認模式:

View Code
<?php
$dsn = 'mysql:dbname=test;host=127.0.0.1';
$user = 'root';
$password = '1';

$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$r = $dbh->query('SELECT * FROM user');
var_dump($r);
foreach($r as $v) {
    var_dump($v);
}
?>

結果:

array(6) {
  ["id"]=>
  string(1) "1"
  [0]=>
  string(1) "1"
  ["name"]=>
  string(3) "jim"
  [1]=>
  string(3) "jim"
  ["desc"]=>
  string(5) "hello"
  [2]=>
  string(5) "hello"
}
array(6) {
  ["id"]=>
  string(1) "2"
  [0]=>
  string(1) "2"
  ["name"]=>
  string(4) "lucy"
  [1]=>
  string(4) "lucy"
  ["desc"]=>
  string(5) "hello"
  [2]=>
  string(5) "hello"
}
array(6) {
  ["id"]=>
  string(1) "3"
  [0]=>
  string(1) "3"
  ["name"]=>
  string(2) "mm"
  [1]=>
  string(2) "mm"
  ["desc"]=>
  string(5) "hello"
  [2]=>
  string(5) "hello"
}

PDO::FETCH_LAZY模式:

View Code
<?php
$dsn = 'mysql:dbname=test;host=127.0.0.1';
$user = 'root';
$password = '1';

$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $dbh->query('SELECT * FROM user');
$stmt->setFetchMode(PDO::FETCH_LAZY);
while ($row = $stmt->fetch()) {
    var_dump($row);
}
?>

結果:

object(PDORow)#3 (4) {
  ["queryString"]=>
  string(18) "SELECT * FROM user"
  ["id"]=>
  string(1) "1"
  ["name"]=>
  string(3) "jim"
  ["desc"]=>
  string(5) "hello"
}
object(PDORow)#3 (4) {
  ["queryString"]=>
  string(18) "SELECT * FROM user"
  ["id"]=>
  string(1) "2"
  ["name"]=>
  string(4) "lucy"
  ["desc"]=>
  string(5) "hello"
}
object(PDORow)#3 (4) {
  ["queryString"]=>
  string(18) "SELECT * FROM user"
  ["id"]=>
  string(1) "3"
  ["name"]=>
  string(2) "mm"
  ["desc"]=>
  string(5) "hello"
}

PDO::FETCH_ASSOC 關聯數組形式:

View Code
<?php
$dsn = 'mysql:dbname=test;host=127.0.0.1';
$user = 'root';
$password = '1';

$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $dbh->query('SELECT * FROM user');
$stmt->setFetchMode(PDO::FETCH_ASSOC);
while ($row = $stmt->fetch()) {
    var_dump($row);
}
?>

結果:

array(3) {
  ["id"]=>
  string(1) "1"
  ["name"]=>
  string(3) "jim"
  ["desc"]=>
  string(5) "hello"
}
array(3) {
  ["id"]=>
  string(1) "2"
  ["name"]=>
  string(4) "lucy"
  ["desc"]=>
  string(5) "hello"
}
array(3) {
  ["id"]=>
  string(1) "3"
  ["name"]=>
  string(2) "mm"
  ["desc"]=>
  string(5) "hello"
}

PDO::FETCH_NAMED 同 PDO::FETCH_ASSOC,連手冊上的說明都是一模一樣……

PDO::FETCH_NUM 不要列名,用數字做key:

View Code
<?php
$dsn = 'mysql:dbname=test;host=127.0.0.1';
$user = 'root';
$password = '1';

$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $dbh->query('SELECT * FROM user');
$stmt->setFetchMode(PDO::FETCH_NUM);
while ($row = $stmt->fetch()) {
    var_dump($row);
}
?>

結果:

array(3) {
  [0]=>
  string(1) "1"
  [1]=>
  string(3) "jim"
  [2]=>
  string(5) "hello"
}
array(3) {
  [0]=>
  string(1) "2"
  [1]=>
  string(4) "lucy"
  [2]=>
  string(5) "hello"
}
array(3) {
  [0]=>
  string(1) "3"
  [1]=>
  string(2) "mm"
  [2]=>
  string(5) "hello"
}

PDO::FETCH_BOTH 結果和 默認情況下一樣,只是程序寫法不一樣:

View Code
<?php
$dsn = 'mysql:dbname=test;host=127.0.0.1';
$user = 'root';
$password = '1';

$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $dbh->query('SELECT * FROM user');
$stmt->setFetchMode(PDO::FETCH_BOTH);
while ($row = $stmt->fetch()) {
    var_dump($row);
}
?>

結果:

View Code
array(6) {
  ["id"]=>
  string(1) "1"
  [0]=>
  string(1) "1"
  ["name"]=>
  string(3) "jim"
  [1]=>
  string(3) "jim"
  ["desc"]=>
  string(5) "hello"
  [2]=>
  string(5) "hello"
}
array(6) {
  ["id"]=>
  string(1) "2"
  [0]=>
  string(1) "2"
  ["name"]=>
  string(4) "lucy"
  [1]=>
  string(4) "lucy"
  ["desc"]=>
  string(5) "hello"
  [2]=>
  string(5) "hello"
}
array(6) {
  ["id"]=>
  string(1) "3"
  [0]=>
  string(1) "3"
  ["name"]=>
  string(2) "mm"
  [1]=>
  string(2) "mm"
  ["desc"]=>
  string(5) "hello"
  [2]=>
  string(5) "hello"
}

PDO::FETCH_OBJ 對象形式:

View Code
<?php
$dsn = 'mysql:dbname=test;host=127.0.0.1';
$user = 'root';
$password = '1';

$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $dbh->query('SELECT * FROM user');
$stmt->setFetchMode(PDO::FETCH_OBJ);
while ($row = $stmt->fetch()) {
    var_dump($row);
}
?>

結果:

object(stdClass)#3 (3) {
  ["id"]=>
  string(1) "1"
  ["name"]=>
  string(3) "jim"
  ["desc"]=>
  string(5) "hello"
}
object(stdClass)#4 (3) {
  ["id"]=>
  string(1) "2"
  ["name"]=>
  string(4) "lucy"
  ["desc"]=>
  string(5) "hello"
}
object(stdClass)#3 (3) {
  ["id"]=>
  string(1) "3"
  ["name"]=>
  string(2) "mm"
  ["desc"]=>
  string(5) "hello"
}

PDO::FETCH_COLUMN 只取某一列:

View Code
<?php
$dsn = 'mysql:dbname=test;host=127.0.0.1';
$user = 'root';
$password = '1';

$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $dbh->query('SELECT * FROM user');
$stmt->setFetchMode(PDO::FETCH_COLUMN, 1);
while ($row = $stmt->fetch()) {
    var_dump($row);
}
?>

結果:

string(3) "jim"
string(4) "lucy"
string(2) "mm"

PDO::FETCH_CLASS 把列名設置為一個新對象的屬性。官方文檔有例子:http://www.php.net/manual/zh/pdostatement.setfetchmode.php

PDO::FETCH_INTO 把列名設置為一個舊對象的屬性。

PDO::FETCH_KEY_PAIR 查兩個字段時,直接格式化成key value:

View Code
<?php
$dsn = 'mysql:dbname=test;host=127.0.0.1';
$user = 'root';
$password = '1';

$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $dbh->query('SELECT `id`,`name` FROM user');
$stmt->setFetchMode(PDO::FETCH_KEY_PAIR);
$r = $stmt->fetchAll();
var_dump($r);
?>

結果:

array(3) {
  [1]=>
  string(3) "jim"
  [2]=>
  string(4) "lucy"
  [3]=>
  string(2) "mm"
}

 

PDOStatement::fetch() 逐行讀取

官方文檔:http://www.php.net/manual/zh/pdostatement.fetch.php

PDOStatement::fetchAll() 格式化成:一個大數組

官方文檔:http://www.php.net/manual/zh/pdostatement.fetchall.php

PDOStatement::fetchObject() 逐行讀取,等於 PDOStatement::fetch() with PDO::FETCH_CLASS or PDO::FETCH_OBJ style。

官方文檔:http://www.php.net/manual/zh/pdostatement.fetchobject.php

又是alias……這不是添亂嗎!

 

注意:

PDO::errorCode()只返回數據庫連接錯誤。PDOStatement::errorCode()返回sql執行錯誤。

官方文檔:http://www.php.net/manual/zh/pdostatement.errorcode.php

 

參考資料:

http://board.phpbuilder.com/showthread.php?10379869-RESOLVED-PDO-prepare-can-anyone-suggest-query-prepare-statement-that-throws-exception


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM