Подготовленные запросы и хранимые процедуры
Большинство баз данных поддерживают концепцию подготовленных запросов. Что это такое? Это можно описать, как некий вид скомпилированного шаблона SQL запроса, который будет запускаться приложением и настраиваться с помощью входных параметров. У подготовленных запросов есть два главных преимущества:
- Запрос необходимо однажды подготовить и затем его можно запускать столько раз, сколько нужно, причём как с теми же, так и с отличающимися параметрами. Когда запрос подготовлен, СУБД анализирует его, компилирует и оптимизирует план его выполнения. В случае сложных запросов этот процесс может занимать ощутимое время и заметно замедлить работу приложения, если потребуется много раз выполнять запрос с разными параметрами. При использовании подготовленного запроса СУБД анализирует/компилирует/оптимизирует запрос любой сложности только один раз, а приложение запускает на выполнение уже подготовленный шаблон. Таким образом подготовленные запросы потребляют меньше ресурсов и работают быстрее.
- Параметры подготовленного запроса не требуется экранировать кавычками; драйвер это делает автоматически. Если в приложении используются исключительно подготовленные запросы, разработчик может быть уверен, что никаких SQL-инъекций случиться не может (однако, если другие части текста запроса создаются с неэкранированным вводом, то SQL инъекция по-прежнему возможна).
Подготовленные запросы также полезны тем, что PDO может эмулировать их, если драйвер базы данных не имеет подобной функциональности. Это значит, что приложение может пользоваться одной и той же методикой доступа к данным независимо от возможностей СУБД.
Пример #1 Повторяющиеся вставки в базу с использованием подготовленных запросов
В этом примере 2 раза выполняется INSERT запрос с разными значениями name и value , которые подставляются вместо соответствующих псевдопеременных:
$stmt = $dbh -> prepare ( «INSERT INTO REGISTRY (name, value) VALUES (:name, :value)» );
$stmt -> bindParam ( ‘:name’ , $name );
$stmt -> bindParam ( ‘:value’ , $value );
?php
// вставим одну строку
$name = ‘one’ ;
$value = 1 ;
$stmt -> execute ();
// теперь другую строку с другими значениями
$name = ‘two’ ;
$value = 2 ;
$stmt -> execute ();
?>
Пример #2 Повторяющиеся вставки в базу с использованием подготовленных запросов
В этом примере 2 раза выполняется INSERT запрос с разными значениями name и value , которые подставляются вместо псевдопеременных ? .
$stmt = $dbh -> prepare ( «INSERT INTO REGISTRY (name, value) VALUES (?, ?)» );
$stmt -> bindParam ( 1 , $name );
$stmt -> bindParam ( 2 , $value );
?php
// вставим одну строку
$name = ‘one’ ;
$value = 1 ;
$stmt -> execute ();
// теперь другую строку с другими значениями
$name = ‘two’ ;
$value = 2 ;
$stmt -> execute ();
?>
Пример #3 Выборка данных с использованием подготовленных запросов
В этом примере производится выборка из базы по ключу, который вводит пользователь через форму. Пользовательский ввод автоматически заключается в кавычки, поэтому нет риска SQL инъекции.
$stmt = $dbh -> prepare ( «SELECT * FROM REGISTRY where name = ?» );
$stmt -> execute ([ $_GET [ ‘name’ ]]);
foreach ( $stmt as $row ) print_r ( $row );
>
?>?php
Пример #4 Вызов хранимой процедуры с выходными параметрами
Если СУБД поддерживает выходные параметры, приложение может пользоваться ими также как и входными. Выходные параметры обычно используют для получения данных из хранимых процедур. Пользоваться выходными параметрами несколько сложнее, так как разработчику необходимо знать максимальный размер извлекаемых значений ещё на этапе задания этих параметров. Если извлекаемое значение окажется больше, чем предполагалось, будет вызвана ошибка.
$stmt = $dbh -> prepare ( «CALL sp_returns_string(?)» );
$stmt -> bindParam ( 1 , $return_value , PDO :: PARAM_STR , 4000 );
?php
// вызов хранимой процедуры
$stmt -> execute ();
print «процедура вернула $return_value \n» ;
?>
Пример #5 Вызов хранимой процедуры с входным/выходным параметром
Можно задать параметр одновременно входным и выходным; синтаксис при этом тот же, что и для выходных параметров. В следующем примере строка ‘привет’ передаётся в хранимую процедуру, а затем эта строка будет заменена возвращаемым значением.
$stmt = $dbh -> prepare ( «CALL sp_takes_string_returns_string(?)» );
$value = ‘привет’ ;
$stmt -> bindParam ( 1 , $value , PDO :: PARAM_STR | PDO :: PARAM_INPUT_OUTPUT , 4000 );
?php
// вызов хранимой процедуры
$stmt -> execute ();
print «процедура вернула $value \n» ;
?>
Пример #6 Неправильное использование псевдопеременной
$stmt = $dbh -> prepare ( «SELECT * FROM REGISTRY where name LIKE ‘%?%'» );
$stmt -> execute ([ $_GET [ ‘name’ ]]);
?php
// псевдопеременная может использоваться только в виде отдельного значения
$stmt = $dbh -> prepare ( «SELECT * FROM REGISTRY where name LIKE ?» );
$stmt -> execute ([ «% $_GET [ name ] %» ]);
?>
Php mssql хранимые процедуры
// Store procedure call without params
$MyConnection = new mysqli ( «DB_SERVER» , «DB_USER» , «DB_PASS» , «DB_NAME» );
mysqli_multi_query ( $MyConnection , «CALL MyStoreProcedure» ) OR DIE ( mysqli_error ( $MyConnection ));
while ( mysqli_more_results ( $MyConnection ))
if ( $result = mysqli_store_result ( $MyConnection ))
while ( $row = mysqli_fetch_assoc ( $result ))
// i.e.: DBTableFieldName=»userID»
echo «row keyword»>. $row [ «DBTableFieldName» ]. «
» ;
.
>
mysqli_free_result ( $result );
>
mysqli_next_result ( $conn );
// Store procedure call using params
$MyConnection = new mysqli ( «DB_SERVER» , «DB_USER» , «DB_PASS» , «DB_NAME» );
mysqli_query ( $MyConnection , «SET @p0='» . $MyParam1 . «‘» );
mysqli_query ( $MyConnection , «SET @p1='» . $MyParam2 . «‘» );
mysqli_multi_query ( $MyConnection , «CALL MyStoreProcedure (@p0,@p1)» ) OR DIE ( mysqli_error ( $MyConnection ));
while ( mysqli_more_results ( $MyConnection ))
if ( $result = mysqli_store_result ( $MyConnection ))
while ( $row = mysqli_fetch_assoc ( $result ))
// i.e.: DBTableFieldName=»userID»
echo «row keyword»>. $row [ «DBTableFieldName» ]. «
» ;
.
>
mysqli_free_result ( $result );
>
mysqli_next_result ( $conn );
/**
* Small function to facilitate call procedure with multiple arguments (supports in/inout/out)
*/
$db = new mysqli ( ‘localhost’ , ‘root’ , ‘password’ , ‘database’ );
?php
$lt_query = callProcedure (
$db ,
«stored_procedure» ,
array(
«in_param1» => «Value1» ,
«in_param2» => «Value2» ,
«inout_param3» => «Value3» ,
«out_param4» => «» ,
«out_param5» => «»
));
function callProcedure ( $po_db , $pv_proc , $pt_args )
if (empty( $pv_proc ) || empty( $pt_args ))
return false ;
>
$lv_call = «CALL ` $pv_proc `(» ;
$lv_select = «SELECT» ;
$lv_log = «» ;
foreach( $pt_args as $lv_key => $lv_value )
$lv_query = «SET @_ $lv_key = ‘ $lv_value ‘» ;
$lv_log .= $lv_query . «;\n» ;
if (! $lv_result = $po_db -> query ( $lv_query ))
/* Write log */
return false ;
>
$lv_call .= » @_ $lv_key ,» ;
$lv_select .= » @_ $lv_key AS $lv_key ,» ;
>
$lv_call = substr ( $lv_call , 0 , — 1 ). «)» ;
$lv_select = substr ( $lv_select , 0 , — 1 );
$lv_log .= $lv_call ;
if ( $lv_result = $po_db -> query ( $lv_call ))
if( $lo_result = $po_db -> query ( $lv_select ))
$lt_result = $lo_result -> fetch_assoc ();
$lo_result -> free ();
return $lt_result ;
>
/* Write log */
return false ;
>
/* Write log */
return false ;
>
/**
* This will return an array like this:
*
* $lt_query = array(
* ‘in_param1’ = ‘Value1’, // Same value as in call
* ‘in_param2’ = ‘Value2’, // Same value as in call
* ‘inout_param3’ = ?, // Value is changed accordingly
* ‘out_param4’ = ?, // Value is changed accordingly
* ‘out_param5’ = ? // Value is changed accordingly
* )
*/
?>
Совместная работа PHP и MSSQL ч.2
В предыдущей части в результате запроса к БД был получен sqlsrv объект типа SQL Server Statement resource. Для его обработки существует функция sqlsrv_fetch_array(). Первым ее параметром является объект-ресурс, полученный на предыдущем шаге, а вторым можно указать какой массив нам нужно получить — ассоциативный (SQLSRV_FETCH_ASSOC), не ассоциативный( SQLSRV_FETCH_NUMERIC) или оба сразу ( SQLSRV_FETCH_BOTH — стоит по умолчанию).
while( $row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC) )
В результате в переменной result получим что-то вроде такого:
array:1 [▼ 0 => array:3 [▼ "id" => 1 "name" => "a" "email" => "a@aa.aa" ] ]
Получился обычный ассоциативный массив, с которым можно дальше работать
Хранимые процедуры в sqlsrv
Хранимая процедура — объект базы данных, представляющий собой набор SQL-инструкций, который компилируется один раз и хранится на сервере. Это означает, что она может хранится отдельно на сервере и использоваться разными приложениями. Библиотека sqlsrv позволяет работать с такими процедурами.
Для выполнения хранимой процедуры используется та же функция sqlsrv_query(). В качестве второго параметра (запроса) ей необходимо передать следующую строку:
где $nameFunc — имя хранимой процедуры.
Представим, что у нас на сервере существует хранимая процедура get_users, которая возвращает всех пользователей. Для ее выполнения необходимо написать следующий код:
$nameFunc = 'get_users'; $sql = ""; $stmt = sqlsrv_query($this->conn, $sql); while( $row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC) )