2024 年 PHP Conference Japan

sqlsrv_query

(沒有版本資訊,可能只存在於 Git 中)

sqlsrv_query準備並執行查詢

說明

sqlsrv_query(
    資源 $conn,
    字串 $sql,
    陣列 $params = ?,
    陣列 $options = ?
): 混合

準備並執行查詢。

參數

conn

sqlsrv_connect() 傳回的連線資源。

sql

定義要準備和執行的查詢的字串。

params

執行參數化查詢時指定參數資訊的陣列。陣列元素可以是以下任何一種

  • 字面值
  • PHP 變數
  • 具有以下結構的陣列:array($value [, $direction [, $phpType [, $sqlType]]])
下表描述了上述陣列結構中的元素

陣列結構
元素 說明
$value 字面值、PHP 變數或 PHP 參考變數。
$direction (選用) 下列其中一個 SQLSRV 常數,用於指示參數方向:SQLSRV_PARAM_IN、SQLSRV_PARAM_OUT、SQLSRV_PARAM_INOUT。預設值為 SQLSRV_PARAM_IN。
$phpType (選用) 指定傳回值的 PHP 資料類型的 SQLSRV_PHPTYPE_* 常數。
$sqlType (選用) 指定輸入值的 SQL Server 資料類型的 SQLSRV_SQLTYPE_* 常數。
選項

指定查詢屬性選項的陣列。下表描述了支援的鍵值

