SELECT * FROM в MySQLi
Мой сайт довольно обширен, и я недавно сделал переход на PHP5 (назовите меня поздним blloomer).
Все мои запросы MySQL были построены следующим образом:
"SELECT * FROM tablename WHERE field1 = 'value' && field2 = 'value2'";
Это сделало его очень простым, простым и дружелюбным.
Теперь я пытаюсь переключиться на mysqli по очевидным соображениям безопасности, и мне сложно понять, как реализовать те же запросы SELECT * FROM
, когда bind_param
требует определенных аргументов.
Является ли это утверждение ушедшим в прошлое?
Если это так, как мне обрабатывать запрос с количеством столбцов? Мне действительно нужно каждый раз набирать их?
Ответы
Ответ 1
"SELECT * FROM tablename WHERE field1 = 'value' && field2 = 'value2'";
становится
"SELECT * FROM tablename WHERE field1 = ? && field2 = ?";
который передается в $mysqli::prepare
:
$stmt = $mysqli->prepare(
"SELECT * FROM tablename WHERE field1 = ? && field2 = ?");
$stmt->bind_param( "ss", $value, $value2);
// "ss' is a format string, each "s" means string
$stmt->execute();
$stmt->bind_result($col1, $col2);
// then fetch and close the statement
Комментарии к OP:
поэтому, если у меня есть 5 параметров, я могу потенциально иметь "sssis" или что-то (в зависимости от типов входов?)
Вправо, один спецификатор типа для параметра ?
в подготовленном операторе, все из них позиционные (первый спецификатор применяется к первому ?
, который заменяется первым фактическим параметром (который является вторым параметром bind_param
)).
mysqli позаботится об ускорении и цитировании (я думаю).
Ответ 2
Это было уже месяц назад, но хорошо.
Я мог ошибаться, но для вашего вопроса я чувствую, что bind_param
на самом деле не проблема. Вы всегда должны определять некоторые условия, будь то непосредственно в самой строке запроса, использовать bind_param
для установки заполнителей ?
. Это не проблема.
Проблема, которую я использовал с помощью запросов MySQLi SELECT *
, - это часть bind_result
. То, где это становится интересным. Я наткнулся на этот пост от Джеффри Пути: http://jeff-way.com/2009/05/27/tricky-prepared-statements/ (Эта ссылка больше не активна). script в основном проходит через результаты и возвращает их как массив - не нужно знать, сколько столбцов есть, и вы все еще можете использовать подготовленные инструкции.
В этом случае он будет выглядеть примерно так:
$stmt = $mysqli->prepare(
'SELECT * FROM tablename WHERE field1 = ? AND field2 = ?');
$stmt->bind_param('ss', $value, $value2);
$stmt->execute();
Затем используйте фрагмент с сайта:
$meta = $stmt->result_metadata();
while ($field = $meta->fetch_field()) {
$parameters[] = &$row[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $parameters);
while ($stmt->fetch()) {
foreach($row as $key => $val) {
$x[$key] = $val;
}
$results[] = $x;
}
И $results
теперь содержит всю информацию из SELECT *
. Пока я нашел это идеальным решением.
Ответ 3
Пока вы переключаетесь, переключитесь на PDO вместо mysqli, он поможет вам написать код агносита базы данных и иметь лучшие функции для подготовленных операторов.
http://www.php.net/pdo
Bindparam для PDO:
http://se.php.net/manual/en/pdostatement.bindparam.php
$sth = $dbh->prepare("SELECT * FROM tablename WHERE field1 = :value1 && field2 = :value2");
$sth->bindParam(':value1', 'foo');
$sth->bindParam(':value2', 'bar');
$sth->execute();
или:
$sth = $dbh->prepare("SELECT * FROM tablename WHERE field1 = ? && field2 = ?");
$sth->bindParam(1, 'foo');
$sth->bindParam(2, 'bar');
$sth->execute();
или выполнить с параметрами как массив:
$sth = $dbh->prepare("SELECT * FROM tablename WHERE field1 = :value1 && field2 = :value2");
$sth->execute(array(':value1' => 'foo' , ':value2' => 'bar'));
Вам будет проще, если вы хотите, чтобы ваше приложение могло работать в разных базах данных в будущем.
Я также думаю, что вы должны потратить некоторое время на использование некоторых классов из Zend Framwework во время работы с PDO. Проверьте их Zend_Db и более конкретно [Zend_Db_Factory] [2]. Вам не нужно использовать всю фреймворк или конвертировать ваше приложение в шаблон MVC, но использование фреймворка и чтение на нем - это потраченное время.
Ответ 4
Является ли это утверждение ушедшим в прошлое?
Да. Не используйте SELECT *
; это кошмар для обслуживания. Есть много других потоков на SO о том, почему эта конструкция плоха, и как избежать этого поможет вам написать лучшие запросы.
См. также:
Ответ 5
Вы все еще можете использовать его (mysqli - это еще один способ связи с сервером, сам язык SQL расширяется, а не изменяется). Подготовленные утверждения более безопасны, поскольку - вам не нужно беспокоиться о том, чтобы каждый раз избегать ваших значений. Вы можете оставить их такими, какими они были, если вы хотите, но риск перекодировки sql уменьшается, если вы переключаетесь.
Ответ 6
Я искал хороший и полный пример того, как динамически связывать несколько параметров запроса с любыми запросами SELECT, INSERT, UPDATE и DELETE. Alec упоминает в своем ответе способ привязки результата, для меня функция get_result() после выполнения() для запросов SELECT работает отлично, и я могу получить все выбранные результаты в массив ассоциативных массивов.
В любом случае, я создал функцию, в которой я могу динамически связывать любое количество параметров с параметризованным запросом (используя функцию call_user_func_array) и получать результат выполнения запроса. Ниже приведена функция с ее документацией (перед ее использованием прочитайте ее перед тем, как ее использовать - особенно важно понять параметр $paremetersTypes - Тип спецификации символов)
/**
* Prepares and executes a parametrized QUERY (SELECT, INSERT, UPDATE, DELETE)
*
* @param[in] $dbConnection mysqli database connection to be used for query execution
* @param[in] $dbQuery parametrized query to be bind parameters for and then execute
* @param[in] $isDMQ boolean value, should be set to TRUE for (DELETE, INSERT, UPDATE - Data manipulaiton queries), FALSE for SELECT queries
* @param[in] $paremetersTypes String representation for input parametrs' types as per http://php.net/manual/en/mysqli-stmt.bind-param.php
* @param[in] $errorOut A variable to be passed by reference where a string representation of an error will be present if a FAUILURE occurs
* @param[in] $arrayOfParemetersToBind Parameters to be bind to the parametrized query, parameters need to be specified in an array in the correct order
* @return array of feched records associative arrays for SELECT query on SUCCESS, TRUE for INSERT, UPDATE, DELETE queries on SUCCESS, on FAIL sets the error and returns NULL
*/
function ExecuteMySQLParametrizedQuery($dbConnection, $dbQuery, $isDMQ, $paremetersTypes, &$errorOut, $arrayOfParemetersToBind)
{
$stmt = $dbConnection->prepare($dbQuery);
$outValue = NULL;
if ($stmt === FALSE)
$errorOut = 'Failed to prepare statement for query: ' . $dbQuery;
else if ( call_user_func_array(array($stmt, "bind_param"), array_merge(array($paremetersTypes), $arrayOfParemetersToBind)) === FALSE)
$errorOut = 'Failed to bind required parameters to query: ' . $dbQuery . ' , parameters :' . json_encode($arrayOfParemetersToBind);
else if (!$stmt->execute())
$errorOut = "Failed to execute query [$dbQuery] , erorr:" . $stmt->error;
else
{
if ($isDMQ)
$outValue = TRUE;
else
{
$result = $stmt->get_result();
if ($result === FALSE)
$errorOut = 'Failed to obtain result from statement for query ' . $dbQuery;
else
$outValue = $result->fetch_all(MYSQLI_ASSOC);
}
}
$stmt->close();
return $outValue;
}
использование:
$param1 = "128989";
$param2 = "some passcode";
$insertQuery = "INSERT INTO Cards (Serial, UserPin) VALUES (?, ?)";
$rowsInserted = ExecuteMySQLParametrizedQuery($dbConnection, $insertQuery, TRUE, 'ss', $errorOut, array(&$param1, &$param2) ); // Make sure the parameters in an array are passed by reference
if ($rowsInserted === NULL)
echo 'error ' . $errorOut;
else
echo "successfully inserted row";
$selectQuery = "SELECT CardID FROM Cards WHERE Serial like ? AND UserPin like ?";
$arrayOfCardIDs = ExecuteMySQLParametrizedQuery($dbConnection, $selectQuery, FALSE, 'ss', $errorOut, array(&$param1, &$param2) ); // Make sure the parameters in an array are passed by reference
if ($arrayOfCardIDs === NULL)
echo 'error ' . $errorOut;
else
{
echo 'obtained result array of ' . count($arrayOfCardIDs) . 'selected rows';
if (count($arrayOfCardIDs) > 0)
echo 'obtained card id = ' . $arrayOfCardIDs[0]['CardID'];
}
Ответ 7
вы можете использовать get_result()
в инструкции.
http://php.net/manual/en/mysqli-stmt.get-result.php