MySQL хранимой процедуры против функции, которую я буду использовать, когда? Введение в хранимые процедуры MySQL

Представляем вашему вниманию новый курс от команды The Codeby - "Тестирование Веб-Приложений на проникновение с нуля". Общая теория, подготовка рабочего окружения, пассивный фаззинг и фингерпринт, Активный фаззинг, Уязвимости, Пост-эксплуатация, Инструментальные средства, Social Engeneering и многое другое.


Хранимые процедуры MySQL (часть 1)

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

Создание хранимой процедуры

Открываем phpmyadmin. Выбираем базу данных mytest и нажимаем на её заголовок, либо на значок Browse. Затем переходим на вкладку Routines и создаём новую процедуру, нажав на надпись Add routine.

Появится форма, которую необходимо заполнить.

Routine Name (имя процедуры/функции) ReplyToComment .

Type (тип) — процедура. Отличие процедуры от функции в том, что функция всегда возвращает какое-то значение и содержит оператор return.

Parameters (параметры) наша процедура будет принимать два параметра: текст ответа и id комментария, на который мы отвечаем. Оба параметра будут передаваться из нашего клиентского приложения.

Создание параметров процедуры

Создадим первый параметр

Direction указываем направление параметра (IN, OUT, INOUT). В нашей процедуре оба передаваемых параметра будут входящими (IN).

Name (имя параметра) Content .

Type (тип) INT, VARCHAR, DATETIME и так далее. Параметр Content содержит текст ответа, который будет храниться в столбце comment_content. Данная колонка имеет определенный тип, чтобы его определить, открываем таблицу wp_comments и переходим на вкладку Structure, находим нужное нам имя столбца и смотрим на его тип в колонке Type. В данном примере колонка имеет тип - text, этот же тип нужно указать для нашего параметра.

Length/Values (длина или значение) для типа Text данное поле установить нельзя, но обычно здесь указывается длина, например VARCHAR(20), INT(10), либо какое-то значение по умолчанию.

Options в качестве дополнительных опций можно указать текущую кодировку столбца, её так же можно посмотреть на вкладке Structure колонка Collation. Установим значение utf8.

результат

Добавим второй параметр, нажав на кнопку Add parameter.

Direction - IN Name - ComID Type - BIGINT Length/Values - 20 Options - UNSIGNED

Оба параметра созданы, продолжаем заполнять форму.

Definition здесь мы описываем тело процедуры. Тело представляет собой блок, который начинается с ключевого слова BEGIN и заканчиваются ключевым словом END. Внутри тела процедуры можно размещать текст запроса, объявлять переменные, использовать конструкции ветвления, циклы и многое другое, как в любом языке программирования.

тело процедуры

Для начала создадим блок начала и конца тела нашей процедуры.

BEGIN END;

Теперь добавим текст запроса, который будет заполнять поля в таблице wp-comments, при добавлении нового комментария (ответа).

BEGIN INSERT INTO wp_comments (comment_author, comment_author_email, comment_content, comment_date, comment_date_gmt, comment_post_id, comment_parent, comment_approved, user_id) VALUES (подставляемые значения); END;

Хранить подставляемые значения будем в переменных. Для создания переменной используется ключевое слово DECLARE затем указывается имя, тип и длина переменной, так же можно указать значение по умолчанию. Если у переменной есть параметр DEFAULT, то такая переменная является инициализированной.

DECLARE имя тип (длина) DEFAULT значение по умолчанию;

Так же можно установить значение, для любой переменной используя, оператор SET.

SET имя переменной = значение;

И так создадим три переменных: Author, Email, UsedID, которые будут хранить значения для колонок: comment_author, comment_author_email, user_id.

BEGIN DECLARE Author tinytext DEFAULT "admin"; DECLARE UserID bigint(20) DEFAULT 1; -- Объявили переменную Email DECLARE Email varchar(100); -- Установили значение переменной Email SET Email = "[email protected]"; END;

