当前位置: 首页 > 面试题库 >

sqlsrv_query是否限制在一个查询中可以执行的语句数?

洪高扬
2023-03-14
问题内容

insert在PHPfor循环中生成一条SQL语句。

生成的SQL字符串是大量的单个SQL语句,如下所示:

INSERT INTO tbl VALUES(1,2,3);
INSERT INTO tbl VALUES(4,5,6);
INSERT INTO tbl VALUES(7,8,9);

等等…

然后我执行:

$InsertResult = sqlsrv_query($conn, $InsertSQL);

问题在于仅执行前312条语句,而不执行完整的2082行(表中仅插入312行)。

当我将$InsertSQL变量输出到JavaScript控制台,然后在SSMS中手动执行它时,它可以完美工作并插入所有2082行。只有当我运行$InsertSQL变量时sqlsrv_query,它才能完成。

我也没有收到任何错误,查询结果返回真,如以下代码所示:

if(!$InsertResult) die('Problem with Insert query: ' . $InsertSQL);

当我搜索该问题的解决方案时,我发现(尽管PHP手册站点中未提及)sqlsrv_query$SQL变量显然有一个字符串限制(大约65k个字符)。

请参阅此处的其他StackOverflow文章:
sqlsrv_query上sql变量的长度限制?

我发现这就是问题所在,因此创建了一个较短版本的字符串(通过仅添加我实际想要导入的列值)。但是,这个短得多的版本仍然只插入前312行!所以现在看来​​这与最大字符串长度无关。实际上,如果是这样,我应该只能得到大约250行(在执行250条语句后,我大约需要65k个字符)。

我也可以insert单独执行每个语句,但是当然这要花费更长的时间。在我的测试中,以这种方式执行此操作大约需要90
s,而在SMSS中手动运行组合语句仅需40 s。

请注意,我也研究了SQL Server的批量插入,但是无法将文件发送到安装SQL Server的计算机上(SQL
Server和Web服务器位于单独的计算机上)。据我了解,这消除了这种可能性。

非常感谢任何帮助,因为我什至无法弄清楚是什么限制了我,没关系解决它,我讨厌一次只能执行一行。


问题答案:

说明:

发布在GitHub上的该驱动程序存在一个有关执行大型SQL语句的已知问题。提供的解决方案的一部分是以下说明:

似乎在执行大批SQL语句时,Microsoft SQL Server可能会在执行该批处理中的所有语句之前停止处理该批处理。 处理批处理的结果时,SQL
Server用该批处理创建的结果集填充连接的输出缓冲区。这些结果集必须由客户端应用程序处理。如果您正在执行具有多个结果集的大批处理,则SQL
Server会填充该输出缓冲区,直到达到内部限制并且无法继续处理更多结果集。此时,控制权返回给客户端。此行为是设计使然。客户端应用应刷新所有待处理的结果集。客户端使用完所有待处理的结果集后,SQL
Server将立即执行批处理。客户端应用程序可以调用sqlsrv_next_result(),直到返回NULL。

因此,我认为SQL语句的长度没有限制,只有PHP字符串变量的大小($InsertSQL在您的情况下)被限制为允许的最大PHP内存限制。发生这种意外行为的实际原因是这样的事实,即SQL
Server使用SET NOCOUNT OFF(默认情况下)和大量单个INSERT语句,将受影响的行的计数作为结果集返回(例如(1 row affected))。

解决方案:

我能够重现此问题(使用SQL Server 2012,PHP 7.1.12和SQL Server 4.3.0 +
9904的PHP驱动程序),并且您可以使用以下选项来解决此问题:

  • 使用刷新待处理的结果集sqlsrv_next_result()
  • SET NOCOUNT ON在复杂的T-SQL语句中作为第一行执行,以停止SQL Server将受影响的行的计数作为结果集返回。
  • 使用参数化语句 sqlsrv_prepare()\sqlsrv_execute()

桌子:

CREATE TABLE MyTable (
    Column1 int,
    Column2 int,
    Column3 int
)

一个复杂的语句(使用sqlsrv_query()sqlsrv_next_result()):

<?php

// Connection info
$server = 'server\instance';
$database = 'database';
$username = 'username';
$password = 'password';
$cinfo = array(
    "Database" => $database,
    "UID" => $username,
    "PWD" => $password
);

// Statement with sqlsrv_query
$sql = "";
for ($i = 1; $i <= 1000; $i++) {
    $sql .= "INSERT INTO MyTable (Column1, Column2, Column3) VALUES (".$i.", 0, 0);";
}
$stmt = sqlsrv_query($con, $sql);
if ($stmt === false) {
    echo "Error (sqlsrv_query): ".print_r(sqlsrv_errors(), true);
    exit;
}

