PHP Conference Japan 2024

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 (可选) 一个SQLSRV_PHPTYPE_*常量,指定返回值的PHP数据类型。
$sqlType (可选) 一个SQLSRV_SQLTYPE_*常量,指定输入值的SQL Server数据类型。
options

一个指定查询属性选项的数组。支持的键在下表中描述

查询选项
描述
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 传递未定义的连接对象。
vavra at 602 dot cz
6 年前
注意!
如果sql包含INSERT、UPDATE或DELETE语句,则必须使用受影响的行数。sqlsrv_query返回一个sql游标,如果结果不是false,则必须读取该游标以完成事务。这同样适用于sqlsrv_execute。在这种情况下,还必须使用准备好的语句句柄$smt读取游标。

另一种解决方案是在sqlsrv语句以及所有调用的过程、函数和触发器的顶部放置SET NOCOUNT ON。

我们实际上已经观察到,对于包含500个插入的sql语句,如果没有返回false,则只插入了368个。通过添加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
//trim 最后返回我们的空字符串作为字符串数据类型
$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 Driver Manager] Invalid cursor state”,请查看以下错误报告

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

目前存在一个错误,要求在相应的数组中先指定 Scrollable 选项,然后再指定 QueryTimeout。
erikpsundberg at gmail dot com
4年前
2 个有效的示例

print "<h1>SQL 查询非 PDO</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);
}

--------------------------------------------------
结果

连接
良好的数据库连接: 资源 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