通用插入存储过程:运行时错误

时间:2009-01-19 13:17:14

标签: sql-server stored-procedures visual-studio-2005

以下代码为要插入的新记录生成primaey密钥,并将记录插入到表中,该表的名称和要插入的值将作为存储过程的参数给出。我收到运行时错误。我正在使用Visual Studio 2005来使用SQL Server 2005 Express Edition

ALTER PROCEDURE spGenericInsert

(
    @insValueStr nvarchar(300), 
    @tblName nvarchar(10) 
)


AS

DECLARE @sql nvarchar(400)
DECLARE @params nvarchar(200)
DECLARE @insPrimaryKey nvarchar(10)
DECLARE @rowCountVal integer
DECLARE @prefix nvarchar(5)

--following gets the rowcount of the table--
SELECT @rowCountVal = ISNULL(SUM(spart.rows), 0)
    FROM sys.partitions spart 
    WHERE spart.object_id = object_id(@tblName) AND spart.index_id < 2

SET @rowCountVal = @rowCountVal+1




--Following Creates the Primary Key--
IF @tblName = 'DEFECT_LOG' 
    SET @prefix='DEF_'
ELSE IF @tblName='INV_Allocation_DB'
    SET @prefix='INV_'
ELSE IF @tblName='REQ_Master_DB'
    SET @prefix='REQ_'
ELSE IF @tblName='SW_Master_DB'
    SET @prefix='SWI_'
ELSE IF @tblName='HW_Master_DB'
    SET @prefix='HWI_'  


SET @insPrimaryKey= @prefix + RIGHT(replicate('0',5)+ convert(varchar(5),@rowCountVal),5) -- returns somethin like 'DEF_00005'


-- Following is for inserting into the table --

SELECT @sql =   N' INSERT INTO @tableName VALUES ' +
        N' ( @PrimaryKey , @ValueStr )'

SELECT @params = N'@tableName nvarchar(10), ' +
                    N'@PrimaryKey nvarchar(10), ' +
                    N'@ValueStr  nvarchar(300)'

EXEC sp_executesql @sql, @params, @tableName=@tblName, @PrimaryKey=@insPrimaryKey, @ValueStr=@insValueStr

输出消息:

Running [dbo].[spGenericInsert] ( @insValueStr = 2,"Hi",1/1/1987, @tblName = DEFECT_LOG ).

Must declare the table variable "@tableName".

No rows affected.

(0 row(s) returned)

@RETURN_VALUE = 0

Finished running [dbo].[spGenericInsert].

3 个答案:

答案 0 :(得分:4)

您必须将表名直接连接到字符串中,因为这不能参数化:

SELECT @sql =   N' INSERT INTO [' + @tblName + '] VALUES ' +
            N' ( @PrimaryKey , @ValueStr )'

SELECT @params = N'@PrimaryKey nvarchar(10), ' +
                N'@ValueStr  nvarchar(300)'

要防止注入攻击,您应该将此表名称列入白名单。如果表具有其他非可空列等,这也不健壮。

注意:但就个人而言,我认为这不是对TSQL的良好使用;在客户端(C#或其他)中构造命令可能更合适,并将其作为参数化命令执行。动态SQL有一些用例,但我不确定这是一个很好的例子。

更好的是,使用您首选的ORM工具(LINQ-to-SQL,NHibernate,LLBLGen,实体框架等)为您完成所有这些工作,并专注于您的实际问题域。

答案 1 :(得分:0)

白名单本质上意味着确保传入的表是您希望它们能够插入的有效表。我们只是为了论证,表名是用户提供的,然后用户可以开始将记录插入系统表。

您可以通过弹出sysobjects表的表名来执行白名单检查:

从sysobjects中选择*,其中name = @ tblname和xType ='U'

然而,正如Marc所说,这不是一个很好的TSQL使用,你最好在app层中处理这个作为一个参数化的查询。

答案 2 :(得分:0)

同意Marc-总体而言,这是一个非常糟糕的主意。通用插入/更新或删除最终会导致数据库出现问题。

另一点是,当两个用户同时针对同一个表运行时,此过程会出现问题,因为他们会尝试插入相同的主键。