【问题标题】:PDO prepared statements for INSERT and ON DUPLICATE KEY UPDATE with named placeholdersPDO 使用命名占位符为 INSERT 和 ON DUPLICATE KEY UPDATE 准备语句
【发布时间】:2012-12-14 20:56:28
【问题描述】:

我想将 PDO INSERT 和 UPDATE 准备语句切换为 INSERT 和 ON DUPLICATE KEY UPDATE,因为我认为它会比我目前正在做的更有效,但我无法弄清楚与命名占位符和 bindParam 一起使用的正确语法。

我在 SO 上发现了几个类似的问题,但我是 PDO 的新手,无法根据我的标准成功调整代码。这是我尝试过的,但它不起作用(它不插入或更新):

try { 
  $stmt = $conn->prepare('INSERT INTO customer_info (user_id, fname, lname) VALUES(:user_id, :fname, :lname)'          
 'ON DUPLICATE KEY UPDATE customer_info SET fname= :fname, 
                                            lname= :lname   
                                            WHERE user_id = :user_id'); 
  $stmt->bindParam(':user_id', $user_id);  
  $stmt->bindParam(':fname', $_POST['fname'], PDO::PARAM_STR);
  $stmt->bindParam(':lname', $_POST['lname'], PDO::PARAM_STR);      
  $stmt->execute();
}

这是我的代码的简化版本(我有几个查询,每个查询有 20 到 50 个字段)。我目前正在首先更新并检查更新的行数是否大于 0,如果不是,则运行 Insert,每个查询都有自己的一组 bindParam 语句。

【问题讨论】:

  • 不要在同一个查询的多个地方重复使用同一个占位符。您的 PDO 连接是否设置为抛出异常?除非你真的需要bindParambindValue或者通过execute传递参数是更好的选择。
  • 当你说我不应该在多个地方重复使用同一个占位符时,你的意思是我需要有 2 组 bindParam 语句吗?我目前用 - catch(PDOException $e) {echo 'Error: ' 设置它。 $e->getMessage();} - 我没有收到此代码的任何错误消息。
  • 仅在查询周围放置 try/catch 是不够的。请参阅Error Handling 并将其配置为在错误时引发异常。
  • @DCoder:您介意详细说明您的意思吗?我认为我这样做的方式与手册中的 ERRMODE_EXCEPTION 一致,应该会抛出 PDOException。
  • 请参阅this question 了解bindParambindValue 之间的差异。 $stmt->execute(array(':fname' => $_POST['fname'])) 也可用于传递绑定变量。在我看来,execute 是最简单的选择,除非您需要bindParambindValue 的特定行为,否则应该首选。当然,您的里程可能会有所不同。

标签: php pdo prepared-statement on-duplicate-key sqlbindparameter


【解决方案1】:

恕我直言,对于再次遇到此问题的人来说,这是正确的答案。
注意:此语句假定 user_id 是表中的 KEY。

STATEMENT 确实是错误的,但接受的答案并不完全正确。

如果您使用相同的值插入和更新(而不是使用不同的值进行更新),则查询伪代码已更正:

try { 
    //optional if your DB driver supports transactions
    $conn->beginTransaction();

    $stmt = $conn->prepare('INSERT INTO customer_info (user_id, fname, lname) ' . 
                'VALUES(:user_id, :fname, :lname)' .
                'ON DUPLICATE KEY UPDATE fname=VALUES(fname), lname=VALUES(lname)');
    $stmt->bindParam(':user_id', $user_id);  
    $stmt->bindParam(':fname', $_POST['fname'], PDO::PARAM_STR);
    $stmt->bindParam(':lname', $_POST['lname'], PDO::PARAM_STR);      
    $stmt->execute();

    //again optional if on MyIASM or DB that doesn't support transactions
    $conn->commit();
} catch (PDOException $e) {
    //optional as above:
    $conn->rollback();

    //handle your exception here $e->getMessage() or something
}

【讨论】:

  • +1 我同意在需要使用参数时使用VALUES() 更容易。但作为一个附带问题,你不需要像在 Java 中那样在 PHP 中破坏你的字符串。您可以将多行字符串放入一组引号中。
  • @BillKarwin 是正确的,但我们喜欢在我们公司将我们的行换行到 80 或 100 个字符:-)
  • 再次使用VALUES() 非常优雅的解决方案,而不是重复没有额外后缀的字段名称。它将很快被弃用,并且可以通过仅使用字段名称来简化。 mysql docs.
【解决方案2】:

您的ON DUPLICATE KEY 语法不正确。

$stmt = $conn->prepare('INSERT INTO customer_info (user_id, fname, lname) VALUES(:user_id, :fname, :lname)
    ON DUPLICATE KEY UPDATE fname= :fname2, lname= :lname2');

$stmt->bindParam(':user_id', $user_id);  
$stmt->bindParam(':fname', $_POST['fname'], PDO::PARAM_STR);
$stmt->bindParam(':lname', $_POST['lname'], PDO::PARAM_STR);      
$stmt->bindParam(':fname2', $_POST['fname'], PDO::PARAM_STR);
$stmt->bindParam(':lname2', $_POST['lname'], PDO::PARAM_STR);      

您不需要将表名或SET 放在ON DUPLICATE KEY 子句中,也不需要WHERE 子句(它总是使用重复键更新记录)。

http://dev.mysql.com/doc/refman/5.5/en/insert-on-duplicate.html

您还有一个 PHP 语法错误:您将查询分成两个字符串。

更新:

绑定多个参数:

function bindMultiple($stmt, $params, &$variable, $type) {
  foreach ($params as $param) {
    $stmt->bindParam($param, $variable, $type);
  }
}

然后调用它:

bindMultiple($stmt, array(':fname', ':fname2'), $_POST['fname'], PDO::PARAM_STR);

【讨论】:

  • 感谢您解释得如此清楚 :-) 有没有办法不必为每个参数创建两个 bindParam 语句?
  • 不,PDO 要求每个占位符都是唯一的。您可以编写一个函数,该函数接受一个占位符数组和一个变量,并在循环中调用bindParam() 将它们全部绑定。
  • 你太棒了:-D我早上第一件事就试试看:-)
  • 我刚刚注意到一个问题。 bindParam 要求变量是引用。我刚刚更改了bindMultiple 以反映这一点。
  • 是的,只需将其更改为使用普通参数而不是引用即可。我不认为有任何性能问题,这样做没有什么特别的。
猜你喜欢
  • 1970-01-01
  • 2014-05-07
  • 1970-01-01
  • 2010-10-07
  • 1970-01-01
  • 1970-01-01
  • 2013-10-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多