comment_content данная колонка хранит текст комментария, который передается в процедуру в виде входного параметра Cоntent. Мы не будем создавать отдельную переменную, а просто подставим в VALUES значение входного параметра.

comment_date и comment_date_gmt обе колонки при первом заполнение будут иметь одинаковые значения. Создадим переменную Date и присвоим ей в качестве значения результат, который будет возвращать встроенная функция NOW. Данная функция возвращает текущую дату и время в формате DATETIME.

DECLARE MyCurDate DATETIME DEFAULT NOW();

DECLARE MyCurDate DATETIME; SET MyCurDate = NOW();

comment_approved Одобрен ли комментарий, 1 (да) иначе 0. Создадим переменную Approved, но перед установкой значения сделаем небольшую проверку.

DECLARE Approved varchar(20); IF Author = "admin" THEN SET Approved = 1; ELSE SET Approved = 0; END IF;

comment_parent здесь в качестве значения нужно указать id комментария, на который мы отвечаем. ID передается в процедуру как второй входной параметр. Создадим переменную ParentCom и присвоем ей значение переданного параметра.

DECLARE ParentCom varchar(20); SET ParentCom = ComID ;

Остался последний параметр comment_post_id здесь нужно указать id поста, в котором будет размещён наш комментарий. Объявим переменную с именем PostID.

DECLARE PostID BIGINT(20);

на данный момент тело процедуры должно выглядеть так

BEGIN -- блок объявления переменных DECLARE Author tinytext DEFAULT "admin"; DECLARE UserID bigint(20) DEFAULT 1; DECLARE Email varchar(100); DECLARE Date DATETIME DEFAULT NOW(); DECLARE ParentCom varchar(20); DECLARE Approved varchar(20); DECLARE PostID BIGINT(20); -- Установка значений переменных IF Author = "admin" THEN SET Approved = 1; ELSE SET Approved = 0; END IF; SET Email = "[email protected]"; SET ParentCom = ComID ; -- запрос INSERT INTO wp_comments (comment_author, comment_author_email, comment_content, comment_date, comment_date_gmt, comment_post_id, comment_parent, comment_approved, user_id) VALUES (Author, Email, Content, Date, Date, PostID, ParentCom, Approved, UserID); END;

От автора: чего это вы спите на рабочем месте! Вы не спите, а ждете, пока СУБД выполнит запрос? Так ее нужно разогнать. Хранимые процедуры MySQL применяли? Не знаете как? Ну, тогда просыпайтесь, потому что сейчас мы будем рассматривать как раз эту тему.

Что за процедуры еще?

Если у вас фобия насчет медицинских процедур, то данные структуры «не из той темы». Так что можно не бояться. А если серьезно, то хранимые процедуры – это удобная и полезная для «здоровья» СУБД вещь. Их еще называют «хранимыми функциями MySQL», но это не совсем точное определение. Хотя давайте разбираться со всем по порядку.

Хранимые процедуры позволяют значительно оптимизировать работу сервера и повысить скорость его работы, поскольку их код после первого выполнения сохраняется в кэше оперативки. При всех последующих вызовах процедура будет извлекаться из кэша, а не отправляться снова на выполнение.

В MySQL вызов хранимой процедуры означает, что вписанные в ее код запросы обработаются лишь наполовину. И то, если будет изменены значения их параметров. Но не все так идеально. Опишем сначала положительные стороны использования процедур:

Инкапсуляция функциональности – весь код хранится в одном месте, что облегчает доступ к нему для других приложений. В этом хранимые процедуры схожи с программными функциями.

Изоляция доступа к данным – все пользователи получают доступ не к строкам таблиц, а только к хранимым процедурам. Что в свою очередь повышает уровень защищенности всех данных.

Повышение скорости работы сервера – за счет кэширования и объединения запросов.

