Home » Php » php – Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in

php – Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in

Posted by: admin July 12, 2020 Leave a comment

Questions:

I’m working with PHP PDO and I have the following problem:

Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in /var/www/site/classes/enterprise.php on line 63

Here is my code:

    public function getCompaniesByCity(City $city, $options = null) {
  $database = Connection::getConnection();

  if(empty($options)) {
   $statement = $database->prepare("SELECT * FROM `empresas` WHERE `empresas`.`cidades_codigo` = ?");
   $statement->bindValue(1, $city->getId());
  }
  else {
   $sql = "SELECT * FROM `empresas`
    INNER JOIN `prods_empresas` ON `prods_empresas`.`empresas_codigo` = `empresas`.`codigo` WHERE ";

   foreach($options as $option) {
    $sql .= '`prods_empresas`.`produtos_codigo` = ? OR ';
   }

   $sql = substr($sql, 0, -4);
   $sql .= ' AND `empresas`.`cidades_codigo` = ?';

   $statement = $database->prepare($sql);

   echo $sql;

   foreach($options as $i => $option) {
    $statement->bindValue($i + 1, $option->getId());
   }

   $statement->bindValue(count($options), $city->getId());
  }

  $statement->execute();

  $objects = $statement->fetchAll(PDO::FETCH_OBJ);
  $companies = array();

  if(!empty($objects)) {
   foreach($objects as $object) {
    $data = array(
     'id' => $object->codigo,
     'name' => $object->nome,
     'link' => $object->link,
     'email' => $object->email,
     'details' => $object->detalhes,
     'logo' => $object->logo
    );

    $enterprise = new Enterprise($data);
    array_push($companies, $enterprise);
   }

   return $companies;
  }
 }
How to&Answers:

You have a mismatch between the amount of bound parameters and the amount of binds in the SQL. Double check that the amount of ? and the amount of bound parameters is the same.

Additionally, HY093 will show up if you have tried to bind a parameter that does not exist:

$stmt = "INSERT INTO table VALUES (:some_value)";
$stmt->bindValue(':someValue', $someValue, PDO::PARAM_STR);

See that :some_value does not match :someValue! The fix is:

$stmt = "INSERT INTO table VALUES (:some_value)";
$stmt->bindValue(':some_value', $someValue, PDO::PARAM_STR);

Answer:

It looks like you’re trying to build a long(?) series of ‘or’ comparisons: if (x=1) or (x=2) or (x=3) etc.... You may find it easier to replace it with:

$cnt = count($options);
if ($cnt > 0) {
   $placeholders = str_repeat(', ?', $cnt - 1);
   $sql .= 'WHERE '`prods_empresas`.`produtos_codigo` IN (?' . $placeholders . ')';
}

which, if there were 5 options, would give you

 WHERE prods_empresas.produtos_condigo IN (?, ?, ?, ?, ?)

And then do the values binding with:

$pos = 1;
foreach ($options as $option) {
   $statement->bindValue($pos, $option->getId());
   $pos++
}

Answer:

Positional parameters in SQL start at 1. You’re handling this by binding to position $i+1 in your $options loop.

But then you bind the last parameter for cidades_codigo to position count($options), which overwrites the last parameter set in the $options loop.

You need to bind the last parameter to position count($options)+1.


FWIW, you don’t need to bindValue() at all. It’s easier to just pass an array of parameters to execute(). Here’s how I’d write this function:

public function getCompaniesByCity(City $city, $options = null) {
  $database = Connection::getConnection();

  $sql = "SELECT * FROM `empresas` WHERE `empresas`.`cidades_codigo` = ?"

  $params = array();
  $params[] = $city->getId();

  if ($options) {
    $sql .= " AND `prods_empresas`.`produtos_codigo` IN (" 
      . join(",", array_fill(1, count($options), "?") . ")";
    foreach ((array)$options as $option) {
      $params[] = $option->getId();
    }
  }

  $statement = $database->prepare($sql);

  echo $sql;

  $statement->execute($params);
  . . .

Also be sure to check the return value of prepare() and execute(), it will be false if there’s an error, and you need to check for that and report the error. Or else enable PDO to throw exceptions on error.

Answer:

I was running into this problem due to having extra entries in the named parameter mapping array passed to PDO::Statement->execute()

$args=array (":x" => 17 );
$pdo->prepare("insert into foo (x) values (:x)");
$pdo->execute($args); // success
$args[':irrelevant']=23;
$pdo->execute($args) // throws exception with HY093

Answer:

Since you have made $i+1 in the loop so count($options) would equal the last $i+1 which makes a duplicate binding.Try

 foreach($options as $i => $option)
 { 
      $statement->bindValue($i + 1, $option->getId()); 
 }

 $statement->bindValue(count($options)+1, $city->getId());