Search code examples
phpmysqlpdoprepared-statementprepare

Inserting multiple rows (single query) to MySQL in PHP: Prepare-Execute vs. Prepare-Bind-Execute


I'm writing some PHP to accept an array of numbers and names in POST and insert them into a MySQL table (named Contacts_table) Here's the version that works fine without any error:

<?php

  // Includes
  require_once 'Admin/Connector.php';

  // Test if payload exists
  if($_POST){

  // Read payload into arrays
      $ar = 0;
      foreach($_POST as $entry){
          $namenum = explode(',', $entry);
          $names[$ar] = $namenum[1];
          $numbers[$ar] = $namenum[0];
          $ar += 1;
      }
      $namenum = NULL;

  // Build SQL query
      $sql = 'INSERT INTO Contact_table (NAME, PHONE) VALUES ';
      $insertQuery = array();
      $insertData = array();
      $n = 0;
      foreach ($numbers as $num) {
        $insertQuery[] = '(?, ?)';
        $insertData[] = $names[$n];
        $insertData[] = $num;
        $n++;
      }
      $sql .= implode(', ', $insertQuery);
      $sql .= ' ON DUPLICATE KEY UPDATE name = COALESCE(VALUES(name), name);';
      $n = NULL;
      $num = NULL;

  // Connect to MySQL database
      $connect = dbconn(PROJHOST,PROJDB,PROJDBUSER,PROJDBPWD);

  // Execute SQL query
      $query = $connect->prepare($sql);
      $query->execute($insertData);
      $insertQuery = NULL;
      $insertData = NULL;
      $sql = NULL;
      $query = NULL;

  // Close connection to MySQL database
      $connect = NULL;

  }

?>

However, as you can see, I'm not using the bindParam() function here and just feeding the values directly to the execute() function. Many have recommended that I use bindParam() instead for server performance gains. Is it true or I am better off with this program as it stands? I did try writing and running a version of the above code using bindParam:

<?php

  // Includes
  require_once 'Admin/Connector.php';

  // Test if payload exists
  if($_POST){

  // Read payload into arrays
      $ar = 0;
      foreach($_POST as $entry){
          $namenum = explode(',', $entry);
          $names[$ar] = $namenum[1];
          $numbers[$ar] = $namenum[0];
          $ar += 1;
      }
      $namenum = NULL;

  // Build SQL query
      $sql = 'INSERT INTO Contact_table (NAME, PHONE) VALUES ';
      $insertQuery = array();
      $insertData = array();
      $n = 0;
      foreach ($numbers as $num) {
        $insertQuery[] = '(?, ?)';
        $insertData[] = $names[$n];
        $insertData[] = $num;
        $n++;
      }
      $sql .= implode(', ', $insertQuery);
      $sql .= ' ON DUPLICATE KEY UPDATE name = COALESCE(VALUES(name), name);';
      $n = NULL;
      $num = NULL;

  // Connect to MySQL database
      $connect = dbconn(PROJHOST,PROJDB,PROJDBUSER,PROJDBPWD);

   // Prepare SQL query 
      $query = $connect->prepare($sql);

  // Bind variables
      foreach($insertData as $key => &$ins) {
          $connect->bindParam($key+1,$ins);
      }

  // Execute SQL query
      $query->execute();
      $insertQuery = NULL;
      $insertData = NULL;
      $sql = NULL;
      $query = NULL;
      $key = NULL;
      $ins = NULL;

  // Close connection to MySQL database
      $connect = NULL;

  }

?>

But this code refuses to run and returns a fatal error – Call to undefined method PDO::bindParam(). What am I doing wrong here? I understand it's possible to write a much simpler code if I include execute() within the loop but that would spawn multiple queries which I want to avoid at all costs. My goal is a single query no matter what.


Solution

  • You can't $connect->bindParam($key+1,$ins);. Because PDO object doesn't have such method. Only PDOStatement has. That is why you've got error message.

    You should :

     $query->bindValue($key+1,$ins);
    

    And you should use bindValue because if not, all your inserted values will get same value (the last one of $ins before you call execute).