В MySQL хранимые процедуры в теории являются структурами, относящимся к более «высоким материям» — программированию СУБД. Так что мы с вами (как профессионалы) хоть потихоньку, но . Но вернемся к процедурам, и опишем негативные стороны их использования:

Нагрузка на сервер БД повышается – большая часть кода процедур выполняется на стороне сервера. Данная СУБД построена по модели «клиент-сервер», в которой задействованы несколько устройств.

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

Усложняется процесс переноса БД на другие рельсы (СУБД).

Процедуры в phpMyAdmin

Для начала рассмотрим применение в MySQL хранимых процедур на примере phpMyAdmin. Таким образом нам будет легче разобраться с этим типом структур. Стартуем!

Запускаем программную оболочку, справа выбираем тестовую БД. У меня таковой является база world. Затем в главном меню сверху переходим по вкладке «Процедуры». Здесь жмем на «Добавить процедуру».

После этого появится окно диалога «Добавить процедуру». Заполняем в ней все указанные на снимке поля. Указываем имя процедуры, тип. В графе «Определение» вводим учетную запись пользователя, и в комментариях (необязательно) указываем для себя, что это всего лишь пример хранимой процедуры.

Уже на этом этапе мы знакомимся с особенностями синтаксиса создания хранимых процедур MySQL. В поле «Определение» прописываем тело структуры. Обратите внимание, что выполняемый запрос находится между ключевыми словами BEGIN и END:

BEGIN SELECT "HELLO, WORD!"; END

BEGIN

SELECT "HELLO, WORD!" ;

Данный запрос не выполняет никаких действий с базой, а лишь выводит надпись. Это мы указали в поле «Доступ к SQL данным».

Для окончания создания нашей первой процедуры жмем внизу «Ок». После этого программа выводит «зелененькое» сообщение об удачном выполнении запроса. Его код представлен ниже. В MySQL хранимые процедуры и функции создаются с помощью специальной команды CREATE PROCEDURE. Но об этом позже.

Теперь запустим созданную структуру на выполнение. Для этого в разделе «Процедуры» нажимаем ссылку «Выполнить». Но что это за безобразие! Куда делся наш любимый «зелененький»? Почему программа «ругается» и «кричит», что ей не хватает выделенной памяти?

Куда смотрел автор данной публикации…! Извините, немного запутался. Ведь автор – это я . Спокойствие, сейчас все исправим! Такая ошибка возникает из-за того, что в главном конфигурационном файле значение параметра thread_stack оставлено без изменений. По умолчанию для каждого потока выделяется 128 Kb. Выделенного лимита оперативки для выполнения простых запросов вполне хватает, но для процедур мало.

Это еще раз доказывает, что на выполнение триггеров и хранимых процедур в MySQL тратится больше ресурсов.

Перейдите в конфигурационный файл my.ini, и увеличьте лимит оперативки, установленной для каждого потока, до 256 kb. Теперь еще раз запустите на выполнение созданную процедуру. В этот раз все прошло как надо, и программа вернула результат без ошибки.

Обратите внимание, что для вызова используется команда CALL с указанием имени процедуры и принимаемых параметров (в скобках).

Более сложный пример

Но все же phpMyAdmin по своим возможностям больше подходит для быстрого составления процедур. А для разработки в MySQL хранимой процедуры с динамическим количеством аргументов (например) потребуется более удобное ПО. Почему:

phpMyAdmin не хочет нормально «понимать» процедуры, созданные не через специальный конструктор.

Программа не выполняет структуры, запущенные под root и пустым паролем, а в Денвере создать нового пользователя и зайти под ним в phpMyAdmin целая проблема.

Если вы внимательно следите за моими публикациями и выполняете все прописанные в них «пожелания», то у вас должен уже стоять MySQL Administrator. В привязку к нему осталось скачать MySQL Query Browser по этой ссылке. Эти две программы лучше использовать вместе: в первой создавать процедуры, а в другой тестировать их. Поехали:

Слева вверху переходим через вкладку «Catalog».

