Я использую хранимые процедуры в максимально возможной степени по ряду причин.
Сокращение количества обращений к базе данных
Если вам нужно изменить несколько связанных таблиц водин раз, тогда вы можете использовать одну хранимую процедуру, чтобы был выполнен только один вызов базы данных.
Четко определить бизнес-логику
Если определенные вещи должны быть правдойзапрос, а затем процедура хранения позволяет тому, кто знает SQL (довольно простой язык), убедиться, что все сделано правильно.
Создание простых интерфейсов для других программистов
ВашКомпетентные партнеры не из SQL могут использовать гораздо более простые интерфейсы с базой данных, и вы можете быть уверены, что они не могут случайно перевести отношения в плохое состояние.
Рассмотрим:
SELECT a.first_name, IFNULL( b.plan_id, 0 ) AS plan_id
FROM account AS a
LEFT JOIN subscription AS s ON s.account_id = a.id
WHERE a.id = 23
По сравнению с:
CALL account_get_current_plan_id( 23 );
Напишите им симпатичную маленькую обертку, которая позаботится об обработке вызовов хранимых процедур, и они будут в бизнесе.
Обновите все использования в системе сразу
Если все используют хранимые процедуры для запроса базы данных, и вам нужно изменить, как что-то работает, вы можете обновить хранимую процедуру, и она будет обновляться везде, пока вы не измените интерфейс.
Усиленная безопасность
Если вы можете использовать только хранимые процедуры, чтобы делать все в вашей системе, тогда вы можете дать серьезно ограниченные разрешения учетной записи пользователя, которая получает доступ к данным.Нет необходимости давать им разрешения UPDATE, DELETE или даже SELECT.
Простая обработка ошибок
Многие люди не понимают этого, но вы можете создавать свои хранимые процедурытаким образом, что отслеживание большинства проблем становится очень легким.
Вы даже можете интегрировать свою кодовую базу для правильной обработки возвращаемых ошибок, если вы используете хорошую структуру.
Вот пример, которыйвыполняет следующие действия:
- Использует обработчик выхода для серьезных проблем
- Использует обработчик продолжения для менее серьезных проблем
- Предусматривает ли предварительное сканирование таблицы вне таблицы
- Следует ли проверка таблицы следующим образом, если проверка не удалась
- Выполняет ли обработку транзакции, если что-то проверяется
- Откатывает все, если есть проблема
- Сообщает о любых проблемахнайдено
- Позволяет избежать ненужных обновлений
Вот внутренняя часть вымышленной хранимой процедуры, которая принимает идентификатор учетной записи, закрывающую учетную записьидентификатор и IP-адрес, а затем использует их для обновления, как это необходимо.Разделитель уже был установлен в $$:
BEGIN
# Helper variables
DECLARE r_code INT UNSIGNED;
DECLARE r_message VARCHAR(128);
DECLARE it_exists INT UNSIGNED;
DECLARE n_affected INT UNSIGNED;
# Exception handler - for when you have written bad code
# - or something really bad happens to the server
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SELECT 0 as `id`, 10001 as `code`, CONCAT(r_message, ' Failed with exception') as `message`;
END;
# Warning handler - to tell you exactly where problems are
DECLARE CONTINUE HANDLER FOR SQLWARNING
BEGIN
SET r_code = 20001, r_message = CONCAT( r_message, 'WARNING' );
END;
SET r_code = 0, r_message = '', it_exists = 0, n_affected = 0;
# STEP 1 - Obvious basic sanity checking (no table scans needed)
IF ( 0 = i_account_id ) THEN
SET r_code = 40001, r_message = 'You must specify an account to close';
ELSEIF ( 0 = i_updated_by_id ) THEN
SET r_code = 40002, r_message = 'You must specify the account doing the closing';
END IF;
# STEP 2 - Any checks requiring table scans
# Given account must exist in system
IF ( 0 = r_code ) THEN
SELECT COUNT(id) INTO it_exists
FROM account
WHERE id = i_account_id;
IF ( 0 = it_exists ) THEN
SET r_code = 40001, r_message = 'Account to close does not exist in the system';
END IF;
END IF;
# Given account must not already be closed
# - if already closed, we simply treat the call as a success
# - and don't bother with further processing
IF ( 0 = r_code ) THEN
SELECT COUNT(id) INTO it_exists
FROM account
WHERE id = i_account_id AND status_id = 2;
IF ( 0 < it_exists ) THEN
SET r_code = 1, r_message = 'already closed';
END IF;
END IF;
# Given closer account must be valid
IF ( 0 = r_code ) THEN
SELECT COUNT(id) INTO it_exists
FROM account
WHERE id = i_updated_by_id;
END IF;
# STEP 3 - The actual update and related updates
# r-message stages are used in case of warnings to tell exactly where a problem occurred
IF ( 0 = r_code ) THEN
SET r_message = CONCAT(r_message, 'a');
START TRANSACTION;
# Add the unmodified account record to our log
INSERT INTO account_log ( field_list )
SELECT field_list
FROM account
WHERE id = i_account_id;
IF ( 0 = r_code ) THEN
SET n_affected = ROW_COUNT();
IF ( 0 = n_affected ) THEN
SET r_code = 20002, r_message = 'Failed to create account log record';
END IF;
END IF;
# Update the account now that we have backed it up
IF ( 0 = r_code ) THEN
SET r_message = CONCAT( r_message, 'b' );
UPDATE account
SET
status_id = 2,
updated_by_id = i_updated_by_id,
updated_by_ip = i_updated_by_ip
WHERE id = i_account_id;
IF ( 0 = r_code ) THEN
SET n_affected = ROW_COUNT();
IF ( 0 = n_affected ) THEN
SET r_code = 20003, r_message = 'Failed to update account status';
END IF;
END IF;
END IF;
# Delete some related data
IF ( 0 = r_code ) THEN
SET r_message = CONCAT( r_message, 'c' );
DELETE FROM something
WHERE account_id = i_account_id;
END IF;
# Commit or roll back our transaction based on our current code
IF ( 0 = r_code ) THEN
SET r_code = 1, r_message = 'success';
COMMIT;
ELSE
ROLLBACK;
END IF;
END IF;
SELECT
r_code as `code`,
r_message as `message`,
n_affected as `affected`;
END$$
Значения кода состояния:
- 0: никогда не должно происходить - плохой результат
- 1: успех -учетная запись была либо уже закрыта, либо должным образом закрыта
- 2XXXX - проблемы с логикой или синтаксисом
- 3XXXX - проблемы с неожиданными значениями данных в системе
- 4XXXX - пропущены обязательные поля
Вместо того, чтобы доверять программистам, которые не знакомы с базами данных (или просто не знакомы со схемой), гораздо проще предоставить им интерфейсы.
Вместо выполнения всех вышеперечисленных проверок, они могут просто использовать:
CALL account_close_by_id( 23 );
А затем проверить код результата и предпринять соответствующие действия.
Лично я считаю, что если у вас есть доступ к хранимым процедурам и вы их не используететогда вы действительно должны изучить их.