我insert在PHPfor循环中生成一条SQL语句。
insert
for
生成的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,它才能完成。
$InsertSQL
sqlsrv_query
我也没有收到任何错误,查询结果返回真,如以下代码所示:
if(!$InsertResult) die('Problem with Insert query: ' . $InsertSQL);
当我搜索该问题的解决方案时,我发现(尽管PHP手册站点中未提及)sqlsrv_query该$SQL变量显然有一个字符串限制(大约65k个字符)。
$SQL
请参阅此处的其他文章: [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服务器位于单独的计算机上)。据我了解,这消除了这种可能性。
非常感谢任何帮助,因为我什至无法弄清楚是什么限制了我,没关系解决它,我讨厌一次只能执行一行。
说明:
发布在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))。
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
sqlsrv_prepare()\sqlsrv_execute()
桌子:
CREATE TABLE MyTable ( Column1 int, Column2 int, Column3 int )
一个复杂的语句(使用sqlsrv_query()和sqlsrv_next_result()):
sqlsrv_query()
<?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()):
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"; ?>