MySQL хранимые процедуры или PHP-код? - PullRequest
7 голосов
/ 06 августа 2010

Общий вопрос, без учета конкретного случая - обычно предпочитают использовать хранимые процедуры MySQL вместо написания PHP-скрипта, который выполняет те же вычисления и запросы?

Каковы преимущества каждого метода?

Ответы [ 11 ]

10 голосов
/ 10 августа 2010

Point / Counter Point с Джефф Этвудс "Кому нужны хранимые процедуры, в любом случае?"с 2004 :

1) Хранимые процедуры пишутся на «языках» большой базы данных железа, таких как PL / SQL (Oracle) или T-SQL (Microsoft).Эти так называемые языки являются архаичными и полны сумасшедших, непоследовательных вариантов дизайна, которые всегда являются результатом мучительной эволюции десятилетней обратной совместимости.Вы действительно не хотите писать много кода в этом материале.Для контекста, JavaScript является гигантским шагом по сравнению с PL / SQL или T-SQL.

Ответ : «S» в «SQL» означает«Структурированный», а не «стандартизированный» - PLSQL и TSQL - это пользовательские расширения SQL, которые также вводят ANSI SQL в игру, потому что очень мало SQL, который не зависит от базы данных.Как правило, если вы хотите, чтобы запрос выполнялся хорошо, вы не можете полагаться на ANSI SQL.

ORM не является серебряной пулей - из-за абстракции базы данныхБольшинство из них поддерживают запуск собственных хранимых процедур / функций, чтобы получить хорошо работающий запрос.Это хорошо, но совершенно не вписывается в цель ORM ...

Я никогда не пойму, почему веб-разработка, сочетание бесчисленных технологий (HTML, Javascript / AJAX, Flash ...) всегда разделяет SQL какЧерная овца семьи.Как и все остальные, вы должны изучить это, чтобы получить что-то из этого.Это должно быть мгновенное удовлетворение, которое вы получаете при использовании других технологий ...

2) Хранимые процедуры обычно не могут быть отлажены в той же IDE, в которой вы пишете свой пользовательский интерфейс.Каждый раз, когда я изолирую исключение в процессах, мне нужно остановить то, что я делаю, уничтожить свою копию Toad и загрузить пакеты базы данных, чтобы увидеть, что происходит не так.Часто переход между двумя совершенно разными IDE с совершенно разными интерфейсами и языками не совсем продуктивен.

Ответ : был ли изначально отладчик Javascript вЗатмение или Visual Studio?Нет, они позволяют использовать плагины, чтобы вывести продукт на улицу и оживить ранее не существующий рынок.У большинства из них нет проблем с использованием Firebug за пределами Visual Studio / Eclipse, почему отладка SQL должна отличаться?

3) Хранимые процедуры не дают большой обратной связи, когда что-то идет не так.Если процесс не закодирован во внутренней части с помощью странной обработки исключений T-SQL или PL / SQL, мы получим загадочные «ошибки», возвращаемые на основе конкретной строки внутри процесса, которая завершилась неудачно, например, в таблице нет строк.Ну, хорошо?

Ответ : Ваше незнакомство не означает плохой язык.Как будто вам никогда не приходилось искать в Google странную ошибку на выбранном вами языке ... По крайней мере, Oracle и MySQL дают вам ссылочные номера ошибок.

4) Хранимые процедуры не могут передавать объекты.Так что, если вы не будете осторожны, вы можете получить миллионы параметров.Если вам нужно заполнить строку таблицы более чем 20 полями с помощью процедуры, скажите «привет» более 20 параметрам.Хуже всего, если я передаю неверный параметр - либо слишком много, либо недостаточно, либо плохие типы данных - я получаю общую ошибку «плохой вызов».Oracle не может сказать мне, какие параметры ошибочны!Таким образом, я должен вручную определить более 20 параметров, чтобы выяснить, какой из них является виновником.

Ответ : SQL полностью основан на SET.в отличие от процедурного / ОО-программирования.Типы близки к объектам, но в какой-то момент должно быть соответствие между процедурными / объектными объектами и объектами базы данных.