Выбираем нужную БД, и в верхнем меню жмем на «Stored procedures», а внизу на «Create Stored Proc»

В появившемся окне редактора вводим код процедуры и жмем «Execute SQL».

CREATE DEFINER=`roman`@`localhost` PROCEDURE `proc5`() BEGIN declare a int; set a="SELECT COUNT(*) FROM city as a"; if(a > 1000)THEN SELECT "<1000"; ELSE SELECT ">1000"; END IF; END

CREATE DEFINER = ` roman ` @ ` localhost ` PROCEDURE ` proc5 ` ()

BEGIN

declare a int ;

set a = "SELECT COUNT(*) FROM city as a" ;

if (a > 1000 ) THEN

SELECT "<1000" ;

ELSE

SELECT ">1000" ;

END IF ;

Вы, наверное, заметили, что мы не передаем никаких значений внутрь процедуры. Кроме этого в MySQL может быть неизвестное количество параметров в хранимой процедуре, которые затем можно передать через новые объявления переменных, расположенные внутри циклов.

Для запуска процедуры заходим в MySQL Query Browser. Сначала вводим свою учетку и пароль, а затем слева в «Object Explorer» находим папку с нужной базой. Остальная очередность действия показана на следующем снимке.

Запуск процедуры в PHP

Теперь рассмотрим, как происходит в PHP вызов хранимой процедуры MySQL. Для этого нам придется немного «перекроить» код нашего предыдущего примера. Мы добавим в процедуру параметр на вывод, а также изменим код запроса:

CREATE DEFINER=`root`@`localhost` PROCEDURE `proc6`(out col decimal) BEGIN SELECT COUNT(*) into col FROM city; END

CREATE DEFINER = ` root ` @ ` localhost ` PROCEDURE ` proc6 ` (out col decimal )

BEGIN

SELECT COUNT (* ) into col FROM city ;

Для вызова из файла PHP процедуры и вывода результата будем использовать возможности класса PDOStatement, созданного специально для работы с БД через SQL

Этот класс реализован сравнительно недавно, и поддерживается PHP, начиная с версии 5.1.0. Советую перед использованием проверить используемую версию языка c помощью встроенной функции phpversion().

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

  • Процедура не возвращает значение. Вместо этого он вызывается с помощью инструкции CALL для выполнения операции, такой как изменение таблицы или обработка извлеченных записей.
  • Функция вызывается внутри выражения и возвращает одно значение непосредственно вызывающему, которое должно использоваться в выражении.
  • Вы не можете вызывать функцию с помощью инструкции CALL и вы не можете вызвать процедуру в выражении.

Синтаксис для рутинного создания несколько отличается для процедур и функций:

  • Параметры процедуры могут быть определены как входные, выходные или оба. Это означает, что процедура может передавать значения обратно вызывающему, используя выходные параметры. Доступ к этим значениям можно получить в операторах, которые следуют за инструкцией CALL. Функции имеют только входные параметры. В результате, хотя обе процедуры и функции могут иметь параметры, определение параметра процедуры отличается от описания функций для функций.
  • Функция возвращает значение, поэтому в определении функции должно быть предложение RETURNS, чтобы указать тип данных возвращаемого значения. Кроме того, в теле функции должно быть хотя бы одно выражение RETURN, чтобы вернуть значение вызывающему. RETURNS и RETURN не отображаются в определениях процедур.

    • Чтобы вызвать хранимую процедуру, используйте CALL statement . Чтобы вызвать хранимую функцию, обратитесь к ней в выражении. Функция возвращает значение во время оценки выражения.

      Процедура вызывается с помощью инструкции CALL и может только возвращать значения с использованием выходных переменных. Функция может вызываться изнутри оператора точно так же, как любая другая функция (то есть, вызывая имя функции), и может возвращать скалярное значение.

      Задание параметра IN, OUT или INOUT действительна только для ПРОЦЕДУРЫ. Для функции FUNCTION параметры всегда считаются параметрами IN.

    Если ключевое слово не задано перед именем параметра, оно по умолчанию является параметром IN. Параметрам для сохраненных функций не предшествуют IN, OUT или INOUT. Все параметры функции рассматриваются как параметры IN.

