******* 簡易程式範例 *******

本程式動作順序(類似 PEAR::MDB2 範例的動作):

  1. 新增兩個 TABLE 的資料
  2. 從 TABLE 取出資料後列出
  3. 刪除兩個 TABLE 的所有資料

若要讓此程式可以正常運作,必須先建立資料庫以及相關資料表,
資料表可以如以下的方式建立:
(mysql 與 pgsql 皆同,差異在 pgsql 不能用 user 當 column 名稱)

CREATE TABLE users(room VARCHAR(8) NOT NULL,user_id VARCHAR(10) NOT NULL);
CREATE TABLE list(user_id VARCHAR(10) NOT NULL,block VARCHAR(10) NOT NULL);

db.inc 內容如下:

define(“RDBMS","資料庫種類"); // 例如 mysql 或 pgsql
define(“USER","資料庫使用者名稱");
define(“PASS","資料庫使用者密碼");
define(“HOST","資料庫所在主機"); // 例如 localhost
define(“DBNAME","資料庫名稱");

程式本體:


# 測試使用 PDO 執行 INSERT、DELETE、UPDATE、SELECT 動作
# 順序為:建立資料→查詢→刪除
# 1.建立 DSN(Data Source Name)以及 PDO 物件
require "db.inc";
$dsn=RDBMS.":host=".HOST.";dbname=".DBNAME;

try
{
   $conn=new PDO($dsn,USER,PASS);   
#*************************************************
# 2.建立 PDOStatement 以進行 INSERT 動作 
# :name 的好處是 array 裡面不用照順序放值
# (array 為關聯式陣列 Hash)
# ? 的好處是不用寫更多的程式碼,只要照順序放值就好了
# (array 為索引式陣列)
# 第一種使用 :name 方式的 placeholder,並使用 bindParam 設定值
# 建立 users Table 資料
# INSERT 的 VALUES 順序要正確,否則會出現怪異現象 ...

   $stat=$conn->prepare("INSERT INTO users VALUES(:room,:user_id)");
   $ins_users=array(array("user_id"=>"Terry","room"=>"RBS"),
                    array("room"=>"RBS","user_id"=>"Andy"),
                    array("user_id"=>"Joe","room"=>"RBS"),
                    array("room"=>"RBS","user_id"=>"Mai"),
                    array("user_id"=>"Mary","room"=>"RBS"));
   foreach($ins_users as $rec)
   {
     $stat->bindParam(":user_id",$rec["user_id"]);
     $stat->bindParam(":room",$rec["room"]);
     $stat->execute();
   }

#*************************************************
# 第二種使用 :name 方式的 placeholder,但是在 execute 中傳入 array
# 建立 list Table 資料
   $stat=$conn->prepare("INSERT INTO list VALUES(:user_id,:block)");
   $ins_list=array(array("user_id"=>"Mai","block"=>"Terry"),
                   array("block"=>"Joe","user_id"=>"Mai"));
   foreach($ins_list as $rec)
   { $stat->execute($rec); }

#*************************************************
# 第三種使用 ? 方式的 placeholder,並使用 bindParam 設定值
# 建立 users Table 資料
# bindParam 時可以用索引指定值,第一個 ? 為 1,第二個 ? 為 2,如此類推。
   $stat=$conn->prepare("INSERT INTO users VALUES(?,?)");
   $ins_users=array(array("RB2","XiangFei"),
                    array("RB2","Rick"),
                    array("RB2","Alex"));
   foreach($ins_users as $rec)
   {
     $stat->bindParam(1,$rec[0]);
     $stat->bindParam(2,$rec[1]);
     $stat->execute();
   }

#*************************************************
# 第四種使用 ? 方式的 placeholder,但是在 execute 中傳入 array
# 建立 list Table 資料
   $stat=$conn->prepare("INSERT INTO list VALUES(?,?)");
   $ins_list=array(array("Mary","Andy"),
                   array("Mary","Joe"));
   foreach($ins_list as $rec)
   { $stat->execute($rec); }

#*************************************************
# 3.查詢列出房間內的所有人,但加自己黑名單者名字不會顯示出來。
# 取出資料 (1) 使用 fetch 一筆一筆取出
# fetch 可以移動 cursor,可以設定取出的資料成 obj 或 array
# fetch 的引數 1 →PDO::FETCH_NUM	取出資料成數值索引的 array
#        →PDO::FETCH_ASSOC	取出資料成關聯式陣列
#        →PDO::FETCH_BOTH	取出資料成以上兩項。資料會有兩份
#        →PDO::FETCH_OBJ	取出資料成物件。以 obj->prop 取資料
#        →PDO::FETCH_LAZY	取出資料成 BOTH + OBJ
# PostgreSQL 不能設定 user 為 column name,硬要使用必須加上雙引號 "" 及使用小寫
# 而且 SQL 中的 user 也要用雙引號包起來,非常不妥,還是盡量避免使用 user

   $query="SELECT * FROM users WHERE room = :room AND user_id NOT IN (SELECT user_id FROM list WHERE block = :block)";
   $stat=$conn->prepare($query,array(PDO::ATTR_CURSOR=>PDO::CURSOR_SCROLL));
   $sel=array("room"=>"RBS","block"=>"Joe");
   $stat->bindParam("block",$sel["block"]);
   $stat->bindParam("room",$sel["room"]);
   $stat->execute();

   print "<h3>使用 fetch 取出資料</h3>";
   print "<table border='1'>";
   while($row=$stat->fetch(PDO::FETCH_LAZY,PDO::FETCH_ORI_NEXT))
   {
     print "<tr><td>".$row->room."</td><td>".$row["user_id"]."</td></tr>";
   }
   print "</table>";

#*************************************************
# 取出資料 (2) 使用 fetchAll 一次全部取出
# fetchAll 無法移動 cursor(因為已一次取出),取出的資料一定為 array
# fetchAll 無法指定為 PDO::FETCH_LAZY,但可以指定為 PDO::FETCH_OBJ
# BOTH 為 array 的陣列,OBJ 為物件的陣列
   $query="SELECT * FROM users WHERE room = :room AND user_id NOT IN (SELECT user_id FROM list WHERE block = :block)";
   $stat=$conn->prepare($query,array(PDO::ATTR_CURSOR=>PDO::CURSOR_SCROLL));
   $sel=array("room"=>"RBS","block"=>"Joe");
   $stat->execute($sel);

   $rows=$stat->fetchAll(PDO::FETCH_BOTH);
   //若是第一引數為 PDO::FETCH_COLUMN,第二引數可以指定取出哪一個 column 的資料
   print "<h3>使用 fetchAll 取出資料</h3>";
   print "<table border='1'>";
   foreach($rows as $row)
   {
     print "<tr><td>".$row["room"]."</td><td>".$row[1]."</td></tr>";
   }
   print "</table>";

#*************************************************
# 取出資料 (3) 移動 cursor 依 offset 的值取出
# fetch(PDO::FETCH_BOTH,PDO::FETCH_ORI_ABS,offset) 取出絕對 row 值(absolute)
# pgsql 不支援 SELECT 的 rowCount(),但是 mysql 支援
# PDO mysql 不支援 scrollable cursor,PDO PostgreSQL 支援 scrollable cursor
# 第一筆資料的 index 為 1 而不是 0 (同 bindParam 指定 ? placeholder 的數值)
   $query="SELECT * FROM users WHERE room = ? AND user_id NOT IN (SELECT user_id FROM list WHERE block = ?)";
   $stat=$conn->prepare($query,array(PDO::ATTR_CURSOR=>PDO::CURSOR_SCROLL));
   $sel=array("RBS","Joe");
   $stat->bindParam(2,$sel[1]);
   $stat->bindParam(1,$sel[0]);
   $stat->execute();

   print "<h3>使用 fetch + PDO::FETCH_ORI_ABS 取出資料</h3>";
   print "<table border='1'>";
   $count=$stat->rowCount();	# pgsql不支援SELECT rowCount()
   print "<tr><td colspan='2'>SELECT 共找出 {$count} 筆資料 ...</td></tr>";

   $row=$stat->fetch(PDO::FETCH_LAZY,PDO::FETCH_ORI_ABS,3);
   print "<tr><td>".$row["room"]."</td><td>".$row->user_id."</td></tr>";
   $row=$stat->fetch(PDO::FETCH_LAZY,PDO::FETCH_ORI_ABS,1);
   print "<tr><td>".$row->room."</td><td>".$row[1]."</td></tr>";

   print "</table>";

#*************************************************
# fetch(PDO::FETCH_BOTH,PDO::FETCH_ORI_REL,num) 取出相對於前一次結果的 row(relative)
   $row=$stat->fetch(PDO::FETCH_LAZY,PDO::FETCH_ORI_ABS,1);
   print "第一筆資料為 ".$row["room"]." → ".$row->user_id."<br/>";
   $row=$stat->fetch(PDO::FETCH_LAZY,PDO::FETCH_ORI_REL,2);
   print "往後兩筆的資料為 ".$row["room"]." → ".$row->user_id."<br/>";
   $row=$stat->fetch(PDO::FETCH_LAZY,PDO::FETCH_ORI_REL,-1);
   print "再往前一筆資料為 ".$row["room"]." → ".$row->user_id."<br/>";

#*************************************************
# 4.刪除資料表中的所有資料
# (1)刪除 users Table 使用 prepare 指定 room 為 RBS
# 將變數資料傳給 bind 使用 bindParam,將 literal 資料傳給 bind 用 bindValue
# 若將 literal 傳給 bindParam 會發生錯誤

   $stat=$conn->prepare("DELETE FROM users WHERE room = :room");
   $stat->bindValue(":room","RBS");
   $stat->execute();

   $count=$stat->rowCount();
   print "<h3>共刪除了 {$count} 位房間為 RBS 的使用者</h3>";

   unset($stat);

#*************************************************
# (2)刪除 users Table 使用 prepare 指定 room 為 RB2
   $stat=$conn->prepare("DELETE FROM users WHERE room = ?");
   $stat->execute(array("RB2"));

   $count=$stat->rowCount();
   print "<h3>共刪除了 {$count} 位房間為 RB2 的使用者</h3>";

   unset($stat);

#*************************************************
# (3)刪除 list Table 使用 PDO::exec 直接快速處理
   $count=$conn->exec("DELETE FROM list");
   print "<h3>list 黑名單資料共刪除了 {$count} 筆</h3>";

   unset($stat);

}
catch(PDOException $x)
{
   exit("錯誤代碼:".$x->getCode()."<br />".
        "錯誤訊息:".$x->getMessage()."<br />".
        "錯誤行數:".$x->getLine()."<br />".
        "錯誤檔案:".$x->getFile()."<br />");
}