【问题标题】:Generic Insert stored proc : Runtime error通用插入存储过程:运行时错误
【发布时间】:2009-01-19 13:17:13
【问题描述】:

以下代码为要插入的新记录生成主键并将记录插入到表中,表的名称和要插入的值作为存储过程的参数给出。我收到运行时错误。我正在使用 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].

【问题讨论】:

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


    【解决方案1】:

    您将不得不将表名直接连接到字符串中,因为这无法参数化:

    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、实体框架等)为您完成所有这些工作,并专注于您的实际问题领域。

    【讨论】:

    • 什么是表名白名单?
    • 只需检查它是否是您期望的表...搜索“Bobby Tables”以获取更多信息;-p 否则,有人可能会提交一个看起来很奇怪的表名,它会删除您的所有数据.其他值(@ValueStr 等)不受所谓的注入攻击,因为它们是参数化的。
    • 我想投票赞成你的 Li'l Bobby Tables 评论 :-) 让我再次微笑。
    【解决方案2】:

    白名单本质上意味着确保传入的表是您希望他们能够插入的有效表。让我们假设表名是用户提供的,然后用户可以开始将记录插入系统表中。

    可以通过弹跳sysobjects表的表名来做白名单检查:

    select * from sysobjects where name=@tblname and xType='U'

    但是,正如 Marc 所建议的那样,这不是 TSQL 的好用处,最好在应用层中将其作为参数化查询处理。

    【讨论】:

      【解决方案3】:

      同意 Marc 的观点——总的来说,这是一个非常糟糕的想法。通用插入/更新或删除最终会导致数据库出现问题。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-27
        相关资源
        最近更新 更多