PDO lastInsertID() 由于在一次调用中运行多个查询而失败
Posted
技术标签:
【中文标题】PDO lastInsertID() 由于在一次调用中运行多个查询而失败【英文标题】:PDO lastInsertID() failing due to running multiple queries in a single call 【发布时间】:2019-09-29 23:30:03 【问题描述】:这很奇怪。我正在运行一个只有一个 INSERT 的查询,前面是一个 SET 语句。查询看起来像这样:
SET @discount:=(SELECT discount * :applyDiscount FROM fra_cus WHERE customerID=:customerID AND franchiseID=:franchiseID);
INSERT INTO discounts_applied (unitID, franchiseID, customerID, amount)
VALUES(:unitID, :franchiseID, :customerID, @discount * :price);
看来,如果我将它们准备为两个单独的 PDO 查询,lastInsertID() 可以正常工作......但如果我准备它们并在同一语句中执行它们,lastInsertID() 不会返回任何内容。
这不是世界末日,但很烦人。有谁知道为什么会这样?作为记录,我需要将 @discount 定义为变量(与其中一个表上的触发器有关)是有原因的。而且这一切都发生在一个更大的交易中。
【问题讨论】:
【参考方案1】:首先,我强烈建议在不同的 API 调用中运行每个查询。这就是应用程序编程接口打算工作的方式。
它不仅可以防止这种情况发生,还可以使您的代码更具可读性和可维护性。
它也会让你的代码更安全。您可以在一次调用中运行多个语句,但代价是原生预处理语句。不管这个漏洞是虚拟的,为什么要冒险呢?
为什么不进行常规的 SELECT 查询而不是 SET,将结果值放入 php 变量中,然后通过占位符在其他变量中使用它?我看不出有什么理由应该有如此复杂的方式来处理简单的数据。
如果我没能说服你,原因很简单。您正在运行 两个 查询,第一个不会触发任何插入 ID。显然,您需要此查询的元数据(错误、受影响的行等),而不是其他的第一个。所以你明白了。要获得第二个'查询的元数据,您必须向数据库询问它。该过程在我的文章中进行了解释:Treating PHP delusions - The only proper PDO tutorial: Running multiple queries with PDO。基本上PDOStatement::nextRowset()
就是你所需要的。
【讨论】:
啊。很有意义……第一个查询提供了行数。丁。至于不将这些包装在单个准备好的语句中,我基本上认为我通过只准备一次来节省周期并避免延迟......它不是模拟语句(除非模拟在单个 PDO 执行中自动启动多个语句...... . 这是暗示吗?)另外 - 在 SELECT 中使用 SET 而不是内联 @var:= 是否有任何性能损失?事实上,我是用 SET 写的,以阐明查询的目的。 我也对你的陈述感到困惑,即在仿真模式下“同名占位符可以在同一个查询中使用任意次数,而相应的变量只能绑定一次”但是它不适用于正常模式。 AFAIK我没有在仿真模式下运行任何这些,并且我可以使用数组(“:var”=> $ var)执行,其中“:var”在查询中多次显示,而mysql没有任何错误。也许现在 PHP 7 才支持这一点? 您正在使用仿真模式。您不保存任何周期。这两种方式都没有性能影响。性能是一个复杂的话题,您的担忧在这里完全是错误的。 当我连接到数据库时,我有 setAttribute(PDO::ATTR_EMULATE_PREPARES, false) 。我如何使用仿真模式?以上是关于PDO lastInsertID() 由于在一次调用中运行多个查询而失败的主要内容,如果未能解决你的问题,请参考以下文章
为啥 PDO 的 Oracle 驱动程序不实现 lastInsertId()?
多线程单连接中的 PDO::lastInsertId() 是不是安全?