2008-10-29 2 views
4

Один из «лучших практик» - это доступ к данным через хранимые процедуры. Я понимаю, почему этот сценарий хорош. Моей мотивацией является разделенная база данных и логика приложения (таблицы могут меняться, если поведение хранимых процедур одинаково), защита для SQL-инъекций (пользователи не могут выполнить «select * from some_tables», они могут вызывать только хранимые процедуры) и безопасность (в хранимой процедуре может быть «что угодно», которое защищает, что пользователь не может выбирать/вставлять/обновлять/удалять данные, что не для них).Доступ к данным с хранимыми процедурами

Я не знаю, как получить доступ к данным с помощью динамических фильтров.

Я использую MSSQL 2005.

Если у меня есть таблица:

CREATE TABLE tblProduct (
    ProductID uniqueidentifier -- PK 
    , IDProductType uniqueidentifier -- FK to another table 
    , ProductName nvarchar(255) -- name of product 
    , ProductCode nvarchar(50) -- code of product for quick search 
    , Weight decimal(18,4) 
    , Volume decimal(18,4) 
) 

тогда я должен создать 4 хранимые процедуры (создание/чтение/обновление/удаление).

Сохраненная процедура для «создания» проста.

CREATE PROC Insert_Product (@ProductID uniqueidentifier, @IDProductType uniqueidentifier, ... etc ...) AS BEGIN 
    INSERT INTO tblProduct (ProductID, IDProductType, ... etc ..) VALUES (@ProductID, @IDProductType, ... etc ...) 
END 

Сохраненная процедура для удаления также проста.

CREATE PROC Delete_Product (@ProductID uniqueidentifier, @IDProductType uniqueidentifier, ... etc ...) AS BEGIN 
    DELETE tblProduct WHERE ProductID = @ProductID AND IDProductType = @IDProductType AND ... etc ... 
END 

Хранимая процедура для «обновления» аналогично, как для «удалить», но я не уверен, что это правильный путь, как это сделать. Я думаю, что обновление всех столбцов неэффективно.

CREATE PROC Update_Product(@ProductID uniqueidentifier, @Original_ProductID uniqueidentifier, @IDProductType uniqueidentifier, @Original_IDProductType uniqueidentifier, ... etc ...) AS BEGIN 
    UPDATE tblProduct SET ProductID = @ProductID, IDProductType = @IDProductType, ... etc ... 
     WHERE ProductID = @Original_ProductID AND IDProductType = @Original_IDProductType AND ... etc ... 
END 

И последняя - хранимая процедура для «чтения» - это маленькая загадка для меня. Как передать значения фильтра для сложных условий? У меня есть несколько предложения:

Использования параметра XML для передачи где условия:

CREATE PROC Read_Product (@WhereCondition XML) AS BEGIN 
    DECLARE @SELECT nvarchar(4000) 
    SET @SELECT = 'SELECT ProductID, IDProductType, ProductName, ProductCode, Weight, Volume FROM tblProduct' 

    DECLARE @WHERE nvarchar(4000) 
    SET @WHERE = dbo.CreateSqlWherecondition(@WhereCondition) --dbo.CreateSqlWherecondition is some function which returns text with WHERE condition from passed XML 

    DECLARE @LEN_SELECT int 
    SET @LEN_SELECT = LEN(@SELECT) 
    DECLARE @LEN_WHERE int 
    SET @LEN_WHERE = LEN(@WHERE) 
    DECLARE @LEN_TOTAL int 
    SET @LEN_TOTAL = @LEN_SELECT + @LEN_WHERE 
    IF @LEN_TOTAL > 4000 BEGIN 
     -- RAISE SOME CONCRETE ERROR, BECAUSE DYNAMIC SQL ACCEPTS MAX 4000 chars 
    END 

    DECLARE @SQL nvarchar(4000) 
    SET @SQL = @SELECT + @WHERE 

    EXEC sp_execsql @SQL 
END 

Но, я думаю, что ограничение «4000» символов для одного запроса некрасиво.

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

CREATE TABLE tblFilter (
    PKID uniqueidentifier -- PK 
    , IDFilter uniqueidentifier -- identification of filter 
    , FilterType tinyint -- 0 = ignore, 1 = equals, 2 = not equals, 3 = greater than, etc ... 
    , BitValue bit , TinyIntValue tinyint , SmallIntValue smallint, IntValue int 
    , BigIntValue bigint, DecimalValue decimal(19,4), NVarCharValue nvarchar(4000) 
    , GuidValue uniqueidentifier, etc ...) 

