TSQL根据选择在过程内添加或删除行

时间:2012-11-22 17:34:44

标签: sql tsql sql-server-2005

我拥有保存用户访问权限的表:

  Id  |  UserId  |  SectionId
  ---------------------------
  1   |  1       |  1
  2   |  1       |  2
  3   |  2       |  1
  4   |  1       |  3

使用此代码:

CREATE FUNCTION dbo.Split (@List    NVARCHAR(2000), 
                       @SplitOn NVARCHAR(5)) 
returns @RtnValue TABLE ( 
   id    INT IDENTITY(1, 1), 
   value NVARCHAR(100)) 
AS 
BEGIN 
  WHILE ( Charindex(@SplitOn, @List) > 0 ) 
    BEGIN 
        INSERT INTO @RtnValue 
                    (value) 
        SELECT Value = Ltrim(Rtrim(Substring(@List, 1, Charindex(@SplitOn, 
                                                       @List) 
                                                       - 1))) 

        SET @List = Substring(@List, Charindex(@SplitOn, @List) + Len( 
                                     @SplitOn 
                                     ), 
                    Len 
                    (@List)) 
    END 

  INSERT INTO @RtnValue 
              (value) 
  SELECT Value = Ltrim(Rtrim(@List)) 

  RETURN 
END 

我创建了一个返回的函数(现在是特定用户和部分的所有记录):

CREATE PROCEDURE UpdateSettings(@UserId int, @NewSettings nvarchar(255))
AS
BEGIN
  SELECT * 
  FROM settings
  WHERE 
    UserId = @UserId AND 
    SectionId IN (SELECT convert(int,Value) FROM dbo.Split(@NewSettings,','))
END;

可在http://sqlfiddle.com/#!3/79de7/4

进行测试

我想修改此程序,以便删除存在的项目,并添加不存在的项目。

这是一个例子。我想改变这个:

  Id  |  UserId  |  SectionId
  ---------------------------
  1   |  1       |  1
  2   |  1       |  2
  3   |  2       |  1
  4   |  1       |  3

分为:

  Id  |  UserId  |  SectionId
  ---------------------------
  1   |  1       |  1
  3   |  2       |  1
  4   |  1       |  3
  5   |  1       |  4

这应该通过调用:

来完成
EXEC UpdateSettings @UserId=1, @NewSettings='2,4'

因为UserId = 1和SectionId = 2的行应该删除它,因为对于userId = 1和SectionId = 4行不存在我想创建它。

我的第一个想法是为NewSettings创建一个游标,然后检查该行是否存在,如果是,则删除它,如果没有添加它。

第二个想法是删除所有已存在的行,如下所示:

CREATE PROCEDURE UpdateSettings(@UserId int, @NewSettings nvarchar(255))
AS
BEGIN
  DELETE 
  FROM settings
  WHERE 
    UserId = @UserId AND 
    SectionId IN (SELECT convert(int,Value) FROM dbo.Split(@NewSettings,','))
  END;

这样我就可以删除已经存在的记录了,但是没有想法如何添加新记录(比如在我的例子中我必须删除一行,因为它存在于表中并添加一个新的)

我想避免光标,也许会做某种JOIN,但我不知道如何做到这一点。

2 个答案:

答案 0 :(得分:0)

如果我这样做,我会保持简单:

  1. 删除用户的所有现有数据
  2. 为用户插入所有新数据
  3. 好处是:

    • 易于编码
    • 易于理解
    • 您希望实现的优化可能非常小,不值得麻烦

答案 1 :(得分:0)

在SQL Server 2005中

CREATE PROCEDURE UpdateSettings(@UserId int, @NewSettings nvarchar(255))
AS
BEGIN
  DECLARE @tmp TABLE(UserId int, SectionId int)
  DELETE s
  OUTPUT DELETED.UserId, DELETED.SectionId INTO @tmp
  FROM settings s JOIN (
                        SELECT @UserId AS UserId, CONVERT(int, Value) AS SectionId 
                        FROM dbo.Split(@NewSettings, ',')  
                        ) d ON s.UserId = d.UserId AND s.SectionId = d.SectionId 
  INSERT settings(UserId, SectionId)
  SELECT @UserId AS UserId, CONVERT(int,Value) AS SectionId
  FROM dbo.Split(@NewSettings, ',') d LEFT JOIN @tmp t ON CONVERT(int, d.Value) = t.SectionId 
                                      LEFT JOIN settings s ON @UserId = s.UserId AND CONVERT(int, d.Value) = s.SectionId
  WHERE t.UserId IS NULL AND s.UserId IS NULL
END

在SQL Server 2008中

CREATE PROCEDURE UpdateSettings(@UserId int, @NewSettings nvarchar(255))
AS
BEGIN
  MERGE settings AS target
  USING (
         SELECT id, @UserID AS UserId, value AS SectionId 
         FROM dbo.Split(@NewSettings,',')
         ) AS source
  ON (target.UserId = source.UserId AND target.SectionId = source.SectionID)
  WHEN MATCHED
    THEN DELETE
  WHEN NOT MATCHED
    THEN INSERT (
                 UserId,
                 SectionId
                 )
         VALUES (
                 source.UserId,
                 source.SectionId
                 );  
END