Чтобы определить хранимую процедуру или функцию, используйте CREATE PROCEDURE или CREATE FUNCTION соответственно:

CREATE PROCEDURE proc_name () routine_body CREATE FUNCTION func_name () RETURNS data_type // diffrent routine_body

Расширение MySQL для хранимой процедуры (не функций) состоит в том, что процедура может генерировать набор результатов или даже несколько наборов результатов, которые вызывающий выполняет так же, как результат инструкции SELECT. Однако содержимое таких наборов результатов не может быть использовано непосредственно в выражении.

Сохраненные процедуры (ссылаясь как на хранимые процедуры, так и на хранимые функции) связаны с конкретной базой данных, подобно таблицам или представлениям. При удалении базы данных любые сохраненные процедуры в базе данных также отбрасываются.

Хранимые процедуры и функции не используют одно и то же пространство имен. Возможно, в базе данных есть процедура и функция с тем же именем.

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

Готовые заявления SQL (PREPARE, EXECUTE, DEALLOCATE PREPARE) могут использоваться в хранимых процедурах, но не в хранимых функциях или триггерах. Таким образом, хранимые функции и триггеры не могут использовать Dynamic SQL (где вы строите операторы как строки, а затем выполняете их). (Динамический SQL в MySQL хранимых процедурах)

Еще несколько интересных отличий между FUNCTION и STORED PROCEDURE:

    Функции обычно используются для вычислений, где as процедуры обычно используются для выполнения бизнес-логики.

    Функции Не могут повлиять на состояние базы данных (Заявления, которые делают явное или неявное совершение или откат, запрещены в функции) В то время как Хранимые процедуры могут влиять на состояние базы данных, используя фиксацию и т.д.
    refrence: J.1. Ограничения на хранимые процедуры и триггеры

    Функции не могут использовать FLUSH , тогда как Хранимые процедуры могут выполнять.

    Внутри хранимой функции или триггера не разрешается изменять таблицу, которая уже используется (для чтения или записи) оператором, вызывающим функцию или триггер. Хороший пример: Как обновить ту же таблицу при удалении в MYSQL?

Примечание : хотя некоторые ограничения обычно применяются к хранимым функциям и триггерам, но не к хранимым процедурам, эти ограничения применяются к хранимым процедурам, если они вызываются из хранимой функции или триггера. Например, хотя вы можете использовать FLUSH в хранимой процедуре, такую ​​хранимую процедуру нельзя вызывать из хранимой функции или триггера.

Проще говоря, хранимые процедуры («ХП») - это сохраненные в базе данных процедуры (написанные с помощью SQL и других управляющих операторов), которые могут быть выполнены движком баз данных и вызваны из программного кода, который с этим движком работает. »»» Читать полностью

Хранимые процедуры в MySQL и PHP. Часть 2

Тэйлор Рен (Taylor Ren ), 03.01.2014

Создание хранимой процедуры в MySQL

Поскольку ХП хранятся на сервере, то и создавать их рекомендуется непосредственно на сервере, т.е. не следует использовать PHP или другие языки программирования для выполнения SQL-команд по созданию хранимых процедур.

Давайте рассмотрим, как создать ХП на сервере MySQL, как создать пользователя для нее и как назначить ему привилегии на запуск нашей ХП. Затем проверим корректность результата. Для этого я воспользуюсь MySQL Workbench . Можно использовать и другие программы (например, PHPMyAdmin). Вы можете выбрать тот инструментарий, который вам больше подходит.

Допустим, наша таблица выглядит так:

