TcK*_*cKs 4 sql stored-procedures sql-server-2005 crud
"最佳实践"之一是通过存储过程访问数据.我明白为什么这种情况很好.我的动机是拆分数据库和应用程序逻辑(表可以改变,如果存储过程的行为相同),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)
)
Run Code Online (Sandbox Code Playgroud)
然后我应该创建4个存储过程(创建/读取/更新/删除).
"创建"的存储过程很简单.
CREATE PROC Insert_Product ( @ProductID uniqueidentifier, @IDProductType uniqueidentifier, ... etc ... ) AS BEGIN
INSERT INTO tblProduct ( ProductID, IDProductType, ... etc .. ) VALUES ( @ProductID, @IDProductType, ... etc ... )
END
Run Code Online (Sandbox Code Playgroud)
"删除"的存储过程也很简单.
CREATE PROC Delete_Product ( @ProductID uniqueidentifier, @IDProductType uniqueidentifier, ... etc ... ) AS BEGIN
DELETE tblProduct WHERE ProductID = @ProductID AND IDProductType = @IDProductType AND ... etc ...
END
Run Code Online (Sandbox Code Playgroud)
"更新"的存储过程与"删除"类似,但我不确定这是正确的方法,如何做到这一点.我认为更新所有列效率不高.
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
Run Code Online (Sandbox Code Playgroud)
最后一次存储的"阅读"程序对我来说是个谜.如何通过复杂条件的过滤器值?我有一些建议:
使用XML参数传递where条件:
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
Run Code Online (Sandbox Code Playgroud)
但是,我认为一个查询的"4000"字符的限制很难看.
下一个建议是为每列使用过滤表.将过滤器值插入过滤器表,然后使用过滤器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
Run Code Online (Sandbox Code Playgroud)
但我认为这个建议很复杂.
是否有一些"最佳实践"来执行此类存储过程?
第一:对于您的删除例程,您的where子句应该只包含主键.
第二:对于您的更新例程,在使用代码之前不要尝试优化.实际上,在您可以分析应用程序并查看瓶颈所在之前,请不要尝试进行优化.我可以肯定地告诉你,更新一行中的一列并更新一行中的所有列的速度几乎相同.在DBMS中花费时间的是(1)找到要写入数据的磁盘块,以及(2)锁定其他写入器以使您的写入保持一致.最后,编写仅更新需要更改的列所需的代码通常会更难以维护.如果你真的想要挑剔,你必须比较确定哪些列发生变化而不是更新每一列的速度.如果您全部更新它们,则不必阅读其中任何一个.
第三:我倾向于为每个检索路径编写一个存储过程.在您的示例中,我将通过主键创建一个,每个外键创建一个,然后在应用程序中根据需要为每个新访问路径添加一个.敏捷; 不要编写你不需要的代码.我也同意使用视图而不是存储过程,但是,您可以使用存储过程返回多个结果集(在某些版本的MSSQL中)或将行更改为列,这可能很有用.
例如,如果您需要通过主键获得7行,则可以选择一些选项.您可以通过主键调用获取一行的存储过程七次.如果您在所有呼叫之间保持连接打开,这可能足够快.如果您知道一次不需要超过一定数量(例如10个)的ID,您可以编写一个包含where子句的存储过程,例如"和(arg1,arg2,arg3 ...)中的ID"和make确保未使用的参数设置为NULL.如果您决定需要生成动态SQL,我不会理会存储过程,因为TSQL与其他任何语言一样容易出错.此外,使用数据库进行字符串操作也没有任何好处 - 它几乎总是你的瓶颈,所以没有必要为DB提供超出必要的工作量.