One of the “best practices” is access to data through stored procedures. I understand why this scenario is good. My motivation is to separate the database and application logic (tables can change if the behavior of stored procedures is the same), protection for SQL injections (users cannot execute "select * from some_tables", they can only call stored procedures) and security (in the stored the procedure can be “anything” that protects that the user cannot select / insert / update / delete data, which is not for them).
I do not know how to access data using dynamic filters.
I am using MSSQL 2005.
If I have a table:
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) )
then I have to create 4 stored procedures (create / read / update / delete).
The stored procedure for creating is simple.
CREATE PROC Insert_Product ( @ProductID uniqueidentifier, @IDProductType uniqueidentifier, ... etc ... ) AS BEGIN INSERT INTO tblProduct ( ProductID, IDProductType, ... etc .. ) VALUES ( @ProductID, @IDProductType, ... etc ... ) END
The stored procedure for "delete" is also simple.
CREATE PROC Delete_Product ( @ProductID uniqueidentifier, @IDProductType uniqueidentifier, ... etc ... ) AS BEGIN DELETE tblProduct WHERE ProductID = @ProductID AND IDProductType = @IDProductType AND ... etc ... END
The stored procedure for “updating” is similar to “delete”, but I'm not sure if this is the right way to do this. I think updating all columns is inefficient.
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
And the last - the stored procedure for "reading" - is a little mystery to me. How to pass filter values for difficult conditions? I have a few suggestions:
Using an XML parameter to pass, where is the condition:
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
But I think the 4000 character limit for a single request is ugly.
The next suggestion is to use filter tables for each column. Insert the filter values into the filter table, and then call the stored procedure with the filter ID:
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
But this sentence is a little more complicated, I think.
Is there some kind of “best practice” for this type of stored procedure?