PHP PDO传递数组至SQL Server表值参数时触发错误
问题描述
使用PHP PDO驱动向MSSQL存储过程传递表值参数(Table Valued Parameter)时,触发以下错误:
Uncaught PDOException: SQLSTATE[IMSSP]: An invalid type for parameter 8 was specified. Only booleans, integers, floating point numbers, strings, and streams may be used as parameters
相关代码片段:
$serialtablejsonstr = $_POST["serialtablearrstr"]; $serialtablearr = json_decode($serialtablejsonstr,true); $tvpType = "SerialInfoTblType"; $tvpInput = array($tvpType => $serialtablearr); $con = connectDatabasePDO(); $sqladdoldstock = "EXEC StoreEntry @type = 2, @ItemId = :item, @ItemDetail = :itemspecs, @guarantee = :gaurantee, @Warranty = :warranty, @Amount = :unitamount, @itemtype = :itemtype, @TenderId = :istender, @FirmId = :firmid, @Serialinfotbl = :serialtable, @FirmInvoiceNo = :firminvoiceno"; $stmtaddoldstock = $con->prepare($sqladdoldstock); $stmtaddoldstock->bindParam(':item', $item); $stmtaddoldstock->bindParam(':itemspecs', $itemspecs); $stmtaddoldstock->bindParam(':gaurantee', $gaurantee); $stmtaddoldstock->bindParam(':warranty', $warranty); $stmtaddoldstock->bindParam(':unitamount', $unitamount); $stmtaddoldstock->bindParam(':itemtype', $itemtype); $stmtaddoldstock->bindParam(':istender', $istender); $stmtaddoldstock->bindParam(':firmid', $firmid); $stmtaddoldstock->bindParam(':serialtable', $tvpInput, PDO::PARAM_LOB); $stmtaddoldstock->bindParam(':firminvoiceno', $firminvoiceno); $stmtok = $stmtaddoldstock->execute(); $arr = $stmtaddoldstock->errorInfo(); if(!$stmtok) { echo "Error"; } else { echo "Success"; } $con = null; $stmtaddoldstock = null;
已尝试将数组转为JSON字符串并以PDO::PARAM_STR传递,问题依旧。原应用基于.NET开发,当时通过DataTable传递表值参数,已参考官方文档但未解决。
解决方法
PHP PDO调用MSSQL表值参数时,需遵循特定的参数构造和绑定规则,以下是修正方案:
1. 确认表值参数结构匹配
先确保SQL Server中定义的SerialInfoTblType表类型,其列名、数据类型与你传入的数组元素完全对应。比如表类型包含SerialNumber、ExpiryDate列,那么$serialtablearr中的每个子数组必须包含这两个键,且值的类型符合表列要求。
2. 修正参数绑定逻辑
不能用PDO::PARAM_LOB或普通字符串绑定表值参数,需使用bindValue配合特殊结构传递,具体代码修正如下:
$serialtablejsonstr = $_POST["serialtablearrstr"]; $serialtablearr = json_decode($serialtablejsonstr, true); // 验证子数组键与表类型列名一致,比如SerialInfoTblType的列是SerialNo、CreateDate // 则每个子数组必须是["SerialNo" => "XXX", "CreateDate" => "2024-05-20"] $con = connectDatabasePDO(); $sqladdoldstock = "EXEC StoreEntry @type = 2, @ItemId = :item, @ItemDetail = :itemspecs, @guarantee = :gaurantee, @Warranty = :warranty, @Amount = :unitamount, @itemtype = :itemtype, @TenderId = :istender, @FirmId = :firmid, @Serialinfotbl = :serialtable, @FirmInvoiceNo = :firminvoiceno"; $stmtaddoldstock = $con->prepare($sqladdoldstock); // 绑定普通参数 $stmtaddoldstock->bindParam(':item', $item); $stmtaddoldstock->bindParam(':itemspecs', $itemspecs); $stmtaddoldstock->bindParam(':gaurantee', $gaurantee); $stmtaddoldstock->bindParam(':warranty', $warranty); $stmtaddoldstock->bindParam(':unitamount', $unitamount); $stmtaddoldstock->bindParam(':itemtype', $itemtype); $stmtaddoldstock->bindParam(':istender', $istender); $stmtaddoldstock->bindParam(':firmid', $firmid); $stmtaddoldstock->bindParam(':firminvoiceno', $firminvoiceno); // 绑定表值参数:使用bindValue,传入[表类型名 => 二维数据数组]的结构,类型指定为PDO::PARAM_NULL $stmtaddoldstock->bindValue(':serialtable', [ 'SerialInfoTblType' => $serialtablearr ], PDO::PARAM_NULL); $stmtok = $stmtaddoldstock->execute(); $arr = $stmtaddoldstock->errorInfo(); if(!$stmtok) { echo "Error: " . $arr[2]; // 输出具体错误信息便于排查 } else { echo "Success"; } $con = null; $stmtaddoldstock = null;
3. 关键注意事项
- 表值参数必须构造为
["表类型名" => 二维数组]的格式,二维数组的每个元素对应表的一行数据,键名需与表类型列名完全匹配(大小写敏感,取决于SQL Server设置)。 - 必须使用
bindValue绑定表值参数,不能用bindParam(bindParam是引用传递,无法正确处理表值参数的结构)。 - 绑定类型指定为
PDO::PARAM_NULL,MSSQL PDO驱动会自动识别并处理为表值参数。 - 确保使用的PHP版本和Microsoft SQL Server PHP驱动版本支持表值参数(推荐使用最新版驱动)。
内容的提问来源于stack exchange,提问作者Vikas Gopalani
相关产品推荐
相关产品推荐

