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

5

8

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;
  }
 }
Intyre answered 26/4, 2010 at 12:59 Comment(0)
B
4

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++
}
Bunt answered 26/4, 2010 at 16:12 Comment(0)
T
3

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);
Tenderfoot answered 19/6, 2013 at 9:12 Comment(0)
R
0

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.

Racine answered 19/7, 2013 at 17:51 Comment(0)
A
0

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
Assize answered 4/11, 2013 at 23:28 Comment(1)
I dont understand if this is an answer or a just a comment on the OP. Could you rephrase please?Clarisclarisa
S
-2

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()); 
Scrod answered 26/4, 2010 at 13:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.