// Clean the buffer
while (sqlsrv_next_result($stmt) != null){};

// End
sqlsrv_free_stmt($stmt);
sqlsrv_close($con);
echo "OK";
?>

一个复杂的语句(使用sqlsrv_query()SET NOCOUNT ON):

<?php

// Connection info
$server = 'server\instance';
$database = 'database';
$username = 'username';
$password = 'password';
$cinfo = array(
    "Database" => $database,
    "UID" => $username,
    "PWD" => $password
);

// Connection
$con = sqlsrv_connect($server, $cinfo);
if ($con === false) {
    echo "Error (sqlsrv_connect): ".print_r(sqlsrv_errors(), true);
    exit;
}

// Statement with sqlsrv_query
$sql = "SET NOCOUNT ON;";
for ($i = 1; $i <= 1000; $i++) {
    $sql .= "INSERT INTO MyTable (Column1, Column2, Column3) VALUES (".$i.", 0, 0);";
}
$stmt = sqlsrv_query($con, $sql);
if ($stmt === false) {
    echo "Error (sqlsrv_query): ".print_r(sqlsrv_errors(), true);
    exit;
}

// End
sqlsrv_free_stmt($stmt);
sqlsrv_close($con);
echo "OK";
?>

参数化的语句(使用sqlsrv_prepare()sqlsrv_execute()):

<?php

// Connection info
$server = 'server\instance';
$database = 'database';
$username = 'username';
$password = 'password';
$cinfo = array(
    "Database" => $database,
    "UID" => $username,
    "PWD" => $password
);

// Connection
$con = sqlsrv_connect($server, $cinfo);
if ($con === false) {
    echo "Error (sqlsrv_connect): ".print_r(sqlsrv_errors(), true);
    exit;
}

$sql = "INSERT INTO MyTable (Column1, Column2, Column3) VALUES (?, ?, ?);";
$value1 = 0;  
$value2 = 0;  
$value3 = 0;  
$params = array(&$value1, &$value2, &$value3);
$stmt = sqlsrv_prepare($con, $sql, $params);
if ($stmt === false ) {
    echo "Error (sqlsrv_prepare): ".print_r(sqlsrv_errors(), true);
    exit;
}
for ($i = 1; $i <= 1000; $i++) {
    $value1 = $i;  
    $value2 = 0;  
    $value3 = 0;  
    $result = sqlsrv_execute($stmt);
    if ($result === false) {
        echo "Error (sqlsrv_execute): ".print_r(sqlsrv_errors(), true);
        exit;
    }
}

// End
sqlsrv_free_stmt($stmt);
sqlsrv_close($con);
echo "OK";
?>


 类似资料:
  • 我正在将select查询传递给准备好的语句以获取记录列表。问题是,如果我将查询追加到insert或truncate table中,那么准备好的语句将执行所有查询。示例 如何限制sql查询只执行select查询,或者在Java中一次至少只执行一个查询。

  • 问题内容: 我想知道是否可以使用JDBC执行类似的操作,因为即使在MySQL查询浏览器中,它当前也提供了异常。 虽然我确实意识到可以拆分SQL查询字符串并执行两次语句,但是我想知道是否有一次性的方法。 问题答案: 我想知道是否可以使用JDBC执行类似的操作。 对的,这是可能的。据我所知,有两种方法。他们是 通过设置数据库连接属性以允许多个查询,默认情况下用分号分隔。 通过调用返回隐式游标的存储过程

  • 我想知道是否可以在一个准备好的语句中执行多个SQL查询。这是我要执行的第一个查询:

  • 问题内容: 我想在hibernateHql的同一查询中执行多个更新语句。如下所示: 在同一调用中,我想更新表1中的记录并从表2中删除记录。 那可能吗? 问题答案: 简而言之,您所看到的就像是JDBC中的批处理。Hichnate没有为批量更新查询提供Thich,并且我怀疑是否会为Hibernate考虑它。 根据我过去的经验,HQL的批处理功能在现实生活中很少有用。在SQL + JDBC中有用但在HQ

  • 问题内容: 如何将这些多个查询合并为一个(可以吗?) 顺便问一下,如果在完成所有查询后再执行mysql_close($ db),那会更好吗? 问题答案: 传递到作为第五个参数。 例: 当您使用mysql_fetch_ *或mysql_num_rows或mysql_affected_rows时,仅第一条语句有效。 例如,以下代码,第一个语句为INSERT,则无法执行mysql_num_rows和my