CREATE TABLE `salary` (`empid` int(11) NOT NULL, `sal` int(11) DEFAULT NULL, PRIMARY KEY (`empid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Для нашего служащего, которому нужна статистическая информация по зарплатам (средняя, максимальная, минимальная и т.п.) из этой таблицы, мы создадим пользователя "tr" следующим образом:

CREATE USER "tr"@"localhost" IDENTIFIED BY "mypass";

Теперь назначим этому пользователю единственную привилегию EXECUTE в той схеме, где находится таблица salary:

Grant execute on hris.* to tr@`%`

Мы можем убедиться в том, что назначили нужную привилегию, открыв «Users and Privileges» в MySQL Bench:

Теперь создадим саму ХП следующим образом:

DELIMITER $$ CREATE PROCEDURE `avg_sal`(out avg_sal decimal) BEGIN select avg(sal) into avg_sal from salary; END

После выполнения этой команды в MySQL Workbench, будет создана готовая к использованию ХП avg_sal . Она возвращает среднюю зарплату по таблице salary .

Чтобы проверить, действительно ли пользователь tr может запустить ХП и не имеет доступа к таблице salary , нам нужно переподключиться к серверу MySQL, залогинившись как tr . В MySQL Workbench это можно сделать создав другое соединение и указав нужного пользователя и его пароль .

После подключения из под tr , первое, что мы замечаем, - это то, что пользователь вообще не видит каких-либо таблиц, видит только ХП:

Очевидно, что пользователь tr не может обращаться ни к одной из таблиц (а значит, не может видеть и подробную информацию о зарплатах из таблицы salary), но может запустить созданную нами ХП, которая вернет ему среднюю зарплату по компании:

Call avg_sal(@out); select @out;

Будет отображена средняя зарплата.

Итак, мы выполнили всю подготовительную работу: создали пользователя, назначили ему привилегии, создали ХП и протестировали ее. Теперь посмотрим, как вызывать эту ХП из PHP .

Вызов хранимой процедуры из PHP

При использовании PDO вызов ХП довольно прост. Вот соответствующий PHP-код:

$dbms = "mysql"; // Замените следующие параметры соединения на соответствующие вашему окружению: $host = "192.168.1.8"; $db = "hris"; $user = "tr"; $pass = "mypass"; $dsn = "$dbms:host=$host;dbname=$db"; $cn=new PDO($dsn, $user, $pass); $q=$cn->exec("call avg_sal(@out)"); $res=$cn->query("select @out")->fetchAll(); print_r($res);

Переменная $res содержит среднюю зарплату по таблице salary . Теперь пользователь может производить дальнейшую обработку вывода с помощью PHP.

Выводы

В этой статье мы рассмотрели давно забытую составляющую баз данных MySQL : хранимые процедуры. Преимущества использования ХП очевидны, но позвольте мне напомнить: Хранимые процедуры позволяют нам применять более строгий контроль доступа к определенным данным, когда это требуется бизнес-логикой.

Кроме того, мы продемонстрировали основные шаги в создании хранимых процедур, пользователей и назначения соответствующих привилегий, показали, как ХП вызываются из PHP.

Эта статья не покрывает всю тему хранимых процедур. Некоторые важные аспекты, такие как параметры ввода/вывода , управляющие операторы, курсоры, полный синтаксис и др. не были освещены в этой краткой статье.

Если вы заинтересовались, пожалуйста, оставьте здесь свой комментарий. Если нужно, мы будем рады предложить более глубокие статьи о таком полезном и мощном аспекте MySQL, как хранимые процедуры.

Тэйлор Рен

Тэйлор - свободный разработчик веб- и десктопных приложений , проживающий в Сужоу в восточном Китае. Начинал со средств разработки Borland (C++Builder, Delphi), опубликовал книгу по InterBase. С 2003 является сертифицированным экспертом Borland. Затем переключился на веб-разработку в типичной конфигурации LAMP. Позднее начал работать с jQuery, Symfony, Bootstrap, Dart и т.д.

Предыдущие публикации: