PDO PHP bindValue не работает

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

Это работает:

$sql = 'SELECT a.eventCode, a.eventTime, a.teamCode, a.playerCode, b.lastName, b.firstName, b.number, a.xCoord, a.yCoord, a.id ';
$sql = $sql . 'FROM events a, players b ';
$sql = $sql . 'WHERE a.regGUID in ( ' . $regGUID . ' ) and ';
$sql = $sql . 'a.playerCode=b.playerCode and a.gameCode = "' . $game . '" order by a.eventTime desc, a.actionCode asc'; 
$stmt = $db->prepare($sql);
$results = $stmt->execute();

Это не:

$sql = 'SELECT a.eventCode, a.eventTime, a.teamCode, a.playerCode, b.lastName, b.firstName, b.number, a.xCoord, a.yCoord, a.id ';
$sql = $sql . 'FROM events a, players b ';
$sql = $sql . 'WHERE a.regGUID in ( :regGUID ) and ';
$sql = $sql . 'a.playerCode=b.playerCode and a.gameCode = :game order by a.eventTime desc, a.actionCode asc'; 
$stmt = $db->prepare($sql);
$stmt->bindValue(':regGUID', $regGUID, PDO::PARAM_STR);
$stmt->bindValue(':game', $game, PDO::PARAM_STR);
$results = $stmt->execute();

Что мне не хватает? Спасибо

4 ответа

Решение

Проблема здесь:

$sql = $sql . 'WHERE a.regGUID in ( :regGUID ) and ';
$stmt->bindValue(':regGUID', $regGUID, PDO::PARAM_STR);

Я предполагаю, что $regGUID - это список строк в кавычках, разделенных запятыми.

Каждый параметр запроса принимает только одно скалярное значение. Не списки значений.

Итак, у вас есть два варианта:

  1. Продолжайте интерполировать строку $regGUID, даже если вы используете параметры для других скалярных значений. Но вы все равно должны быть осторожны, чтобы избежать внедрения SQL, поэтому вы должны правильно сформировать строку $regGUID. Вы не можете просто вызвать PDO::quote() для всей строки, это сделало бы ее единственной строкой в ​​кавычках, содержащей UUID и запятые. Вы должны убедиться, что каждая строка UUID экранирована и заключена в кавычки по отдельности, затем объединить список вместе и интерполировать его в предложение IN.

    $regGUIDs = explode(',', $regGUID);
    $regGUIDs = array_map(function ($g) { return $db->quote($g); }, $regGUIDs);
    $regGUID = implode(',', $regGUIDs);
    $sql = $sql . 'WHERE a.regGUID in (' . $regGUID . ') and ';
    
  2. explode() $regGUID в массив и добавить один параметр запроса для каждого элемента в массиве. Интерполировать динамический список заполнителей параметров запроса.

    $regGUIDs = explode(',', $regGUID);
    $params = array_fill(1, count($regGUIDs), '?');
    $sql = $sql . ' WHERE a.regGUID in ( ' . implode(',', $params) . ' ) and ';
    

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

Вместо использования bindValue() я просто передаю массив значений параметров в PDOStatement::execute(), что намного проще.

$paramValues = $regGUIDs;
$paramValues[] = $game;
$results = $stmt->execute($paramValues);

Об этом действительно спрашивали 1000 раз.

Подготовленные операторы могут принимать только скалярные значения, а не произвольные части запроса SQL.

Вы должны сформировать оператор IN(), используя столько заполнителей, сколько элементов нужно вставить, а затем связать их один за другим.

Чтобы облегчить эту задачу, можно использовать некоторую вспомогательную функцию.

Скажем, используя библиотеку SafeMysql, этот код можно записать как

$sql  = 'SELECT * FROM events a, players b WHERE regGUID in (?a) and';
$sql .= ' a.playerCode=b.playerCode and a.gameCode = ?s';
$sql .= ' order by a.eventTime desc, a.actionCode asc'; 
$results = $db->getAll($sql,$regGUID,$game);

Обратите внимание, что $regGUID должен быть массив, а не строка и $results уже содержат все запрошенные данные, без дальнейшей обработки.

Каково содержание $regGUID? Так как вы используете in пункт, я подозреваю, список через запятую.

Привязка переменной к параметру не похожа на подстановку этой строки в запрос; это все равно что говорить MySQL, как использовать ваши фактические переменные PHP. Так что если вы связываете строку как '1,2,3' для параметра запроса он остается одной строкой и не интерпретируется как список чисел.

Следовательно, если $regGUID это что-то вроде "'AAA1', 'BBB2'"ваш первый запрос становится

... WHERE a.regGUID in ( 'AAA1', 'BBB2' ) ...

но ваш второй запрос больше похож

... WHERE a.regGUID in ( '\'AAA1\', \'BBB2\'' ) ...

что то же самое, что сказать

... WHERE a.regGUID = '\'AAA1\', \'BBB2\'' ...

Поскольку другие имеют состояние, вы можете привязать только одно скалярное значение к заполнителю. Так что это означает, что вам на самом деле нужен заполнитель для каждого значения в вашем IN заявление. Я обычно делаю что-то вроде следующего. Следует отметить, что я никогда не использую bindValue так что если у него есть правила о том, что ссылки должны быть такими, как Mysqli, то может потребоваться изменить приведенное ниже:

$regGUIDPlaceholders = array();

// prepare the placeholders
// assume regGUID is an array - if its a string then explode on whatever to make it an array
foreach($regGUID as $k => $v) {
   $placeholder = ':regGUID' . $k;
   $regGUIDPlaceholders[$key] = $value;
}

// prepare the IN statememnt
$in = sprintf('IN (%s)', implode(',', array_keys($regGUIDPlaceholders)));

$sql = 'SELECT a.eventCode, a.eventTime, a.teamCode, a.playerCode, b.lastName, b.firstName, b.number, a.xCoord, a.yCoord, a.id ';
$sql = $sql . 'FROM events a, players b ';

// USE the IN statement dynamically prepared above
$sql = $sql . 'WHERE a.regGUID '. $in . ' and ';

$sql = $sql . 'a.playerCode=b.playerCode and a.gameCode = :game order by a.eventTime desc, a.actionCode asc'; 

$stmt = $db->prepare($sql);

// bind each GUID to its placeholder
foreach($regGUIDPlaceholders as $placeholder => $value) {
   $stmt->bindValue($placeholder, $value, PDO::PARAM_STR);
}

$stmt->bindValue(':game', $game, PDO::PARAM_STR);
$results = $stmt->execute();
Другие вопросы по тегам