Используя bind_param
во всех моих запросах, теперь я хочу использовать IN(?)
Где количество элементов в списке может меняться.
Функция SQLout, которую я использую здесь, в основном выполняет $sql_db->prepare
, ->bind_param
, ->execute()
, ->store_result()
, ->bind_result
// the code below does not work as the query only matches on element 'a': $locations = ('a','b','c','d','e'); SQLout ("SELECT Name FROM Users WHERE Locations IN (?)", array('s', $locations), array(&$usrName)); // the code below does work as a brute-force method, // but is not a viable solution as I can't anticipate the number of elements in $locations going forward: SQLout ("SELECT Name FROM Users WHERE Locations IN (?,?,?,?,?)", array('sssss', $locations[0],$locations[1],$locations[2],$locations[3],$locations[4]), array(&$usrName));
Кто-нибудь придумал более элегантное решение?
Это одно место, заполнившее их лица. Минус автоэксплуатации – это почти буквально операция замены строк внутри, что означает, что если у вас есть WHERE x IN (?)
И перейдите в 1,2,3,4
, вы получите эквивалент
WHERE x IN ('1,2,3,4') // note, it's a string, not individual comma-separated ints
логически эквивалентно
WHERE x = '1,2,3,4' // again, just a string
является более ожидаемым
WHERE (x = 1) or (x = 2) or (x = 3) or (x = 4)
Единственное практическое решение – создать собственный список ?
, например:
$placeholders = implode(array_fill(0, count($values), '?')); $sql = "SELECT ... WHERE x IN ($placeholders)";
а затем привязать параметры, как правило.
Как сказал Хазмат, вам нужно создать параметры, а затем передать их, вызвав call_user_func_array
в подготовленном операторе, но немного ближе к рабочему коду, чем его пример 🙂
//In the calling code $queryString = "SELECT Name FROM Users WHERE Locations IN ("; $queryString .= getWhereIn($locations); $queryString .= " )"; $parametersArray = array(); foreach($locations as $location){ $parameter = array(); $parameter[0] = 's'; //It's a string $parameter[1] = $location; $parametersArray[] = $parameter; } //This is a function in a class that wraps around the class mysqli_statement function bindParameterArray($parameterArray){ $typesString = ''; $parameterValuesArray = array(); foreach($parameterArray as $parameterAndType){ $typesString .= $parameterAndType[0]; $parameterValuesArray[] = $parameterAndType[1]; } $finalParamArray = array($typesString); $finalParamArray = array_merge($finalParamArray, $parametersArray); call_user_func_array(array($this->statement, "bind_param"), $finalParamArray); } function getWhereIn($inArray){ $string = ""; $separator = ""; for($x=0 ; $x<count($inArray) ; $x++){ $string .= $separator."?"; $separator = ", "; } return $string; }
Вы можете «построить» в разделе IN
, прежде чем подготовить / связать его.
$sql = 'SELECT Name FROM Users WHERE Locations IN (' . implode(array_fill(0, count($locations), '?')) . ')';
Затем вы можете использовать call_user_func_array
для привязки параметров, не зная сколько их там.
// Parameters for SQLOut $params = array( # The SQL Query $sql, # The params for bind_param array(str_repeat('s', count($locations))), # The params for bind_result array(&$usrName) ); // Add the locations into the parameter list foreach($locations as &$loc){ // not sure if this is needed, but bind_param // expects its parameters to be references $params[1][] = &$loc; } // Call your function call_user_func_array('SQLout', $params);
Примечание. Это не проверено
IN обычно медленный и не подготовленный к заявлению дружественный. Лучшее решение – построить таблицу элементов, которые будут в IN, и использовать JOIN, чтобы получить тот же эффект.
Кто-нибудь придумал более элегантное решение?
Конечно. Я.
Mysqli практически непригодным для использования с подготовленными заявлениями, особенно с такими сложными случаями.
Таким образом, лучше избавиться от подготовленных заявлений и реализовать свои собственные заполнители с поддержкой всех случаев реальной жизни, с которыми может встретиться разработчик.
Таким образом, у safeMysql есть решение, которое вы ищете (а также решения для дюжины других головных болей).
В вашем конкретном случае это будет так же просто, как эта единственная строка кода
// the code works all right: $locations=array('a','b','c','d','e'); $usrName=$db->getCol("SELECT Name FROM Users WHERE Locations IN (?a)",$locations);
в отличие от уродливых кодов вы можете играть с некоторыми функциями PHP и API (и по-прежнему получать тревожные предупреждения зависит от версии PHP, которую вы используете в данный момент), этот код является аккуратным и читаемым . Это важно. Вы можете сказать, что делает этот код даже после года.