Один из «лучших практик» - это доступ к данным через хранимые процедуры. Я понимаю, почему этот сценарий хорош. Моей мотивацией является разделенная база данных и логика приложения (таблицы могут меняться, если поведение хранимых процедур одинаково), защита для 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 сложно, я думаю.
Есть ли какая-то «лучшая практика» для этого типа хранимых процедур?
Должен ли я использовать виды или функции? Функции намного более гибкие, я думаю. Есть ли причина, почему не использовать функции вместо представлений или это не важно? – TcKs 2008-10-29 15:43:11
Часто оптимизатор СУБД будет лучше понимать представления, чем функции. – 2008-10-29 15:45:50
, то это может и не быть - в большой производственной базе данных у нас были очень значительные улучшения производительности, когда мы переключили некоторые более сложные сложные запросы из представлений в функции ... – 2008-10-29 16:11:41