Wamp+PHP7.1.15+SQL Server:存在则更新否则插入功能失效求助
嘿,我来帮你排查下这个问题!你现在遇到的是「存在则更新,不存在则插入」(也就是常说的UPSERT)逻辑没生效,数据表没变化但又没报错,这种情况通常是几个常见的坑导致的,咱们一步步来理:
1. 先解决直接拼接SQL的致命问题
你现在的SELECT语句是直接把$Client、$Contact、$Jira拼进SQL字符串里的,这不仅有SQL注入风险,还可能因为变量包含特殊字符(比如单引号、空格)导致查询结果为空——程序误以为记录不存在,但实际是查询语句本身就语法错误了,只是PHP没开启错误提示,你看不到而已。
赶紧改成参数绑定的方式执行查询,既能避免注入,又能保证SQL语法正确:
// 用?作为占位符,不要直接拼变量 $stmt = "SELECT * FROM dbo.MyTable WHERE Client = ? AND Contact = ? AND Jira = ?"; $params = array($Client, $Contact, $Jira); $sqlsrv_stmt = sqlsrv_query($conn, $stmt, $params); // 一定要捕获SQL错误,这是排查问题的关键 if ($sqlsrv_stmt === false) { die(print_r(sqlsrv_errors(), true)); }
2. 检查sqlsrv_has_rows的使用逻辑
你得确保调用sqlsrv_has_rows($sqlsrv_stmt)之前,查询已经正确执行,而且没有提前关闭结果集。另外要确认分支逻辑没写反:
- 返回
true时执行更新操作 - 返回
false时执行插入操作
3. 插入/更新语句的参数顺序要对应
你给出的$params1数组是array($Client, $Jira, $Contact, $lvlsatisfaction, $Commentaire),但要注意它必须和插入/更新语句里的占位符顺序完全匹配。比如如果你的更新语句是:
UPDATE dbo.MyTable SET LvlSatisfaction = ?, Commentaire = ? WHERE Client = ? AND Contact = ? AND Jira = ?
那参数顺序应该是array($lvlsatisfaction, $Commentaire, $Client, $Contact, $Jira),顺序错了会导致数据插错位置,甚至因为类型不匹配静默失败。
4. 更高效可靠的终极方案:用SQL Server的MERGE语句
其实SQL Server原生支持MERGE语法,可以用一条SQL完成UPSERT逻辑,不用先查再判断,还能避免并发场景下的竞态问题(比如两个请求同时查都没记录,然后都插入导致重复数据)。示例代码:
$mergeStmt = " MERGE INTO dbo.MyTable AS Target USING (VALUES (?, ?, ?, ?, ?)) AS Source (Client, Contact, Jira, LvlSatisfaction, Commentaire) ON Target.Client = Source.Client AND Target.Contact = Source.Contact AND Target.Jira = Source.Jira WHEN MATCHED THEN UPDATE SET LvlSatisfaction = Source.LvlSatisfaction, Commentaire = Source.Commentaire WHEN NOT MATCHED THEN INSERT (Client, Contact, Jira, LvlSatisfaction, Commentaire) VALUES (Source.Client, Source.Contact, Source.Jira, Source.LvlSatisfaction, Source.Commentaire); "; $mergeParams = array($Client, $Contact, $Jira, $lvlsatisfaction, $Commentaire); $mergeResult = sqlsrv_query($conn, $mergeStmt, $mergeParams); if ($mergeResult === false) { die(print_r(sqlsrv_errors(), true)); } // 如果开启了事务,记得提交 sqlsrv_commit($conn);
最后别忘了开启错误提示
浏览器没报错大概率是PHP错误显示没打开,在代码开头加上:
error_reporting(E_ALL); ini_set('display_errors', 1);
这样任何PHP或SQL的错误都会直接显示出来,排查问题会轻松很多。
内容的提问来源于stack exchange,提问作者Daniel Jean