查詢選項
鍵值 說明
QueryTimeout 正整數值。 以秒為單位設定查詢逾時。預設情況下,驅動程式將無限期地等待結果。
SendStreamParamsAtExec truefalse(預設值為 true 設定驅動程式,以在執行時傳送所有串流資料 (true),或以區塊方式傳送串流資料 (false)。預設值為 true。如需詳細資訊,請參閱 sqlsrv_send_stream_data()
Scrollable SQLSRV_CURSOR_FORWARD、SQLSRV_CURSOR_STATIC、SQLSRV_CURSOR_DYNAMIC 或 SQLSRV_CURSOR_KEYSET 請參閱 Microsoft SQLSRV 文件中的 » 指定游標類型和選擇列

傳回值

成功時傳回陳述式資源,發生錯誤時傳回 false

範例

範例 #1 sqlsrv_query() 範例

<?php
$serverName
= "serverName\sqlexpress";
$connectionInfo = array( "Database"=>"dbName", "UID"=>"username", "PWD"=>"password" );
$conn = sqlsrv_connect( $serverName, $connectionInfo);
if(
$conn === false ) {
die(
print_r( sqlsrv_errors(), true));
}

$sql = "INSERT INTO Table_1 (id, data) VALUES (?, ?)";
$params = array(1, "some data");

$stmt = sqlsrv_query( $conn, $sql, $params);
if(
$stmt === false ) {
die(
print_r( sqlsrv_errors(), true));
}
?>

備註

對於只計劃執行一次的語句,請使用 sqlsrv_query()。如果您打算使用不同的參數值重新執行語句,請使用 sqlsrv_prepare()sqlsrv_execute() 的組合。

另請參閱

新增備註

使用者貢獻的備註 6 則備註

bill_spam0001 at yahoo dot com
11 年前
如果您在嘗試執行查詢時收到錯誤,且 sqlsrv_errors(SQLSRV_ERR_ERRORS) 的輸出如下:

SQLSTATE: IMSSP
代碼:-14
訊息:傳遞給 sqlsrv_query 的參數無效。

您未能將有效的參數傳遞給 sqlsrv_query 本身,這可能是三個參數之一
連線:SQL Server 連線的有效控制代碼
查詢:包含查詢的有效字串,其中包含參數的佔位符:「(?)」
參數:一個包含查詢參數值的陣列。(選用,但數量必須與查詢中的佔位符號數量相符。)

我找不到關於這個錯誤的任何資訊,結果發現是缺少連線參數。在我的例子中,我發現我在程式碼中輸入了「$connn」而不是「$conn」。
if ($stmt=sqlsrv_query($conn, $sql, $params)) { ...

雖然這看起來像是一個完全「新手」才會犯的錯誤,但事實上關於這個 SQL Server 錯誤訊息本身的資訊非常少。因此,SQLSTATE「IMSSP」,程式碼「-14」的簡單含義是您沒有提供有效的連線物件給您的 sqlsrv_query 函式。

這個訊息可能看起來令人困惑,尤其是當一個頁面上有多個 sqlsrv_query 出現時,而且您可能在關閉連線後又新增了一個新的 sqlsrv_query。

由於我浪費了大量的時間追蹤正常的管道,我想在這裡參考這個錯誤會提供一些幫助。我一直糾結於「參數」,並認為這是一個錯誤的參數物件,而忽略了將未定義的連線物件傳遞給 sqlsrv_query。
vavra at 602 dot cz
6 年前
注意!
如果 SQL 包含 INSERT、UPDATE 或 DELETE 陳述式,則必須處理受影響的資料列數。如果結果不是 false,sqlsrv_query 會返回一個必須讀取才能完成交易的 SQL 資料指標。這同樣適用於 sqlsrv_execute。在這種情況下,也必須使用準備好的陳述式控制代碼 $smt 來讀取資料指標。

另一種解決方案是在 sqlsrv 陳述式以及所有被呼叫的預存程序、函式和觸發程序的頂部放置 SET NOCOUNT ON。

我們實際上在包含 500 個插入的 SQL 陳述式中觀察到它,但只有 368 個被插入,且沒有返回 false。在前面加上 SET NOCOUNT ON 或讀取資料指標的所有資料列後,所有資料列都被插入。

請參閱處理結果 (ODBC):https://docs.microsoft.com/en-us/sql/relational-databases/native-client-odbc-results/processing-results-odbc 每個 INSERT、UPDATE 和 DELETE 陳述式都會返回一個結果集,其中僅包含受修改影響的資料列數。當應用程式呼叫 SQLRowCount 時,此計數會變得可用。ODBC 3.x 應用程式必須呼叫 SQLRowCount 來擷取結果集,或呼叫 SQLMoreResults 來取消它。當應用程式執行包含多個 INSERT、UPDATE 或 DELETE 陳述式的批次或預存程序時,必須使用 SQLRowCount 處理每個修改陳述式的結果集,或使用 SQLMoreResults 取消它。可以透過在批次或預存程序中包含 SET NOCOUNT ON 陳述式來取消這些計數。
bill_spam0001 at yahoo dot com
12 年前
提示:這可能看起來很明顯,但您需要修剪字串以使其符合您透過參數化查詢或預存程序儲存它們的資料庫欄位。(例如:僅提交最多 20 個字元到 VARCHAR(20) 資料庫欄位)。如果您將更大的字串傳送到查詢而不是它可以處理的字串,您將收到錯誤訊息。

清理字串時,您很可能會發現自己使用 php substr() 函式。如文件中所述,當出現空字串時,此函式將返回布林值 FALSE。不注意這個布林值 FALSE 將導致在資料庫表格中儲存「0」而不是空字串。

由於修剪輸入也很重要,因此一個簡單直觀的解決方案是修剪 substr() 輸出,這將始終提供空字串,而不是布林值 FALSE。

所以這將永遠有效
<?php
//修剪最後的結果會傳回空字串,資料型態為字串
$address_line_2 = trim(substr($_POST['addr2']),0,30));

echo
gettype($address_line_2); //輸出 string

//執行資料庫查詢會在 tblAddressBook.addr2 欄位儲存 ""
$sql = "update tblAddressBook set name=(?), addr1=(?), addr2=(?),..."
$params = array($name, $address_line_1, $address_line_2, ...)
$sql_srv_query($db_conn, $sql, $params);

?>
這種方式看似沒問題,但在資料庫中可能會出現意想不到的資料。
<?php
//如果修剪 POST 變數的結果是 ""(空字串),substr() 會傳回 FALSE
$address_line_2 = substr(trim($_POST['addr2'])),0,30);

//$address_line_2 實際上 === FALSE,而不是 ""
echo gettype($address_line_2); //輸出 boolean

//執行資料庫查詢會在 tblAddressBook.addr2 欄位儲存 "0"
$sql = "update tblAddressBook set name=(?), addr1=(?), addr2=(?),..."
$params = array($name, $address_line_1, $address_line_2, ...)
$sql_srv_query($db_conn, $sql, $params);

