Обновление MySQL с использованием PDO и подготовленный оператор не работает

У меня возникла странная проблема с php PDO и mysql.

У меня есть следующая таблица:

create table test_table ( id integer, value text );

с одной строкой:

insert into test_table values (1, "asdf");

когда я пытаюсь обновить эту единственную строку с помощью подготовленного оператора, у меня появилось другое поведение в зависимости от используемого синтаксиса:

// connection to db (common code)
$dbh = new PDO("mysql:host=localhost;dbname=test", "myuser", "mypass");

=============================================== ==========

// WORKING
$q = 'update test_table set id=1, value='.rand(0,99999).' where id=1';
$dbh->exec($q);

=============================================== ==========

// WORKING
$q = 'update test_table set value=:value where id=:id';
$par = array(
    "id" => 1,
    "value" => rand(0,99999)
  );
$sth = $dbh->prepare($q, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute($par);

=============================================== ==========

// NOT WORKING
$q = 'update test_table set id=:id, value=:value where id=:id';
$par = array(
    "id" => 1,
    "value" => rand(0,99999)
  );
$sth = $dbh->prepare($q, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute($par);

В третьем случае на моем сервере обновление не выполняется в строке без каких-либо причин и исключений/ошибок. На другом сервере это работает. Я не ищу ответов вроде: "и так? Используйте первую или вторую реализацию":)

Я спрашиваю, почему третья реализация не работает, потому что я переношу много кода с сервера на другой (это не мой код), и он содержит много запросов как этот, и у меня нет времени исправлять их один за другим. На текущем сервере он работает, а на новом - нет.

Почему третья реализация не работает? Существует ли какая-либо конфигурация для php/pdo/mysql, которая может повлиять на это поведение?

Спасибо.

Update: Пытался вызывать сообщения об ошибке:

$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

try {
// NOT WORKING
  $q = 'update test_table set id=:id, value=:value where id=:id';
  $par = array(
    "id" => 1,
    "value" => rand(0,99999)
  );
  $sth = $dbh->prepare($q, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
  print_r($sth);
  print_r($dbh->errorInfo());
} catch(PDOException $e) {
  echo $e->getMessage();
}

$sth->execute($par);

Выполнение этого кода на обоих серверах (работающих и не работающих):

PDOStatement Object
(
    [queryString] => update test_table set id=:id, value=:value where id=:id
)
Array
(
    [0] => 00000
    [1] => 
    [2] => 
)

Обновление 2

Посмотрите на этот дополнительный тест:

create table test_table ( value0 text, value text );
insert into test_table values ("1", "pippo");

// NOT WORKING

$q = 'update test_table set value0=:value0, value=:value where value0=:value0';
$par = array(
    "value0" => "1",
    "value" => rand(0, 839273)
);

create table test_table ( value0 text, value text );
insert into test_table values ("pippo", "1");

// WORKING

$q = 'update test_table set value=:value, value0=:value0 where value=:value';
$par = array(
    "value" => "1",
    "value0" => rand(0, 839273)
);

Невероятно, не так ли? Мой подозреваемый теперь заключается в том, что существует некоторое специальное обновление beahaviour, специально сделанное для первого столбца каждой таблицы при обработке указателей PDO +.

Ответ 1

http://php.net/manual/en/pdo.prepare.php:

Вы должны включить уникальный маркер параметра для каждого значения, которое вы хотите войдите в оператор, когда вы вызываете PDOStatement:: execute(). Вы не может использовать один и тот же параметр маркера с таким же именем более одного раза в подготовленный оператор, если включен режим эмуляции.

Как это указывает, вероятная причина, по которой ваш код работает на одном сервере, а не другой, заключается в том, что PDO::ATTR_EMULATE_PREPARES отключен на сервере, на котором сбой кода. Как сказано в документации, этот атрибут эффективно устраняет ограничение, препятствующее одновременному использованию маркера параметра с тем же именем (наряду с некоторыми другими ограничениями).

Ответ 2

try {
     $db = new PDO('mysql:host=localhost;dbname=vendor_management_system', 'root', '');
     $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
     $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
}catch(PDOException $e) {
    echo 'ERROR: ' . $e->getMessage();
}




$fields[] = 'car_name';
$fields[] = 'model_no';
$fields[] = 'maker_id';
$fields[] = 'dealer_id';

$values[] = "testcar";
$values[] = "no#1";
$values[] = 2;
$values[] = 4;



echo SQLUpdate('car_details', $fields, $values,'car_id = 32 and car_name = "testname"',$db);




//START: SQLUpdate
//$fields = array of fields in DB
//$values = array of values respective to the $fields
 function SQLUpdate($table,$fields,$values,$where,$db) {



  //build the field to value correlation
  $buildSQL = '';
  if (is_array($fields)) {

        //loop through all the fields and assign them to the correlating $values
        foreach($fields as $key => $field) :
      if ($key == 0) {
            //first item
            $buildSQL .= $field.' = ?';
          } else {
            //every other item follows with a ","
            $buildSQL .= ', '.$field.' = ?';
          }
    endforeach;

  } else {
    //we are only updating one field
        $buildSQL .= $fields.' = :value';
  }

  $prepareUpdate = $db->prepare('UPDATE '.$table.' SET '.$buildSQL.'
WHERE '.$where);

  //execute the update for one or many values
  if (is_array($values)) {
    $affected_rows=$prepareUpdate->execute($values);
    return $affected_rows;
  } else {
    $affected_rows=$prepareUpdate->execute(array(':value' => $values));
    return $affected_rows;
  }


  //record and print any DB error that may be given
  $error = $prepareUpdate->errorInfo();
  if ($error[1]) print_r($error);

} 
//END: SQLUpdate

Ответ 3

$maker_id=1;
$stmt = $db->prepare("UPDATE car_details SET maker_id=?");
$affected_rows=$stmt->execute(array($maker_id));
echo $affected_rows.' were affected';