小编典典

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

sql

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个字符)。

请参阅此处的其他文章:
[sqlsrv_query上sql变量的长度限制?](https://codingdict.com/questions/209344

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

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

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

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


阅读 203

收藏
2021-04-14

共1个答案

小编典典

说明:

发布在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";
?>
2021-04-14