Подготовленные MySQL операторы со списком переменных переменных размеров - PullRequest
21 голосов
/ 29 ноября 2008

Как бы вы написали подготовленный оператор MySQL на PHP, который каждый раз принимает разное количество аргументов? Пример такого запроса:

SELECT `age`, `name` FROM `people` WHERE id IN (12, 45, 65, 33)

Предложение IN будет иметь разное число id s при каждом запуске.

У меня есть два возможных решения, но я хочу посмотреть, есть ли лучший способ.

Возможное решение 1 Сделайте так, чтобы оператор принимал 100 переменных, а остальные заполняли фиктивными значениями, которых гарантированно не будет в таблице; сделать несколько звонков для более чем 100 значений.

Возможное решение 2 Не используйте подготовленное утверждение; Построить и выполнить строгий контроль запросов на предмет возможных атак с использованием инъекций.

Ответы [ 7 ]

20 голосов
/ 29 ноября 2008

Я могу придумать пару решений.

Одним из решений может быть создание временной таблицы. Сделайте вставку в таблицу для каждого параметра, который вы бы указали в предложении in. Затем выполните простое соединение со своей временной таблицей.

Другим методом может быть что-то подобное.

$dbh=new PDO($dbConnect, $dbUser, $dbPass);
$parms=array(12, 45, 65, 33);
$parmcount=count($parms);   // = 4
$inclause=implode(',',array_fill(0,$parmcount,'?')); // = ?,?,?,?
$sql='SELECT age, name FROM people WHERE id IN (%s)';
$preparesql=sprintf($sql,$inclause);  // = example statement used in the question
$st=$dbh->prepare($preparesql);
$st->execute($parms);

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


Чтобы порадовать @orrd здесь краткая версия.

$dbh=new PDO($dbConnect, $dbUser, $dbPass);
$parms=array(12, 45, 65, 33);
$st=$dbh->prepare(sprintf('SELECT age, name FROM people WHERE id IN (%s)',
                          implode(',',array_fill(0,count($parms),'?'))));
$st->execute($parms);

10 голосов
/ 10 июня 2012

Существует также функция FIND_IN_SET , второй параметр которой представляет собой строку значений, разделенных запятыми:

SELECT age, name FROM people WHERE FIND_IN_SET(id, '12,45,65,33')
4 голосов
/ 29 ноября 2008

Приличные упаковщики sql поддерживают привязку к значениям массива. т.е.

$sql = "... WHERE id IN (?)";
$values = array(1, 2, 3, 4);
$result = $dbw -> prepare ($sql, $values) -> execute ();
2 голосов
/ 18 января 2009

я получил ответ от: http://bugs.php.net/bug.php?id=43568
это мое рабочее решение моей проблемы. Теперь я могу динамически использовать столько параметров, сколько захочу. Они будут такими же, как у меня в массиве, или как в этом случае я передаю идентификаторы из последнего запроса (который нашел все идентификаторы, где email = 'johndoe@gmail.com') динамическому запросу, чтобы получить все информация о каждом из них, независимо от того, сколько мне нужно.

<?php $NumofIds = 2; //this is the number of ids i got from the last query
    $parameters=implode(',',array_fill(0,$NumofIds,'?')); 
    // = ?,? the same number of ?'s as ids we are looking for<br />
    $paramtype=implode('',array_fill(0,$NumofIds,'i')); // = ii<br/>
    //make the array to build the bind_param function<br/>
    $idAr[] = $paramtype; //'ii' or how ever many ?'s we have<br/>
    while($statement->fetch()){ //this is my last query i am getting the id out of<br/>
        $idAr[] = $id;  
    }

    //now this array looks like this array:<br/>
    //$idAr = array('ii', 128, 237);

    $query = "SELECT id,studentid,book_title,date FROM contracts WHERE studentid IN ($parameters)";
    $statement = $db->prepare($query);
    //build the bind_param function
    call_user_func_array (array($statement, "bind_param"), $idAr);
    //here is what we used to do before making it dynamic
    //statement->bind_param($paramtype,$v1,$v2);
    $statement->execute();
?>
2 голосов
/ 29 ноября 2008

Пожалуйста, возьмите № 2 со стола. Подготовленные операторы - единственный способ защитить себя от внедрения SQL.

Однако вы можете генерировать динамический набор переменных привязки. не зарабатывайте 100, если вам нужно 7 (или 103).

1 голос
/ 29 ноября 2008

Если вы используете только целочисленные значения в предложении IN, нет ничего, что могло бы поспорить с динамическим построением вашего запроса без использования параметров SQL.

function convertToInt(&$value, $key)
{
    $value = intval($value);
}

$ids = array('12', '45', '65', '33');
array_walk($ids, 'convertToInt');
$sql = 'SELECT age, name FROM people WHERE id IN (' . implode(', ', $ids) . ')';
// $sql will contain  SELECT age, name FROM people WHERE id IN (12, 45, 65, 33)

Но, без сомнения, решение здесь является более общим подходом к этой проблеме.

0 голосов
/ 17 января 2014

У меня была похожая проблема сегодня, и я нашел эту тему. Посмотрев на ответы и покопавшись в гугле, я нашел симпатичное решение.

Хотя моя проблема немного сложнее. Потому что у меня фиксированные значения привязки и динамические тоже .

Это решение.

$params = array()
$all_ids = $this->get_all_ids();

for($i = 0; $i <= sizeof($all_ids) - 1; $i++){
    array_push($params, $all_ids[$i]['id']);
}

$clause = implode(',', array_fill(0, count($params), '?')); // output ?, ?, ?
$total_i = implode('', array_fill(0, count($params), 'i')); // output iiii

$types = "ss" . $total_i; // will reproduce : ssiiii ..etc

// %% it's necessary because of sprintf function
$query = $db->prepare(sprintf("SELECT * 
                                FROM clients    
                                WHERE name LIKE CONCAT('%%', ?, '%%') 
                                AND IFNULL(description, '') LIKE CONCAT('%%', ?, '%%')
                                AND id IN (%s)", $clause));

$thearray = array($name, $description);
$merge    = array_merge($thearray, $params); // output: "John", "Cool guy!", 1, 2, 3, 4

// We need  to pass variables instead of values by reference
// So we need a function to that
call_user_func_array('mysqli_stmt_bind_param', array_merge (array($query, $types), $this->makeValuesReferenced($merge))); 

И функция makeValuesreferenced :

public function makeValuesReferenced($arr){
    $refs = array();
    foreach($arr as $key => $value)
        $refs[$key] = &$arr[$key];
    return $refs;
}

Ссылки для получения этого ноу-хау: https://bugs.php.net/bug.php?id=49946, PHP добавляет один массив к другому (не array_push или +) , [PHP]: Ошибка -> Слишком мало аргументы в sprintf (); , http://no2.php.net/manual/en/mysqli-stmt.bind-param.php#89171, Проблема передачи по ссылке с PHP 5.3.1

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...