根据行号删除多行

时间:2013-08-20 16:33:23

标签: c# sql sql-server-2008 stored-procedures sql-delete

我想根据从WinForm传入的行号删除行。当然,删除行时ROW_NUMBER()计算的输出总会更新,因此行号也会改变,因此我想一次性而不是循环执行此操作。

我有这段代码:

CREATE PROCEDURE Delete_Record (@RowNum INT)
AS     
  ;WITH REC_ROW AS
  (
    SELECT *, ROW_NUMBER() OVER (ORDER BY [Record Number]) AS RN
        FROM User_Data
  )
  DELETE
    FROM REC_ROW
    WHERE RN IN (@RowNum)

当我输入时:

 exec Delete_Record @RowNum = '1,2'

它产生错误:

  

将数据类型varchar转换为int时出错。

如果我将@RowNum INT更改为@RowNum varchar(max),则只会产生错误消息:

  

将数据类型varchar转换为bigint时出错。

当我对值进行硬编码时:

;WITH REC_ROW AS
(
    SELECT *, ROW_NUMBER() OVER (ORDER BY [Record Number]) AS RN
        FROM User_Data
)
DELETE
  FROM REC_ROW
  WHERE RN IN (1,2)

它将成功删除第1行和第2行。问题是我如何将其集成到存储过程中并输入'1,2'以传递给IN子句?

2 个答案:

答案 0 :(得分:2)

如果您要传递比1,2更复杂的字符串,您可能需要考虑使用表值参数(TVP)。

CREATE TYPE dbo.Integers AS TABLE
(
  RowNumber INT PRIMARY KEY
);

现在,您可以创建存储过程并从应用程序传入DataTable或其他集合,而无需费心构建以逗号分隔的字符串或尝试将它们分开。

CREATE PROCEDURE dbo.Delete_Record
  @RowNums dbo.Integers READONLY
AS
BEGIN
  SET NOCOUNT ON;

  ;WITH REC_ROW AS (...your CTE unchanged here...)
  DELETE REC_ROW
   FROM REC_ROW INNER JOIN @RowNums AS r
   ON r.RowNumber = REC_ROW.RN;
END
GO

如果你真的想使用分割功能:

CREATE FUNCTION dbo.SplitInts
(
   @List      VARCHAR(MAX),
   @Delimiter VARCHAR(255)
)
RETURNS TABLE WITH SCHEMABINDING 
AS
  RETURN 
  (  
    SELECT Item = y.i.value('(./text())[1]', 'int')
    FROM ( SELECT x = CONVERT(XML, '<i>' 
        + REPLACE(@List, @Delimiter, '</i><i>') 
        + '</i>').query('.')) AS a 
    CROSS APPLY x.nodes('i') AS y(i));
GO

现在您的存储过程:

CREATE PROCEDURE dbo.Delete_Record -- always use a schema prefix!
  @RowNums VARCHAR(MAX)
AS 
BEGIN
  SET NOCOUNT ON;

  ;WITH REC_ROW AS
  (
    SELECT *, ROW_NUMBER() OVER (ORDER BY [Record Number]) AS RN
        FROM dbo.User_Data
  )
  DELETE REC_ROW
    FROM REC_ROW 
    INNER JOIN dbo.SplitInts(@RowNums, ',') AS r
    ON r.Item = REC_ROW.RN;
END
GO

但我保证TVP的表现会更好。

答案 1 :(得分:0)

此表达式不符合您的想法:

 WHERE RN IN (@RowNum)

正在寻找@RowNum'1,2'的案例。这会导致问题,因为'1,2'无法转换为整数。您在定义过程时已将参数声明为整数,但随后使用字符串调用它。

你可以做你想做的事:

WHERE ','+@RowNum+',' like '%,'+RN+',%'

额外的逗号确保“1”与“10”不匹配。

编辑:

基本上有三种方法(我可以想到)将逗号分隔的整数字符串作为实数整数处理(这更好,因为查询将使用整数)。

一种是使用split()函数,它返回一个分隔字符串元素的表。你可以google进行各种实现。

第二种是使用动态SQL。这对于不需要瞬时的查询很好。它可能会在编译查询时产生额外的开销,这在事务系统中可能会很明显。

第三种是使用递归CTE来解析字符串。我可能会将此方法用于存储过程。