pdo經常使用的sql寫法

 mysql_*函數已通過時,至關一段時間以來,mysql_*函數在其餘SQL數據庫編程接口方面已經有所差異;它不支持預處理,存儲過程,事務等一些現代數據庫設計思想,SQL語句字符串轉義函數 mysql_real_escape_string() 和 拼接SQL語句的編程方法 已通過時而且很容易出錯。最近一段時間裏,它缺少開發者的關注,缺乏維護將可能致使一些安全問題不能被即時修復,或者在適配新版本的MySQL的時候不能 正常工做,這成爲mysql_*函數面臨的的另外一個問題。PHP社區最近也對mysql_*函數給出不建議使用的建議,也有可能在將來的版本中最終被棄用 (不過不用過於擔憂,這可能還須要很長一段時間)。php

        PDO擁有更好的編程接口,你可使用它寫出更加簡潔,高效,安全的代碼。PDO還爲不一樣的SQL數據庫提供了不一樣的驅動,方便你 使用新的數據庫而不用再學習不一樣的編程接口。與拼接SQL語句構造查詢語句不一樣,綁定參數能夠簡潔方便的構造出更加安全的查詢語句,使用綁定參數的方法在 屢次類似語句查詢(僅僅某個參數不一樣)中也能夠提升很多性能。PDO在錯誤處理方面也提供了多種方法。mysql_*函數缺少一致的處理,與PDO的異常 模式相比,或者說沒有處理異常,使用PDO,你能夠獲得一致的錯誤處理,這將節省您大量的時間來跟蹤問題。mysql

        在當前的PHP版本中,PDO模塊是默認安裝啓用的,可是在使用PDO前你還須要安裝另外兩個軟件包,一個是pdo_mysql數據庫驅動程序,另一個是相似php-mysql的mysql驅動程序。sql

2.鏈接MySQL


2.1. 之前的方式:

$link = mysql_connect('localhost', 'user', 'pass');mysql_select_db('testdb', $link);mysql_set_charset('UTF-8', $link);

2.2. 新的方式:

* 建立一個PDO對象,參數包括 DSN, username, password 和 一個驅動選項的數組(可忽略)。
* DSN其實就是一個告訴PDO該使用哪種數據庫驅動 和 一些鏈接信息的字符串,瞭解更多 PDO MYSQL DSN .數據庫

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');

注意確保DSN中設置了字符編碼信息,不然將可能返回字符編碼設置錯誤的信息,出於安全考慮,DSN最好包括字符編碼信息設置。 編程

         你也能夠在第四個參數數組裏填寫一些驅動選項,建議將 PDO異常模式(下文講解) 和 關閉預處理模擬(默認打開的,僅對於舊版本MySQL有用)兩個參數加入到第四個參數數組中。數組

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password', array(PDO::ATTR_EMULATE_PREPARES => false,PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

你也能夠在建立PDO對象後再經過setAttribute方法設置相應選項。安全

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

3.錯誤處理


3.1. mysql_*函數的錯誤處理

//connected to mysql$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));

OR die()是個不錯的錯誤處理方法,可是會所以結束頁面,將錯誤信息呈現到用戶面前,這多是咱們不想看到的結果。
PDO有三種錯誤處理模式服務器

  1. PDO::ERRMODE_SILENT # 和 mysql_*函數相似,檢查代碼並查看 $db->errorInfo(); 獲取詳細信息。app

  2. PDO::ERRMODE_WARNING # 拋出PHP警告。數據庫設計

  3. PDO::ERRMODE_EXCEPTION #拋出 PDOException 異常,在我認爲,這是咱們應該使用的模式, 這和 die(mysql_error()); 相似,可是它能夠捕獲並拋出具體異常信息。

3.2. code:

