【问题标题】:Does sqlsrv_query limit the number of statements that can be exectuted in one query?sqlsrv_query 是否限制一个查询中可以执行的语句数?
【发布时间】:2020-09-22 21:29:53
【问题描述】:

我正在 PHP for 循环中生成 SQL insert 语句。

生成的 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 行。只有当我通过sqlsrv_query 运行$InsertSQL 变量时,它才不会完成。

我也没有收到任何错误,并且查询结果在此行中测试为真:

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

当我搜索此问题的解决方案时,我看到(尽管 PHP 手册站点中没有提到)sqlsrv_query 显然对 $SQL 变量有字符串字符限制(大约 65k 个字符)。

在此处查看其他 StackOverflow 文章: length restriction for the sql variable on sqlsrv_query?

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

我也可以单独执行每个insert 语句,但这当然需要更长的时间。在我的测试中,这样做需要 90 秒左右,而在 SMSS 中手动运行组合语句只需要 40 秒左右。

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

非常感谢任何帮助,因为我什至无法弄清楚是什么限制了我,别介意修复它,我不想一次执行一行。

【问题讨论】:

  • 你有什么理由不这样做作为一个插入?性能会更好
  • 是的,如果这样做,会有 1000 行的限制。我的后备解决方案是回到那个状态,但每 999 行执行插入语句,然后重新开始直到完成。虽然有点乱,最后仍然需要多个插入语句(虽然少得多)
  • 三个查询也是如此。还是更快
  • 我编辑了我的评论并解决了这种可能性。
  • @AlwaysLearning SQL Server 对 INSERT INTO ...VALUES() 语句的行数限制为 1000。

标签: php sql sql-server sql-insert sqlsrv


【解决方案1】:

说明:

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

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

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

解决方案:

我能够重现此问题(使用 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";
?>

【讨论】:

  • 该应用程序现在正在使用多批处理方法进行测试,并且运行正常。一旦测试完成,我一定会尝试一下。看起来这是个问题。我没有找到那篇文章,感谢您的链接和所有 3 个详细的解决方案!很高兴知道不只是我!超级有帮助!再次感谢!
猜你喜欢
  • 1970-01-01
  • 2017-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-16
  • 2013-01-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多