SQL Server 2005 Driver for PHP 提供了24 个可以用在PHP 程式的函式,这些函式名称都是sqlsrv_ 开头。而本文即将说明这些函式如何协助PHP 程式存取Microsoft SQL Server 资料库。此外,本文附上一段5分钟的影片,说明以SQL Server 2005 Driver for PHP 的函式所编写的AdventureWorks Product Reviews 应用范例。
- 资料库存取的基本步骤
- 连接资料库
- 检视用户端资讯
- 取得资料
- 更新资料
- 转换资料型别
- 错误处理
- 结语
本文无法尽数说明所有24 个函式,而是依照资料库存取的动作类型-- 包括连接资料库、取得资料、更新资料、转换资料型别、错误处理等,分别说明对应到的SQL Server 2005 Driver for PHP 函式。
资料库存取的基本步骤
连接资料库是存取资料库优先且必要的动作;要取得资料库里的资料、或更新资料库,都必须先成功连上资料库。 SQL Server 2005 Driver for PHP 提供了sqlsrv_connect 函式让我们连接SQL Server。
连接资料库之后,接着要以T-SQL 陈述式来「查询」已连上的资料库,查询的内容包罗万象,甚至有可能超出单纯的查询。因此这个部分必须根据目的、需求,编写出适当的T-SQL 陈述式,并加以执行。我们可以利用SQL Server 2005 Driver for PHP 的sqlsrv_query 函式进行一次查询,而sqlsrv_prepare / sqlsrv_execute 函式能处理多次查询。
然后我们要取得查询结果;查询结果能以阵列、物件、资料流传回程式,或者也能直接撷取单一栏位。取得查询结果大都还需要一一「挖出」阵列或物件里的资料,并呈现在网页。 SQL Server 2005 Driver for PHP 提供的查询结果取得函式包括sqlsrv_fetch_array、sqlsrv_fetch_object、sqlsrv_fetch、sqlsrv_get_field、sqlsrv_next_result 等。
此外也可能要将资料回存资料库-- 不论是以新资料更新旧资料,或是新增资料列。我们可以利用查询函式(sqlsrv_prepare / sqlsrv_execute)来更新资料。
连接及关闭资料库
SQL Server 2005 Driver for PHP 提供了sqlsrv_connect 函式来连接资料库。但为了安全,连接资料库应该有所验证。因此SQL Server 2005 Driver for PHP 也提供了两种资料库连接的验证方式:Windows 验证及SQL Server 验证;预设是采用Windows 验证。
不论是Windows 验证或SQL Server 验证,都必须有权限适当的Windows 帐号或SQL Server 帐号;帐号的权限必须足以进行PHP 程式后续的动作,但为了安全考量,也不应该使用权限太大的帐号。
sqlsrv_connect 函式的使用看似简单,只需两个参数(第1 个是连接的伺服器名称$serverName 字串,第2 个是连接资讯$connectionInfo 阵列);但事实上,第2 个可选择的连接资讯阵列可包含了诸多连接相关参数。
sqlsrv_connect( string $serverName [, array $connectionInfo])
$serverName 字串指定了欲连接的伺服器名称、IP 位址(需确定SQL Server 组态管理员已启用TCP/IP 或具名管道通讯协定),这个字串也可以包含SQL Server 执行个体名称(例如myServer\instanceName)或TCP 通讯埠编号(例如myServer,1943,但这必须先将SQL Server 设定成能接听特定的通讯埠)。
可选择的$connectionInfo 是关联阵列,内含诸多连接相关参数,通常我们会利用这个阵列来指定连接SQL Server 之后欲使用的资料库。如果使用SQL Server 验证,也要以此阵列指定SQL Server 帐号的使用者ID(UID)和通行码(password,PWD)。
sqlsrv_connect 函式若执行成功,会传回PHP 连线资源,后续的存取动作都会用到。但若执行失败,会传回 false。
Windows 验证
如果没有以$connectionInfo 阵列指定SQL Server 帐号的UID 和PWD,sqlsrv_connect 函式就会以Windows 验证登入SQL Server;Windows 验证是SQL Server 2005 Driver for PHP 预设的验证方式。如果Web 伺服器使用模拟(impersonation),这种验证则是以Web 伺服器的行程或绪程作为连接SQL Server 的身份。因此执行中的Web 伺服器行程或绪程的凭证,必须能登入SQL Server。此外,如果SQL Server 和Web 伺服器分属不同电脑,SQL Server 必须设定成能够远端连接。
以下是Windows 验证的例子:
// 指定伺服器名称(本机电脑)$serverName = "(local)";// 指定连接字串的资料库名称$connectionInfo = array( " ;Database"=>"AdventureWorks");/* sqlsrv_connect 函式连接失败会传回false, 成功会传回PHP连线资源, 因此PHP连线资源会被指定到变数$conn */$conn = sqlsrv_connect( $serverName, $connectionInfo);
SQL Server 验证
sqlsrv_connect 函式也能以SQL Server 验证的方式登入SQL Server,但首先SQL Server 必须设定成SQL Server 混合模式验证,然后只要以$connectionInfo 阵列指定能够登入SQL Server 的UID 和PWD 即可。但为了安全起见,不应该直接将UID 和PWD 写在PHP 程式里,可以改成将UID 和PWD 存放在特定且具备适当存取权限的档案,然后再以PHP 程式读取UID 和PWD,是较为安全的作法,例如:
// 指定伺服器名称(本机电脑)$serverName = "(local)";// 将UID 和PWD 分别存在C 碟里的档案,再以file_get_contents 函式读取$uid = file_get_contents("C:\AppData\uid.txt");$pwd = file_get_contents("C:\AppData\pwd.txt");$connectionInfo = array( "UID" =>$uid, "PWD"=>$pwd, "Database"=>"AdventureWorks");/* sqlsrv_connect 函式连接失败会传回false, 成功会传回PHP连线资源,因此PHP连线资源会被指定到变数$conn */$conn = sqlsrv_connect( $serverName, $connectionInfo);
连接共用
连接共用(connection pooling)是指成功连线之后,将连线集中置于集区,让程式能重复使用集区里的连线,而不需重新连接。 SQL Server 2005 Driver for PHP 利用ODBC 的连接共用来提供这项功能,而且预设便启用连接共用。当PHP 程式欲连接SQL Server 时,SQL Server 2005 Driver for PHP 在建立新的连线之前,会检查集区里是不是已经有属性相同的连线:如果有,就会使用集区里现有的连线(并且会重置其连接状态);如果没有,SQL Server 2005 Driver for PHP 才会建立新的连线,并将成功建立的连线加入集区。
但无论如何,我们亦可将连接资讯的ConnectionPooling 属性设为false(0),以此强制SQL Server 2005 Driver for PHP 建立新的SQL Server 连线(ConnectionPooling属性的预设值为True):
// 指定伺服器名称(本机电脑)$serverName = "(local)";$connectionInfo = array("Database"=>"AdventureWorks" , "ConnectionPooling"=>false);$conn = sqlsrv_connect($serverName, $connectionInfo);
结束连接
若欲结束已建立的连接,要使用sqlsrv_close 函式,并以欲结束的PHP 连线资源作为参数;如果是强制建立的SQL Server 连线,用完之后务必要以sqlsrv_close 函式来结束连线。
// 以sqlsrv_close 函式并搭配$conn 变数里的PHP 连线资源来结束连线sqlsrv_close( $conn);
检视用户端及伺服端资讯
为了便于检视用户端及伺服端资讯,SQL Server 2005 Driver for PHP 分别提供了sqlsrv_client_info 及sqlsrv_server_info 函式。这两个函式在使用之前,都必须先建立SQL Server 连线,并以PHP 连线资源作为执行函式的参数。而且这两个函式都是将取得的相关资讯存放在关联阵列。
sqlsrv_client_info:检视用户端资讯
如果执行失败,sqlsrv_client_info 函式会传回false,若执行成功,则会将以下资讯放置在关联阵列。
|
索引键
|
说明
|
|
DriverDllName
|
SQL Server Native Client DLL档名(SQLNCLI.DLL)
|
|
DriverODBCVer
|
ODBC 版本序号
|
|
DriverVer
|
SQL Server Native Client DLL 版本序号
|
|
ExtensionVer
|
php_sqlsrv.dll(SQL Server 2005 Driver for PHP)版本序号
|
以下是sqlsrv_client_info 函式的使用范例及执行结果。
// $conn 内容是PHP 连接资源// 叫用sqlsrv_client_info 函式取得用户端资讯if( $client_info = sqlsrv_client_info( $conn)){ // 以foreach 回圈显示$client_info 阵列内容foreach( $client_info as $key => $value) { echo $key.":".$value."</br>"; }}// 若叫用结果为false,表示叫用失败else{ echo "sqlsrv_client_info函式执行有误</br>";}
图1 sqlsrv_client_info 函式的执行结果
sqlsrv_server_info:检视用户端及伺服端资讯
与sqlsrv_client_info 函式相当类似的是,sqlsrv_server_info 函式如果执行失败也会传回false,若执行成功,则会将以下资讯放置在关联阵列。
|
索引键
|
说明
|
|
CurrentDatabase
|
目前列为目标的资料库
|
|
SQLServerVersion
|
SQL Server 版本序号
|
|
SQLServerName
|
伺服器名称
|
以下是sqlsrv_server_info 函式的使用范例及执行结果。
// $conn 内容是PHP 连接资源// 叫用sqlsrv_server_info 函式取得用户端资讯if( $server_info = sqlsrv_server_info( $conn)){ // 以foreach 回圈显示$server_info 阵列内容foreach( $server_info as $key => $value) { echo $key.":".$value."</br>"; }}// 若叫用结果为false,表示叫用失败else{ echo "sqlsrv_server_info 函式执行有误</br>";}
图2 sqlsrv_server_info 函式的执行结果
取得资料
资料库查询结果会传回资料列,只要利用不同的sqlsrv_fetch_array、sqlsrv_fetch_object、sqlsrv_fetch 等函式,我们即可以阵列、物件、资料流等不同形式来取得资料,而且也能取得单一栏位或多笔结果的资料。
这些函式是以查询的执行结果作为参数,而且若执行成功,会传回所取得的资料,但若执行失败,会传回false;如果执行成功但未能取得资料,则会传回null(可以利用PHP 函式is_null 来检查)。
sqlsrv_fetch_array:以阵列传回下一笔资料
若执行成功,sqlsrv_fetch_array 函式会传回存放资料的阵列,而且可以是数值索引的阵列,也可以是关联阵列。使用sqlsrv_fetch_array 函式时,必须将查询的结果指定为第1 个参数,第2 个参数可选择,用来指定传回数值索引阵列或关联阵列(预设两者皆传回)。
sqlsrv_fetch_array( resource $stmt[ , int $fetchType])
第2 个参数能以如下的常数指定:SQLSRV_FETCH_NUMERIC(数值索引阵列)、SQLSRV_FETCH_ASSOC(关联阵列)、SQLSRV_FETCH_BOTH(两者,此为预设值)。
以下是sqlsrv_fetch_array 函式的使用范例及执行结果,其中是使用数值索引阵列。
// 定义查询$tsql = "SELECT ProductID, UnitPrice, StockedQty FROM Purchasing.PurchaseOrderDetail WHERE StockedQty < 3 AND DueDate='2002-01-29'";/ / 执行查询// $conn 内容是PHP 连接资源$stmt = sqlsrv_query( $conn, $tsql);if ( $stmt ){ echo "已执行SQL 查询";} else { echo "查询失败,错误讯息如下:"; die( print_r( sqlsrv_errors(), true));}// 以回圈显示阵列里的查询结果while( $row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_NUMERIC)){ echo "ProdID: " ;.$row[0]."</br>"; echo "UnitPrice: ".$row[1]."</br>"; echo "StockedQty: ". $row[2]."</br>"; echo "-----------------</br>";}// 释放查询及连接资源sqlsrv_free_stmt( $stmt);sqlsrv_close( $conn);?>
图3 sqlsrv_fetch_array 函式的执行结果
上述范例是使用数值索引阵列,如果要使用关联阵列,需调整如下:
while( $row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_ASSOC)){ echo "ProdID: ".$row['ProductID']."<\br>" ;; echo "UnitPrice: ".$row['UnitPrice']."<\br>"; echo "StockedQty: ".$row['StockedQty']."<\br> ;"; echo "-----------------<\br>";}
sqlsrv_fetch_object:以物件传回下一笔资料
sqlsrv_fetch_object 函式能以物件的形式取得资料列,而且我们可以自订类别来存放所取得的物件,也能使用预设的stdClass 类别。以下的例子是使用预设的类别。
// 已建立PHP 连接资源,也已执行查询($stmt)// $obj 的内容是PHP物件形式的资料列while( $obj = sqlsrv_fetch_object( $stmt )){ // 一一显示物件里的LastName 及FirstName echo $obj->LastName.", ".$obj->FirstName."</br>";}
以下是自订类别的例子,与前者最大差别,是这种作法要先定义类别、建构函式,然后在执行sqlsrv_fetch_object 函式时,必须以第2个参数指定自订的类别。
// 定义Product 类别class Product{ // 建构函式public function Product($ID) { $this->objID = $ID; } public $objID; public $ name; public $StockedQty; public $SafetyStockLevel; private $UnitPrice; // method function getPrice() { return $this->UnitPrice; }}// 省略连接SQL Server、执行查询等程式码$i=0;/ / 以下叫用sqlsrv_fetch_object,是以第2 个参数指定自订的类别while( $product = sqlsrv_fetch_object( $stmt, "Product", array($i))){ echo "Object ID: ".$ product->objID."</br>"; echo "Product Name: ".$product->Name."</br>"; echo "Stocked Qty: " .$product->StockedQty."</br>"; echo "Safety Stock Level: ".$product->SafetyStockLevel."</br>"; echo "Product Color : ".$product->Color."</br>"; echo "Unit Price: ".$product->getPrice()."</br>"; echo "-----------------</br>"; $i++;}
sqlsrv_fetch / sqlsrv_get_field:取得单一栏位资料
如果只是想取得某一笔资料列的单一栏位,可以搭配使用sqlsrv_fetch和sqlsrv_get_field,前者能让下一笔资料列变得可读,后者则可读取目前资料列的特定栏位(如果必须指定回传资料的PHP 资料型别,或必须以资料流的方式取得资料,就应该使用sqlsrv_get_field)。
sqlsrv_fetch( resource $stmt)
sqlsrv_get_field( resource $stmt, int $fieldIndex [, int $getAsType])
以下的范例会先取得Purchasing 资料表里特定日期和特定库存量的产品资讯,再利用sqlsrv_fetch 函式重复检视查询结果的每一笔资料列,然后再以sqlsrv_get_field 函式取得特定栏位的资料。
// 定义查询$tsql = "SELECT ProductID, UnitPrice, StockedQty FROM Purchasing.PurchaseOrderDetail WHERE StockedQty < 3 AND DueDate='2002-01-29'";/ / 省略连接SQL Server、执行查询等程式码,$stmt 是查询结果// 以while 回圈重复检视查询结果的每一笔资料列while( sqlsrv_fetch( $stmt)){ // 以sqlsrv_get_field 函式取得特定栏位的资料echo "ProdID: ".sqlsrv_get_field($stmt, 0)."<\br>"; echo "UnitPrice: ".sqlsrv_get_field($stmt, 1)." <\ br>"; echo "StockedQty: ".sqlsrv_get_field($stmt, 2)." <\br>"; echo "---------------- -<\br>";}
sqlsrv_next_result:处理多笔资料
SQL Server 2005 Driver for PHP 的sqlsrv_next_result 函式能让PHP 程式继续处理下一笔结果。
sqlsrv_next_result( resource $stmt )
以下的例子会执行批次查询来取得特定产品编号的评论资讯、插入产品评论,然后再次取得特定产品编号的评论资讯,因此就能看到新加入的产品评论。这个例子是以sqlsrv_next_result 函式将批次查询的结果移往下一笔。
// 省略连接SQL Server的程式码// 定义批次查询$tsql = "--Query 1 SELECT ProductID, ReviewerName, Rating FROM Production.ProductReview WHERE ProductID=? ;// 省略连接SQL Server 的程式码// 定义批次查询--Query 2 INSERT INTO Production.ProductReview (ProductID, ReviewerName, ReviewDate, EmailAddress, Rating) VALUES (?, ?, ?, ?, ?); - -Query 3 SELECT ProductID, ReviewerName, Rating FROM Production.ProductReview WHERE ProductID=?;";// 指定参数值并执行查询$params = array(798, 798, 'CustomerName', '2008-4-15', 'test@customer.com', 3, 798 );$stmt = sqlsrv_query($conn, $tsql, $params);// 取得并显示第1 次查询的结果echo "查询1 的结果:<\ br>";while( $row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_NUMERIC )){ print_r($row);}// 以sqlsrv_next_result 函式将批次查询的结果移往下一笔sqlsrv_next_result($stmt);//取得并显示第2 次查询的结果echo "查询2 的结果:<\br>";echo "资料已更新:".sqlsrv_rows_affected($stmt)."</br>" ;// 以sqlsrv_next_result 函式将批次查询的结果移往下一笔sqlsrv_next_result($stmt);// 取得并显示第3 次查询的结果echo "查询3 的结果:<\br>"; while( $row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_NUMERIC )){ print_r($row);}
更新资料
SQL Server 2005 Driver for PHP 除了能以参数化查询的方式更新资料,也能以资料流的方式传送资料(包括送进及送出资料库),并且支援资料交易。以下将说明两种参数化查询的用法。
参数化查询
SQL Server 2005 Driver for PHP 提供了sqlsrv_query 或sqlsrv_prepare / sqlsrv_execute 函式用作查询的执行。 sqlsrv_query 可以用作一次查询,而且适用多数情形。 sqlsrv_prepare / sqlsrv_execute 是一对互相搭配的函式,将陈述式分成「准备」和「执行」两部份,适用于多次查询。
以下是以更新Production 资料表某些产品编号的数量为例,说明如何以sqlsrv_query 函式来执行参数化查询。
// 定义查询字串,这里要更新Production 资料表的某些产品编号的数量(数量亦未知)$tsql1 = "UPDATE Production SET Quantity = ? WHERE ProductID = ?";// 初始或更新对应到T-SQL 查询替代符号的PHP 变数// 本例欲将编号为709 的产品数量更新成10$qty = 10;$productId = 709;// 以sqlsrv_query 函式来执行查询$stmt1 = sqlsrv_query( $conn, $tsql1, array($qty, $productId));
sqlsrv_prepare / sqlsrv_execute 函式
以下的例子示范了sqlsrv_prepare / sqlsrv_execute 函式的用法,此例会将数笔订单插入Sales资料表。其中在叫用sqlsrv_prepare 函式时,会将$params 阵列系结到陈述式($stmt),而每次插入新订单之前,会以对应到订单细节的新值来更新$params 阵列。执行后面的查询时,则会用到新的参数值。
// 定义查询字串$tsql = "INSERT INTO Sales (SalesOrderID, OrderQty, ProductID, SpecialOfferID, UnitPrice) VALUES (?, ?, ?, ?, ?)" ;;/* 初始或更新对应到T-SQL 查询替代符号的PHP变数以下的每个子阵列会是查询的参数阵列每个子阵列里的顺序是SalesOrderID、OrderQty、ProductID、SpecialOfferID、UnitPrice */$parameters = array( array(43659, 8, 711, 1, 20.19), array(43660, 6, 762, 1, 419.46), array(43661, 4, 741, 1, 818.70));// 初始参数值$ orderId = 0;$qty = 0;$prodId = 0;$specialOfferId = 0;$price = 0.0;// 准备$stmt = sqlsrv_prepare( $conn, $tsql, array( $orderId, $qty, $prodId, $ specialOfferId, $price));// sqlsrv_ prepare 函式执行失败会传回falseif( $stmt === false ){ // 向使用者显示执行失败的讯息,下一行程式可显示系统的错误讯息die( print_r( sqlsrv_errors(), true));}// 执行$parameters 里每组参数的陈述式foreach( $parameters as $params){ list($orderId, $qty, $prodId, $specialOfferId, $price) = $params; if( sqlsrv_execute($stmt) === false ) { // 向使用者显示执行失败的讯息,下一行程式可显示系统的错误讯息die( print_r( sqlsrv_errors(), true)); } else { // 显示新的资料列,以确认资料列已成功插入echo "资料列已改变: ".sqlsrv_rows_affected( $stmt )."<\br>"; }}// 释放查询的PHP 资源sqlsrv_free_stmt( $stmt);
转换资料型别
PHP 程式从资料库取出资料、或将资料送回资料库时,可能需要转换资料型别,这涉及PHP 本身的资料型别和SQL Server 资料型别的差异,而转换分成两种:一是系统以预设资料型别自动转换,另一种是我们以指定的型别自行转换。
资料送回资料库的型别转换
当资料送回资料库时,如果我们没有自行转换,SQL Server 2005 Driver for PHP 会根据下表SQL Server 预设的资料型别,将PHP 的资料型别转换成SQL Server 资料型别。
|
PHP 资料类别
|
SQL Server 预设的资料型别
|
|
NULL
|
varchar(1)
|
|
Boolean
|
bit
|
|
Integer
|
int
|
|
Float
|
float(24)
|
|
String (长度小于8000 位元组)
|
varchar(<字串长度>)
|
|
String (长度超过8000 位元组)
|
varchar(max)
|
|
Resource
|
Not supported.
|
|
Stream (非二进位编码)
|
varchar(max)
|
|
Stream (二进位编码)
|
varbinary
|
|
Integer
|
int
|
|
Array
|
不支援
|
|
Object
|
不支援
|
|
DateTime
|
datetime
|
从资料库取出资料的型别转换
反之,从资料库取出资料时,如果我们没有自行转换资料型别,SQL Server 2005 Driver for PHP 会根据下表,将SQL Server 资料型别转换成PHP资料型别。
|
SQL Server 资料型别
|
预设的PHP 资料型别
|
预设的编码方式
|
|
bigint
|
String
|
8位元字元¹
|
|
binary
|
Stream²
|
Binary³
|
|
bit
|
Integer
|
8位元字元¹
|
|
char
|
String
|
8位元字元¹
|
|
datetime
|
Datetime
|
不适用
|
|
decimal
|
String
|
8位元字元¹
|
|
float
|
Float
|
8位元字元¹
|
|
image4
|
Stream²
|
Binary³
|
|
int
|
Integer
|
8位元字元¹
|
|
money
|
String
|
8位元字元¹
|
|
nchar
|
String
|
8位元字元¹
|
|
numeric
|
String
|
8位元字元¹
|
|
nvarchar
|
String
|
8位元字元¹
|
|
nvarchar(MAX)
|
Stream²
|
8位元字元¹
|
|
ntext5
|
Stream²
|
8位元字元¹
|
|
real
|
Float
|
8位元字元¹
|
|
smalldatetime
|
Datetime
|
8位元字元¹
|
|
smallint
|
Integer
|
8位元字元¹
|
|
smallmoney
|
String
|
8位元字元¹
|
|
sql_variant
|
String
|
8位元字元¹
|
|
text6
|
Stream²
|
8位元字元¹
|
|
timestamp
|
Stream²
|
8位元字元¹
|
|
tinyint
|
Integer
|
8位元字元¹
|
|
UDT
|
Stream²
|
Binary³
|
|
uniqueidentifier
|
String7
|
8位元字元¹
|
|
varbinary
|
Stream²
|
Binary³
|
|
varbinary(MAX)
|
Stream²
|
Binary³
|
|
varchar
|
String
|
8位元字元¹
|
|
varchar(MAX)
|
Stream²
|
8位元字元¹
|
|
variant
|
不支援
|
不支援
|
|
xml
|
Stream²
|
8位元字元¹
|
以下请注意:
- 资料会根据Windows 系统地区设定的字码页将资料转换成8 位元字元,无法对映到此字码页的字元,会被换成单位元组的问号(?)。
- 若以sqlsrv_fetch_array 或sqlsrv_fetch_object 取得PHP Stream 型别资料,资料会以字串回传(但编码与Stream 相同)。例如,若以sqlsrv_fetcharray 取回SQL Server 二进位型别,回传的预设型别会是二进位字串。
- 回传时,将资料视为来自伺服端且未经编码或转换的未处理的byte stream。
- 这是对映到varbinary(max) 型别的传统型别。
- 这是对映到nvarchar(max) 型别的传统型别。
- 这是对映到varchar(max) 型别的传统型别。
- UNIQUEIDENTIFIER 是由以下规则运算式所代表的GUID:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-f] {4}-[0-9a-fA-f]{4}-[0-9a-fA-F]{12}。
转换成SQL Server 型别
如果要指定存回SQL Server 资料库的资料型别,可以在准备或执行插入或更新资料等查询时,利用$params 阵列。以下将资料存回资料库的例子,将$changeDate、$rate、$payFrequency 的型别分别指定成SQLSRV_SQLTYPE_DATETIME、SQLSRV_SQLTYPE_MONEY、SQLSRV_SQLTYPE_TINYINT,没有指定型别的$employeeId,则回使用预设值。
// 定义查询$tsql1 = "INSERT INTO HumanResources.EmployeePayHistory (EmployeeID, RateChangeDate, Rate, PayFrequency) VALUES (?, ?, ?, ?)";//建构参数阵列$employeeId = 5;$changeDate = "2005-06-07";$rate = 30;$payFrequency = 2;$params1 = array( // EmployeeID 没有指定SQL Server 资料型别,会使用预设型别array($employeeId, null), // datetime:SQLSRV_SQLTYPE_DATETIME array($changeDate, null, null, SQLSRV_SQLTYPE_DATETIME), // money:SQLSRV_SQLTYPE_MONEY array($rate, null, null, SQLSRV_SQLTYPE_MONEY), // tinyint:SQLSRV_SQLTYPE_TINYINT array ($payFrequency, null, null, SQLSRV_SQLTYPE_TINYINT));// 执行插入查询$stmt1 = sqlsrv_query($conn, $tsql1, $params1);
转换成PHP 资料型别
与前述相反的是,从SQL Server 资料库取出的资料,可能需要自行指定成PHP 的资料型别。此时我们可以使用sqlsrv_get_field 函式,并且将欲指定的PHP 资料型别当成函式的第3 个参数。当然,欲使用sqlsrv_get_field 函式,必须先搭配使用sqlsrv_fetch 函式。
// 定义T-SQL 查询$tsql = "SELECT ReviewerName, ReviewDate, Rating, Comments FROM Production.ProductReview WHERE ProductID = ? ORDER BY ReviewDate DESC";// 设定参数值$productID = 709;$params = array( $productID);// 执行查询$stmt = sqlsrv_query($conn, $tsql, $params);// 取回并显示资料;取回资料的同时,亦指定成PHP 资料型别while ( sqlsrv_fetch( $stmt)){ // 不指定;使用预设型别echo "Name: ".sqlsrv_get_field( $stmt, 0 )."</br>"; // 指定成8 位元字元编码的字串echo "Date: ".sqlsrv_get_field( $stmt, 1, SQLSRV_PHPTYPE_STRING( SQLSRV_ENC_CHAR))."</br>"; // 不指定;使用预设型别echo "Rating: ".sqlsrv_get_field( $stmt, 2 )."</br>"; echo "Comments: "; // 指定成8 位元字元编码的资料流$comments = sqlsrv_get_field( $stmt, 3, SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_CHAR)); fpassthru( $comments); echo "</br>"; }
将UTF-8 编码资料存回伺服器
首先,资料库目的资料行的型别必须是nchar 或nvarchar。然后要以PHP 的iconv 函式将资料转成UTF-16LE 编码,例如$data 是UTF-8 编码的变数,以下将可转换成UTF-16LE:
$data = iconv("utf-8", "utf-16le", $data) ;接着要在参数阵列将PHP 型别指定成SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY):$params = array( array($data, null, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY)));
从资料库取得资料后转成UTF-8 编码
首先,资料库里的资料是以UTF-16LE 编码。接着要以sqlsrv_get_field 函式取得资料,然后将PHP 资料型别指定成SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY):
$data = sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY));接着利用PHP 的iconv 函式将取得的资料转换成UTF -8(原本是UTF-16LE 编码):$data = iconv("utf-16le", "utf-8", $data);
错误处理
SQL Server 2005 Driver for PHP 预设会将警告视为错误,因此如果叫用SQL Server 2005 Driver for PHP 所属的函式产生了警告或错误,都会传回false。但我们也可以利用sqlsrv_configure 函式来关闭这项功能。例如PHP 程式若包含以下程式,就会让SQL Server 2005 Driver for PHP 所属的函式产生错误时才会传回false(也就是若产生警告,并不会传回false):
sqlsrv_configure("WarningsReturnAsErrors", 0);
如果将上述程式里的0 改成1,就会恢复成预设状态。不论改成0 或1,只会影响执行的PHP 程式。
此外,利用php.ini 的[sqlsrv] 也可以设定上述的WarningsReturnAsErrors(这会影响整个PHP 执行环境):
sqlsrv.WarningsReturnAsErrors = 0
取得细节
SQL Server 2005 Driver for PHP 提供的sqlsrv_errors 函式,能传回最近一次SQL Server 2005 Driver for PHP 运作的错误或警告详细资讯:
sqlsrv_errors( [int $errorsAndOrWarnings] )
其中的可选择参数$errorsAndOrWarnings,是用来指定sqlsrv_errors 传回何种类型的错误,包括:
- SQLSRV_ERR_ALL:传回错误和警告,此为预设值。
- SQLSRV_ERR_ERRORS:传回错误。
- SQLSRV_ERR_WARNINGS:传回警告。
sqlsrv_errors 函式会传回阵列或null 型别的资料,null 表示没有最近没有产生错误或警告,而阵列则包含了3 组键/值(以下键值亦可是0、1、2 等数值):
- SQLSTATE:
- 若是源自ODBC 驱动程式的错误,SQLSTATE 是由ODBC 传回,细节可参考ODBS 错误码。
- 若是源自SQL Server 2005 Driver for PHP 的错误,SQLSTATE 为IMSSP。
- 若是源自SQL Server 2005 Driver for PHP 的警告,SQLSTATE 为01SSP。
- code:
- 若是源自SQL Server 的错误,是SQL Server 本生的错误码。
- 若是源自ODBC 驱动程式的错误,错误码会由ODBC 传回。
- 若是源自SQL Server 2005 Driver for PHP 的错误,错误码会由SQL Server 2005 Driver for PHP 传回。
- message:描述错误的讯息。
显示错误或警告讯息
我们可以用print_r 来显示整个物件:
print_r(sqlsrv_errors());
也可以一一显示物件的内容:
$errors = sqlsrv_errors();foreach( $errors as $error){ echo "SQLSTATE: ".$error[ 'SQLSTATE']."</br> ;"; echo "code: ".$error[ 'code']."</br>"; echo "message : ".$error[ 'message']."</br>";}不过,处理之前,最好先检查是不是真的有错误或警告:if( ($errors = sqlsrv_errors() ) != null){ // 有错误或警告}
结语
SQL Server 2005 Driver for PHP 提高了PHP 程式存取Microsoft SQL Server 的透通性,尤其是较新的SQL Server 2005、或最新的SQL Server 2008。目前的SQL Server 2005 Driver for PHP 1.0 提供了24 个函式,供作PHP 程式存取SQL Server 之用。虽然本文花了相当的篇幅及例子在说明这些函式,但依然无法尽数说完所有的函式;就算已说明的函式,也无法完全解说所有细节。
因此完整的说明,需请您参阅微软提供的文件(SQL Server Driver for PHP Documentation);除了可于微软网站查阅,下载回来的SQL Server 2005 Driver for PHP档案(SQLServerDriverForPHP.EXE),也包含内容相同的线上说明档(SQLServerDriverForPHP_1.0.8204.chm)。