如何使用TableName,ColumnName和ColumnValue作为传递参数创建DELETE语句存储过程

时间:2016-07-22 21:36:29

标签: sql sql-server stored-procedures sql-delete

这是我正在尝试做的事情。我正在尝试创建一个存储过程,我只需输入表,列和列值的名称,它将删除与该表中该值相关的任何记录。有一个简单的方法吗?我不太了解SQL并且仍在学习它。

这是我到目前为止所拥有的。

ALTER PROCEDURE [dbo].[name of stored procedure] 
@TABLE_NAME varchar(50),
@COLUMN_NAME varchar(50),
@VALUE varchar(5)

AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @RowsDeleted int;
    DECLARE @sql VARCHAR(500);

    SET @sql = 'DELETE FROM (name of table).' + @TABLE_NAME + ' WHERE ' + @COLUMN_NAME + '=' + '@VALUE'
    EXEC(@sql)

    SET @RowsDeleted=@@ROWCOUNT
END
GO

2 个答案:

答案 0 :(得分:0)

夫妻问题

首先,您不需要(表名)

SET @sql = 'DELETE FROM ' + @TABLE_NAME + etc.

通常,您应该尝试包含适当的架构前缀

SET @sql = 'DELETE FROM dbo.' + @TABLE_NAME + etc.

如果你的表名有特殊字符,也许它应该用括号括起来

SET @sql = 'DELETE FROM dbo.[' + @TABLE_NAME + ']' + etc.

由于@Value是一个字符串,因此在计算@SQL的值时必须用单引号括起来。要在字符串中插入单引号,必须使用两个单引号将其转义,如下所示:

SET @SQL = 'DELETE FROM dbo.[' + @TABLE_NAME + '] WHERE [' + @COLUMN_NAME + '] = '''' + @VALUE + ''''

如果@VALUE本身包含单引号,那么整个事情就会中断,所以你也需要转义它

SET @SQL = 'DELETE FROM dbo.[' + @TABLE_NAME + '] WHERE [' + @COLUMN_NAME + '] = '''' + REPLACE(@VALUE,'''','''''') + ''''

此外,@@ ROWCOUNT不会从EXEC填充。如果您希望能够读取@@ ROWCOUNT,请改用sp_ExecuteSQL

EXEC sp_ExecuteSql @SQL

最后,让我编辑一分钟 -

这种存储过程不是一个好主意。我知道它看起来很酷,因为它很灵活,而且当涉及到其他语言时,这种想法通常很聪明,但在数据库领域,这种方法会导致问题,例如:存在安全问题(例如,注入,以及您需要提升权限来调用sp_executeSql)以及预编译/性能问题(因为SQL未提前知道,SQL Server将需要生成新查询计划每次调用此计划)并且由于调用者可以为表名和列名提供任何值,因此您不知道此删除语句是否有效并使用索引,或者是否会因为表大而导致巨大的性能问题并且该列未编入索引。

正确的方法是使用一系列具有强类型输入的适当存储过程,这些输入特定于您需要根据条件删除的每个数据用例。数据库工程师不应该试图使事情变得灵活;你应该强迫人们思考他们究竟需要什么,并实现那个而且只有那个。这是确保人们遵守规则,保持R / I完整,有效使用索引等的唯一方法。

是的,这可能看起来像是重复和冗余的工作,但是并不是很重要。如果您不喜欢额外输入,可以使用工具生成CRUD操作的代码。

答案 1 :(得分:0)

除了John Wu提供的一些信息之外,您还必须担心数据类型,如果您的桌子和事物上有@@ROWCOUNTtriggers可能不准确.....你可以通过转换为nvarchar()并使用OUTPUT子句与temp table进行COUNT()来解决这两个问题。

这里只是为了好玩,你可以这样做:

CREATE PROCEDURE dbo.[ProcName]
@TableName SYSNAME
,@ColumnName SYSNAME
,@Value NVARCHAR(MAX)
,@RecordCount INT OUTPUT
AS

BEGIN

    DECLARE @SQL NVARCHAR(1000)

    SET @SQL = N'IF OBJECT_ID(''tempdb..#DeletedOutput'') IS NOT NULL
        BEGIN
            DROP TABLE #DeletedOutput
        END

    CREATE TABLE #DeletedOutput (
       ID INT IDENTITY(1,1)
        ColumnValue NVARCHAR(MAX)
    )

    DELETE FROM dbo.' + QUOTENAME(@TableName) + ' 
    OUTPUT deleted.' + QUOTENAME(@ColumnName) + ' INTO #DeletedOutput (ColumnValue)
    WHERE CAST(' + QUOTENAME(@ColumnName) + ' AS NVARCHAR(MAX)) = ' + CHAR(39) + @Value  + CHAR(39) + '

    SELECT @RecordCountOUT = COUNT(ID) FROM #DeletedOutput

    IF OBJECT_ID(''tempdb..#DeletedOutput'') IS NOT NULL
        BEGIN
            DROP TABLE #DeletedOutput
        END'

    DECLARE @ParmDefinition NVARCHAR(200) = N'@RecordCountOUT INT OUTPUT'

    EXECUTE sp_executesql @SQL, @ParmDefinition, @RecordCountOUT = @RecordCount OUTPUT

END

因此使用QOUTENAME将有助于对抗注射攻击但不完美。并且我使用CHAR(39)而不是单值引用的转义序列,因为我发现在该点构建字符串时更容易....通过使用OUTPUT中的参数sp_executesql,您可以还记得你的点数。

请记住,因为您可以在SQL中执行某些操作并不总是意味着您应该这样做。