5) Хранимые процедуры скрывают бизнес-логику.Я понятия не имею, что делает процесс, какой тип курсора (DataSet) или значения он мне вернет.Я не могу просмотреть исходный код для процесса (по крайней мере, не прибегая к # 2, если у меня есть соответствующий доступ), чтобы убедиться, что он действительно делает то, что я думаю, - или то, что разработчик намеревался сделать.Встроенный SQL может быть не очень красивым, но, по крайней мере, я вижу его в контексте наряду с другой бизнес-логикой.

Ответ : Это хорошо (тм) - так вы получаете Model-View-Controller (MVC), так что вы можете иметьинтерфейс в любом множестве языков без необходимости дублировать логику каждый раз, когда приходится иметь дело с причудами каждого языка для репликации этой логики.Или это хорошо, что база данных позволяет добавлять неверные данные, если кто-то подключается напрямую к базе данных?Переключение между приложением и базой данных приводит к потере времени и ресурсов, которые ваше приложение никогда не окупит.

4 голосов
/ 06 августа 2010

Я думаю, что Джефф Этвуд ударил по гвоздю по голове в 2004 году относительно сохраненных процедур:

Кому нужны хранимые процедуры?

При интенсивном использовании как хранимых процедур, так и динамического SQL, я определенно предпочитаю последнее: более простое управление, лучшую инкапсуляцию, отсутствие BL на уровне доступа к данным, большую гибкость и многое другое. Практически каждый крупный PHP-проект с открытым исходным кодом использует динамический SQL поверх хранимых процедур (см .: Drupal, Wordpress, Magento и многие другие).

Этот разговор кажется почти архаичным: получите себе хорошую ORM , перестаньте беспокоиться о своем доступе к данным и начните создавать классные приложения.

3 голосов
/ 10 августа 2010

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

Однако во время работы приложения нам иногда приходится вносить незначительные исправления в наши запросы. Простые вещи, такие как добавление или удаление NOLOCK, или, возможно, даже изменение участвующих объединений. Это почти всегда по причинам производительности. Только сегодня у нас была ошибка, вызванная посторонним NOLOCK. 2 минуты, чтобы найти проблему, определить решение и развернуть новый процесс: нулевое время простоя. Выполнение этого с запросами в коде вызвало бы, по крайней мере, незначительное отключение, которое может разозлить многих людей.

Другая причина - безопасность. С помощью proc мы передаем идентификатор пользователя (не последовательный, не угадываемый) в каждый вызов proc. Мы подтверждаем, что пользователь имеет доступ к запуску этой функции в веб-приложении и снова в самой базе данных. Это радикально поднимает барьер для хакеров, если наше веб-приложение было взломано. Мало того, что они не могли запустить любой SQL, который они хотят, но даже чтобы запустить процесс, они должны были бы иметь определенный ключ авторизации. Который было бы трудно получить. (и нет, это не единственная наша защита)

У нас наш процесс находится под контролем исходного кода, так что это не проблема. Кроме того, мне не нужно беспокоиться о том, как я называю вещи (некоторые ORM ненавидят определенные схемы именования), и мне не нужно беспокоиться о характеристиках полета. Чтобы правильно настроить ORM, вам нужно знать больше, чем просто SQL. Вы должны знать особенности поведения ORM.

2 голосов
/ 06 августа 2010

Для меня преимущество сохранения чего-либо общего с базой данных в базе данных является отладкой.Если ваши вычисления (по крайней мере, большинство из них) выполнены в рамках хранимой процедуры, и вам нужно внести изменения, то вы просто модифицируете их, тестируете, сохраняете.В вашем PHP-коде не будет никаких изменений.

Если вы храните основные вычисления в своем PHP-коде, вам нужно взять SQL-операторы из кода, очистить его, затем изменить, протестировать изатем скопируйте его обратно и протестируйте снова.

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

Если база данных настроена правильно, у вас, вероятно, будет немного более быстрое время для выполнения запроса, потому что вместо того, чтобы PHP анализировал строку, затем отправьте ее вбаза данных, затем база данных выполняет ее и отправляет обратно, вы можете просто вставить параметры в базу данных с помощью хранимой процедуры, у нее будет кэшированный план выполнения для хранимой процедуры, и все будет немного быстрее.Несколько тщательно размещенных индексов могут помочь ускорить любой поиск данных, потому что на самом деле - веб-сервер - это всего лишь канал, и PHP-скрипты не загружают его так сильно.

2 голосов
/ 06 августа 2010

Хранимая процедура 99 раз из 100. Если бы я был причиной 1, то было бы так, что если ваше веб-приложение php делает все обращения к базе данных через хранимые процедуры, а у пользователя базы данных есть разрешение только на выполнение указанных хранимых процедур, тогда вы на 100%. % защищен от атак SQL-инъекций.

1 голос
/ 09 декабря 2015

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

Сокращение количества обращений к базе данных

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

Четко определить бизнес-логику

Если определенные вещи должны быть правдойзапрос, а затем процедура хранения позволяет тому, кто знает 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 );

