Лучшие практики структурирования хранимых процедур - PullRequest
20 голосов
/ 24 августа 2011

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

Несколько раз я унаследовал код хранимой процедуры кошмара, первые три или четыре уровня хранимых процедур настраивали некоторые временные таблицы и в основном вызывали друг друга. Никакой реальной работы и всего лишь несколько строк кода. Затем, наконец, происходит вызов «последней» хранимой процедуры, большого монстра из 3000-5000 строк кода SQL. Этот код обычно имеет много запахов кода, таких как дублирование кода, сложные потоки управления (aka spaghetti) и метод, который делает слишком много вещей, расположенных друг за другом без четкого разделения, где начинается один кусок работы и где он заканчивается (даже не комментарий как делитель).

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

Очевидно, мои коллеги по команде также разделяют мое отсутствие хороших практик написания SQL.

Итак ... (и здесь возникает реальный вопрос) ... каковы хорошие практики написания модульных поддерживаемых хранимых процедур?

Приветствуются как домашние практики, так и ссылки на книги / блоги. Методы, а также инструменты, которые помогают с определенными задачами.

Позволяет суммировать некоторые области, где я не нашел хороших практик

  • Модуляризация и инкапсуляция (действительно ли способ передачи хранимых процедур через временные таблицы?)
    • В c # я использую сборки, классы и методы, украшенные модификаторами доступа, чтобы выполнить это.
  • Отладка / тестирование (лучше, чем изменение цели отладки?)
    • Инструменты отладки?
    • Отладка трасс?
    • Испытательные приспособления?
  • Подчеркивая код / ​​логику / данные / поток управления, используя код структуру кода
    • В c # я выполняю рефакторинг и выделяю меньшие методы, каждый из которых выполняет только одну логическую задачу.
  • Дублирование кода

В основном я рассматриваю SQL Server как СУБД, но независимые от СУБД ответы или ответы, указывающие на особенности других СУБД: приветствуются также и те, которые помогают в вышеуказанных случаях.

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

Ответы [ 3 ]

14 голосов
/ 25 августа 2011

Я пишу много сложных хранимых процедур.Некоторые вещи, которые я бы посоветовал:

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

Если вы выполняете более одного запроса действия в процедуре (вставка / обновление / удаление), используйте блоки Try Cacth и обработку транзакций.Добавьте тестовый параметр к входным параметрам, и когда он установлен в 1, всегда выполняйте откат всей транзакции.Перед откатом в тестовом режиме у меня обычно есть раздел, который возвращает значения в таблицах, на которые я влияю, чтобы убедиться, что то, что я делаю с базой данных, действительно то, что я делал.Или вы можете иметь чеки, как показано ниже.Это так же просто, как вставка следующего кода вокруг ваших закомментированных в настоящий момент селектов (и раскомментирование их), когда у вас есть параметр @test.

If @test =1
Begin
Select * from table1 where field1 = @myfirstparameter
End

Теперь вам не нужно проходить, комментировать и раскомментировать каждый раз, когда вы тестируете.

@ test или @debuig всегда должны иметь значение по умолчанию 0 и помещаться последними в списке.Таким образом, их добавление не нарушит существующие вызовы процедуры.

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

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

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

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

Все хранимые процессы (и другой код базы данных) должны находиться под контролем исходного кода.

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

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

5 голосов
/ 24 августа 2011

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

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

В SQL Server избегайте интенсивного использования скалярных функций и табличных функций с несколькими операторами в больших наборах строк - они не очень хорошо работают, поэтому модульные методы, которые могут показаться очевидными в C #, здесь не применяются.

Я бы порекомендовал вам взглянуть на Руководство Кена Хендерсона по хранимым процедурам SQL Server - опубликованное в 2002 году, оно по-прежнему содержит массу полезной информации о разработке приложений баз данных.

2 голосов
/ 25 августа 2011

Это такой хороший вопрос.Как разработчик C #, которому самому приходится баловаться с SQL, кажется, что SQL по самой своей природе сталкивается с лучшими практиками, к которым я привык в C #.

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

Набор результатов из одной хранимой процедуры очень трудно использовать в другой, поэтому у вас может возникнуть желание написать табличные функции.Это увеличивает нагрузку на поддержку разрешений и заставляет вас писать функции «дважды» - один раз как функция, а другой как процедура, которая вызывает функцию.В противном случае у вас есть разные интерфейсы для вашего DAL, в зависимости от того, является ли это процедурой.

Все это заставило меня со временем придерживаться простых хранимых процедур CRUD (которые не вызывают друг друга) вбаза данных и несколько изолированных запросов, когда отношения сложны.Больше BI-вещей.Все остальное в BLL.

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

Избегайте SELECT * и предпочитайте указывать столбцы.Это избавит вас от проблем во время выполнения, когда вы меняете таблицу и не затрагиваете все процессы.Да, для процедур есть перекомпиляция, но некоторые из них будут пропущены, особенно если в них включены представления.Кроме того, SELECT * почти всегда возвращает больше столбцов, чем вам действительно нужно, и это пустая трата пропускной способности.

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