zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    软件工程 speedsnail 第二次冲刺2
    软件工程 speedsnail 第二次冲刺1次
    软件工程 speedsnail 冲刺9
    软件工程 speedsnail 冲刺8
    软件工程 speedsnail 冲刺7
    软件工程 speedsnail 冲刺6
    软件工程 speedsnail 冲刺5
    软件工程 speedsnail 冲刺4
    软件工程 speedsnail 冲刺3
    软件工程 speedsnail 冲刺2
  • 原文地址:https://www.cnblogs.com/sink_cup/p/php_mysql_mysqli_pdo_mysql.html
Copyright © 2011-2022 走看看