А затем проверить код результата и предпринять соответствующие действия.

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

1 голос
/ 06 августа 2010

Ну, есть сторона этого аргумента, которую я очень редко слышу, поэтому я напишу это здесь ...

Код контролируется версией. Базы данных нет. Поэтому, если у вас есть более одного экземпляра кода, вам понадобится какой-либо способ автоматического выполнения миграций после обновления, иначе вы рискуете сломаться. И даже при этом вы все еще сталкиваетесь с проблемой «забывания» добавления обновленного SP в сценарий миграции, а затем прерывания сборки (возможно, даже не осознавая этого, если вы не тестируете ДЕЙСТВИТЕЛЬНО idepth).

Из-за отладки и сопровождения я считаю, что SP в 100 раз труднее анализировать, чем необработанный SQL. Причина в том, что для этого требуется как минимум три шага. Во-первых, посмотрите в коде PHP, чтобы увидеть, как называется код. Затем зайдите в базу данных и найдите эту процедуру. Затем, наконец, посмотрите на код процедуры.

Еще один аргумент (по аналогии с управлением версиями), что для SP нет команды svn st. Так что, если у вас есть разработчик, который вручную модифицирует SP, у вас будет огромное количество времени, чтобы понять это (при условии, что они не все управляются одним администратором базы данных).

Где SP действительно сияет, когда несколько приложений взаимодействуют с одной и той же схемой базы данных. Тогда у вас есть только одно место, где хранятся DDL и DML, и оба приложения могут обмениваться ими, не добавляя перекрестную зависимость в одну или несколько библиотек.

Итак, вкратце, мое мнение таково:

Использовать хранимые процедуры:

  1. Если несколько приложений работают с одним и тем же набором данных
  2. Когда вам необходимо циклически выполнять запросы и выполнять другие запросы (избегание потерь на уровне TCP может значительно повысить эффективность)
  3. Когда у вас есть действительно хороших администраторов баз данных, так как он обеспечит выполнение всего SQL, обрабатываемого им / ею.

Использовать сырой SQL / ORM / Сгенерированный SQL практически в любом другом случае (примерно, поскольку есть обязательные крайние случаи, о которых я не думаю) ...

Опять же, это всего лишь мои 0,02 доллара ...

1 голос
/ 06 августа 2010

Я бы сказал: "не делайте слишком много магии с базой данных". В худшем случае новый разработчик проекта может заметить, что ** операция ** выполнена, но он не может увидеть, где в коде это. Поэтому он продолжает искать это. Но это делается в базе данных.

Так что, если вы выполняете некоторые «невидимые» операции с базой данных (я думаю о триггерах), просто напишите это в какой-то документации кода.

// add a new user
$user = new User("john", "doe");
$user->save();
// The id is computed by the database see MYPROC_ID_COMPUTATION
print $user->getId();

С другой стороны, написание функций для БД - хорошая идея, и она предоставит разработчику хороший уровень абстракции.

// Computes an ID for the given user
DB->execute("SELECT COMPUTE_ID(" . $user->getLogin() . ") FROM DUAL");

Конечно, все это псевдокод, но я надеюсь, что вы понимаете мою неясную идею.

0 голосов
/ 06 августа 2010

Я слышал, как люди говорили: «Позвольте базе данных делать как можно больше», а другие восклицали: «Что вы делаете с производительностью моей базы данных?»

Так что я думаю, что в основномрешение об уровне использования (хранимые процедуры подчеркнут процесс MySQL, а код PHP - процесс веб-сервера).

0 голосов
/ 06 августа 2010

Вам не обязательно нужны базовые значения, если вычисления выполняются в базе данных, а затем дайте базе данных сделать их.Это помогает свести к минимуму объем передачи данных между базами данных PHP-скрипта;но обычно вычисления с данными базы данных лучше всего выполнять самой базой данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...