CREATE TABLE Read_Product (@Filter_ProductID uniqueidentifier, @Filter_IDProductType uniqueidentifier, @Filter_ProductName uniqueidentifier, ... etc ...) AS BEGIN 
    SELECT ProductID, IDProductType, ProductName, ProductCode, Weight, Volume 
    FROM tblProduct 
    WHERE (@Filter_ProductID IS NULL 
      OR ((ProductID IN (SELECT GuidValue FROM tblFilter WHERE IDFilter = @Filter_ProductID AND FilterType = 1) AND NOT (ProductID IN (SELECT GuidValue FROM tblFilter WHERE IDFilter = @Filter_ProductID AND FilterType = 2)) 
     AND (@Filter_IDProductType IS NULL 
      OR ((IDProductType IN (SELECT GuidValue FROM tblFilter WHERE IDFilter = @Filter_IDProductType AND FilterType = 1) AND NOT (IDProductType IN (SELECT GuidValue FROM tblFilter WHERE IDFilter = @Filter_IDProductType AND FilterType = 2)) 
     AND (@Filter_ProductName IS NULL OR (... etc ...)) 
END 

Но это предположение littlebit сложно, я думаю.

Есть ли какая-то «лучшая практика» для этого типа хранимых процедур?

ответ

5

Во-первых: для удаления рутина, ваш, где пункт должен включать только первичный ключ.

Во-вторых: для вашей процедуры обновления не пытайтесь оптимизировать, прежде чем у вас есть рабочий код. На самом деле, не пытайтесь оптимизировать, пока вы не сможете профилировать свое приложение и посмотреть, где узкие места.Я могу сказать вам, что обновление одного столбца одной строки и обновление всех столбцов одной строки почти одинаково по скорости. Требуется время в СУБД: (1) найти блок диска, на котором вы будете записывать данные, и (2) блокировать других авторов, чтобы ваша запись была последовательной. Наконец, писать код, необходимый для обновления только столбцов, которые нужно изменить, обычно сложнее и сложнее поддерживать. Если вы действительно хотите получить придирчивость, вам придется сравнить скорость выяснения того, какие столбцы изменились по сравнению с просто обновлением каждого столбца. Если вы обновите их все, вам не нужно их читать.

В-третьих: Я стараюсь писать одну хранимую процедуру для каждого пути поиска. В вашем примере я делаю один по первичному ключу, по одному по каждому внешнему ключу, а затем добавляю по одному для каждого нового пути доступа, как мне было нужно в приложении. Быть подвижным; не пишите код, который вам не нужен. Я также согласен с использованием представлений вместо хранимых процедур, однако вы можете использовать хранимую процедуру для возврата нескольких наборов результатов (в некоторой версии MSSQL) или для изменения строк в столбцах, что может быть полезно.

Если вам нужно получить, например, 7 строк по первичному ключу, у вас есть некоторые опции. Вы можете вызвать хранимую процедуру, которая получает одну строку с помощью первичного ключа семь раз. Это может быть достаточно быстро, если вы держите соединение открытым между всеми вызовами. Если вы знаете, что вам никогда не нужно больше определенного количества (например, 10) идентификаторов за раз, вы можете написать хранимую процедуру, которая включает предложение where, подобное «и ID в (arg1, arg2, arg3 ...)», и сделать что неиспользуемые аргументы равны NULL. Если вы решите, что вам нужно генерировать динамический SQL, я бы не стал беспокоиться о хранимой процедуре, потому что TSQL так же легко ошибается, как и любой другой язык. Кроме того, вы не получаете никакой пользы от использования базы данных для выполнения строковых манипуляций - это почти всегда ваше узкое место, поэтому нет смысла давать БД больше работать, чем необходимо.

6

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

Просто выделите только вид на вид.

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

+0

Должен ли я использовать виды или функции? Функции намного более гибкие, я думаю. Есть ли причина, почему не использовать функции вместо представлений или это не важно? – TcKs 2008-10-29 15:43:11

+0

Часто оптимизатор СУБД будет лучше понимать представления, чем функции. – 2008-10-29 15:45:50

+0

, то это может и не быть - в большой производственной базе данных у нас были очень значительные улучшения производительности, когда мы переключили некоторые более сложные сложные запросы из представлений в функции ... – 2008-10-29 16:11:41

0

В SQL 2005 он поддерживает nvarchar (max), который имеет ограничение 2G, но практически принимает все строковые операции при нормальном nvarchar. Вы можете проверить, может ли это вписаться в то, что вам нужно в первом подходе.

2

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

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

2

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

Create sp_ProductSelect 
(
@ProductID int = null, 
@IDProductType int = null, 
@ProductName varchar(50) = null, 
@ProductCode varchar(10) = null, 
... 
@Volume int = null 
) 
AS 
SELECT ProductID, IDProductType, ProductName, ProductCode, Weight, Volume FROM tblProduct' 
Where 
    ((@ProductID is null) or (ProductID = @ProductID)) AND 
    ((@ProductName is null) or (ProductName = @ProductName)) AND 
    ... 
    ((@Volume is null) or (Volume= @Volume)) 
3

Я не согласен с тем, что создание вставки/обновления/выбора хранимых процедур является «лучшей практикой». Если все ваше приложение не написано в SP, используйте слой базы данных в своем приложении для обработки этих действий CRUD. Еще лучше, используйте технологию ORM, чтобы обрабатывать их для вас.

Смежные вопросы