web-dev-qa-db-ja.com

名前付きプレースホルダーを使用して、INSERTおよびON DUPLICATE KEYUPDATEのPDOプリペアドステートメント

PDOINSERTおよびUPDATEプリペアドステートメントをINSERTおよびONDUPLICATE 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ステートメントのセットがあります。

12
Chaya Cooper

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);      

ON DUPLICATE KEY句にテーブル名またはSETを入れる必要はなく、WHERE句も必要ありません(常に重複キーでレコードを更新します) )。

http://dev.mysql.com/doc/refman/5.5/en/insert-on-duplicate.html を参照してください

また、PHP構文エラー:クエリを2つの文字列に分割しました。

更新:

複数のパラメーターをバインドするには:

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);
18
Barmar

以下の私見は、これに再び遭遇する人にとって正しい答えです。
注:このステートメントは、user_idがテーブル内のKEYであることを前提としています。

声明は確かに間違っていましたが、受け入れられた答えは完全に正しくありませんでした。

同じ値を使用して挿入および更新する場合(異なる値で更新しない場合)、これは修正されたクエリの擬似コードです。

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
}
18
Ligemer