1

我有持有用户访问权限的表:

  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,但我不知道如何做到这一点。

4

2 回答 2

0

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

  1. 删除用户的所有现有数据
  2. 为用户插入所有新数据

好处是:

  • 易于编码
  • 容易理解
  • 您希望实现的优化可能很小,不值得麻烦
于 2012-11-22T17:50:55.083 回答
0

在 SQL Server2005 中

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

在 SQLServer 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
于 2012-11-24T18:07:30.237 回答