try {    //connect as appropriate as above    $db->query('hi'); //invalid query!} catch(PDOException $ex) {    echo "An Error occured!"; //user friendly message    some_logging_function($ex->getMessage());}

注意:你能夠不用當即執行並捕獲異常,你能夠在任何合適的時候隨時捕獲。

 
 
 
 
 
 

function getData($db) {   $stmt = $db->query("SELECT * FROM table");   return $stmt->fetchAll(PDO::FETCH_ASSOC);}//then much latertry {   getData($db);} catch(PDOException $ex) {   //handle me.}

若是你不想使用try/catch來處理異常,就像使用OR die()那樣處理,在production模式下關閉display_errors選項便可。

4.簡單的查詢語句(SELECT


4.1. mysql_*代碼:

 
 
 
 
 
 


$result = mysql_query('SELECT * from table') or die(mysql_error());$num_rows = mysql_num_rows($result);while($row = mysql_fetch_assoc($result)) {   echo $row['field1'].' '.$row['field2']; //etc...}

4.2. PDO代碼:

foreach($db->query('SELECT * FROM table') as $row) {    echo $row['field1'].' '.$row['field2']; //etc...}

query() 方法返回了一個 PDOStatement 對象,你能夠經過以下方法獲取結果:

 
 
 
 
 
 

$stmt = $db->query('SELECT * FROM table');while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {    echo $row['field1'].' '.$row['field2']; //etc...}

或者

$stmt = $db->query('SELECT * FROM table');$results = $stmt->fetchAll(PDO::FETCH_ASSOC);//use $results

4.3. # Fetch Modes

注意 fetch()fetchAll() 代碼中的PDO::FETCH_ASSOC ,它高速 PDO 以關聯數組的形式返回 鍵,值;其餘好比PDO::FETCH_NUM模式,則返回數值鍵值的數組,默認模式是 PDO::FETCH_BOTH 則返回前面二者的集合,既有數值鍵值的數組,又有關聯數組。PDO也能夠獲取數據返回對象PDO::FETCH_OBJPDO::FETCH_CLASSPDO::FETCH_BOUND,bindColumn方法等更多內容,請閱讀: PDOStatement Fetch documentation

4.4. # 獲取數據行數(Getting Row Count)

代替 mysql_num_rows 方法,你可使用 PDOStatement對象的rowCount();方法。

$stmt = $db->query('SELECT * FROM table');$row_count = $stmt->rowCount();echo $row_count.' rows selected';

注意:官方文檔稱此函數僅適用於返回 `UPDATE`, `INSERT`, `DELETE`操做的`affected rows`,而 `SELECT`操做,僅對於`PDO_MYSQL` 驅動,此函數一樣適用(謹記),在操做其餘數據庫的時候尤爲注意。

4.5. # 獲取最後操做ID(Getting the Last Insert Id)

mysql_*代碼:

$result = mysql_query("INSERT INTO table(firstname, lastname) VALUES('John', 'Doe')") or die("Insert Failed ".mysql_error());$insert_id = mysql_insert_id();

4.6. PDO代碼:

$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");$insertId = $db->lastInsertId();

5.執行 INSERT, UPDATE, DELETE 操做


5.1. mysql_*代碼:

$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());$affected_rows = mysql_affected_rows($result);echo $affected_rows.' were affected';

5.2. PDO代碼:

$affected_rows = $db->exec("UPDATE table SET field='value'");echo $affected_rows.' were affected'

DELETEINSERT 操做一樣適用。

6.運行帶有查詢參數的語句(Running Statements With Parameters)


        對於 不攜帶任何參數的查詢語句,咱們可使用 query方法處理SELECT操做,使用exec方法處理 INSERTUPDATEINSERT操做,而對於攜帶查詢參數的語句,你應該使用綁定參數的方法來安全的處理這些操做。

6.1. mysql_*代碼:

$results = mysql_query(sprintf("SELECT * FROM table WHERE id='%s' AND name='%s'",        mysql_real_escape_string($id), mysql_real_escape_string($name))) or die(mysql_error());$rows = array();while($row = mysql_fetch_assoc($results)){    $rows[] = $row;}

6.2. PDO代碼:

$stmt = $db->prepare("SELECT * FROM table WHERE id=? AND name=?");$stmt->execute(array($id, $name));$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

        prepare方法將查詢語句發送到服務器,以「?」做爲參數佔位符進行編譯,execute方法將查詢參數發送到服務器,運行以前編譯好的查詢語句。由於 查詢語句查詢參數 是分開發送的,因此在參數裏的SQL語句是不可能被執行的,因此不會發生 SQL注入,這是一種比鏈接字符串構造SQL語句更加安全的解決方法。

注意 當你使用**綁定參數**的時候,不要對"?"佔位符使用引號(SQL語句原來是對參數使用引號的),由於參數類型是在execute方法的時候肯定的,因此在prepare的時候沒必要對佔位符使用引號。

        還有一些綁定參數的方法,bindValue方法能夠分別綁定每一個參數來代替execute方法的數組方式,同時還分別設置每一個參數的類型。

$stmt = $db->prepare("SELECT * FROM table WHERE id=? AND name=?");$stmt->bindValue(1, $id, PDO::PARAM_INT);$stmt->bindValue(2, $name, PDO::PARAM_STR);$stmt->execute();$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

6.3. #命名佔位符

若是你有許多參數須要綁定,不要使用問號佔位符,以防混淆出錯,你可使用命名佔位符代替問號佔位符

$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");$stmt->bindValue(':id', $id, PDO::PARAM_INT);$stmt->bindValue(':name', $name, PDO::PARAM_STR);$stmt->execute();$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

你也可使用execute方法,以數組的方式綁定參數:

$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");$stmt->execute(array(':name' => $name, ':id' => $id));$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

6.4. #INSERT, DELETE, UPDATE 預處理查詢

INSERT, DELETE, UPDATE 預處理語句的使用和SELECT相似,咱們舉幾個例子:

$stmt = $db->prepare("INSERT INTO table(field1,field2,field3,field4,field5) VALUES(:field1,:field2,:field3,:field4,:field5)");$stmt->execute(array(':field1' => $field1, ':field2' => $field2, ':field3' => $field3, ':field4' => $field4, ':field5' => $field5));$affected_rows = $stmt->rowCount();

$stmt = $db->prepare("DELETE FROM table WHERE id=:id");$stmt->bindValue(':id', $id, PDO::PARAM_STR);$stmt->execute();$affected_rows = $stmt->rowCount();

$stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");$stmt->execute(array($name, $id));$affected_rows = $stmt->rowCount();

6.5. #在預處理中使用SQL函數

=>無效方法:

//THIS WILL NOT WORK!$time = 'NOW()';$name = 'BOB';$stmt = $db->prepare("INSERT INTO table(`time`, `name`) VALUES(?, ?)");$stmt->execute(array($time, $name));

=>正確方法

$name = 'BOB';$stmt = $db->prepare("INSERT INTO table(`time`, `name`) VALUES(NOW(), ?)");$stmt->execute(array($name));

=>你也能夠在SQL函數裏綁定參數:

$name = 'BOB';$password = 'badpass';$stmt = $db->prepare("INSERT INTO table(`hexvalue`, `password`) VALUES(HEX(?), PASSWORD(?))");$stmt->execute(array($name, $password));

=>可是不能做爲LIKE的參數:

//THIS DOES NOT WORK$stmt = $db->prepare("SELECT field FROM table WHERE field LIKE %?%");$stmt->bindParam(1, $search, PDO::PARAM_STR);$stmt->execute();

=>正確使用LIKE並綁定參數的方法:

$stmt = $db->prepare("SELECT field FROM table WHERE field LIKE ?");$stmt->bindValue(1, "%$search%", PDO::PARAM_STR);$stmt->execute();

注意這裏使用的是bindValue而不是bindParam,不然會發生PDOException或致命錯誤。

6.6. #使用循環運行預處理語句

預處理語句能夠一次設置,屢次調用,由於僅在第一次傳入的時候編譯,所以在後來的屢次調用中提升了很多效率。
         典型的應用就是bindParambindParambindValue的不一樣之處在於,它不是綁定了參數的值,而是綁定參數變量自己,所以,若是參數變量變化了,那麼在execute處理的時候,查詢也將相應變化。

$values = array('bob', 'alice', 'lisa', 'john');$name = '';$stmt = $db->prepare("INSERT INTO table(`name`) VALUES(:name)");$stmt->bindParam(':name', $name, PDO::PARAM_STR);foreach($values as $name) {   $stmt->execute();}

7.PDO中的事務(Transactions)


注意:調用`beginTransaction()`方法即自動關閉了`自動提交`。

 
 
 
 
 
 




try {    $db->beginTransaction();    $db->exec("SOME QUERY");    $stmt = $db->prepare("SOME OTHER QUERY?");    $stmt->execute(array($value));    $stmt = $db->prepare("YET ANOTHER QUERY??");    $stmt->execute(array($value2, $value3));    $db->commit();} catch(PDOException $ex) {    //Something went wrong rollback!    $db->rollBack();    echo $ex->getMessage();}
相關文章
相關標籤/搜索