这是我正在尝试做的事情。我正在尝试创建一个存储过程,我只需输入表,列和列值的名称,它将删除与该表中该值相关的任何记录。有一个简单的方法吗?我不太了解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
答案 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提供的一些信息之外,您还必须担心数据类型,如果您的桌子和事物上有@@ROWCOUNT
,triggers
可能不准确.....你可以通过转换为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中执行某些操作并不总是意味着您应该这样做。