?>

您也可以使用下列方式將類型強制轉換為字串:
這樣會將布林值 false 轉換回預期的空字串。
<?php

$address_line_2
= (string)substr(trim($_POST['addr2'])),0,30);

echo
gettype($address_line_2); //輸出 string

//執行資料庫查詢會在 tblAddressBook.addr2 欄位儲存 ""
$sql = "update tblAddressBook set name=(?), addr1=(?), addr2=(?),..."
$params = array($name, $address_line_1, $address_line_2, ...)
$sql_srv_query($db_conn, $sql, $params);

?>

我直到換成 IIS7、PHP 5.3.8 和 SQL Server 2008 才注意到這個行為。但使用 IIS7、PHP 5.2 和 SQL Server 2008 時也會出現同樣的行為。
oleg at mastak dot fi
12 年前
如果您在指定有效的可捲動游標時遇到 "[Microsoft][ODBC 驅動程式管理員] 無效的游標狀態" 錯誤,請查看下列錯誤報告

https://bugs.php.net/bug.php?id=63498

目前存在一個 bug,需要在對應的陣列中,先指定 Scrollable 選項,再指定 QueryTimeout 選項。
erikpsundberg at gmail dot com
4 年前
2 個有效的範例

print "<h1>非 PDO 的 SQL 查詢</h1>";
print "<h2>連線</h2>";
$connectionInfo = array( "Database"=>$sql_database, "UID"=>$sql_username, "PWD"=>$sql_password );
$conn = sqlsrv_connect( $sql_server, $connectionInfo);

if( $conn === false ) {
die( print_r( sqlsrv_errors(), true));
} else {
print "資料庫連線成功: $conn<br>";
}

print "<h2>查詢範例 1 | 使用關聯式陣列擷取</h2>";
$sql = "SELECT username, active FROM users WHERE username = '$username'";
print "SQL: $sql\n";
$result = sqlsrv_query($conn, $sql);
if($result === false) {
die(print_r(sqlsrv_errors(), true));
}
#使用陣列擷取資料
while($row = sqlsrv_fetch_array($result, SQLSRV_FETCH_ASSOC)) {
print_r($row);
}

print_line(); // 顯示分隔線 (假設此函數存在)
print "<h2>查詢範例 2 | 防止注入攻擊 | 使用物件擷取</h2>";
$sql = "SELECT username, active FROM users WHERE username = ?";
print "SQL: $sql\n";
$result = sqlsrv_query($conn, $sql, array($username));
if($result === false) {
die(print_r(sqlsrv_errors(), true));
}
#使用物件擷取資料
while($row = sqlsrv_fetch_object($result)) {
print_r($row);
}

--------------------------------------------------
結果

連線
資料庫連線成功: Resource id #2
查詢範例 1 | 使用關聯式陣列擷取
SQL: SELECT username, active FROM users WHERE username = 'admin'
陣列
(
[username] => admin
[active] => 1
)
######################################################################
查詢範例 2 | 防止注入攻擊 | 使用物件擷取
SQL: SELECT username, active FROM users WHERE username = ?
stdClass 物件
(
[username] => admin
[active] => 1
)
anon at example dot com
6 年前
請注意,執行單個查詢時,您可以獲得多個結果集,例如

<?php
$stmt
= sqlsrv_query($conn, "use dbname; select 1");
?>

在 tsql 或管理工作室中執行相同的查詢會按預期工作。但是,使用 sqlsrv_* 函數時,這會提供兩個結果集 - 一個用於「use」,另一個用於「select」。

<?php
// 將結果指標從「use」移至「select」
sqlsrv_next_result($stmt);

// 現在您可以讀取「1」
print_r(sqlsrv_fetch_array($stmt));
?>

對於以建立/填入暫存資料表開頭的複雜查詢,您需要將結果指標移到此類語句之後,才能從最終的 select 語句中獲取資料。在較舊的 PHP 版本中(至少使用基於 FreeTDS 的 mssql 連線),您只會獲得最後一個結果,因此不需要考慮這一點。
To Top