SQL Server 2008是否有更有效的方法来执行此更新循环?

时间:2017-04-20 20:20:54

标签: sql sql-server tsql

首先发布的问题,我提前为任何失误道歉。 该表包含分配给团队的记录,初始分配由另一个流程完成。通常,我们必须重新分配代理商的记录,并将其平均分配给团队的其他成员。我们一直在手工做这个,这很麻烦。所以我提出了这个解决方案:

    DECLARE @UpdtAgt TABLE (ID INT, Name varchar(25))
    INSERT INTO @UpdtAgt
    VALUES (1, 'Gandalf')
          ,(2,'Hank')
          ,(3,'Icarus')


   CREATE TABLE #UpdtQry (TblID varchar(25))
   INSERT INTO #UpdtQry
   SELECT ShtID
   FROM TestUpdate

   DECLARE @RowID INT
   DECLARE @AgtID INT
   DECLARE @Agt varchar(25)
   DECLARE @MaxID INT
   SET @MaxID = (SELECT COUNT(*) FROM @UpdtAgt)
   SET @AgtID = 1

   --WHILE ((SELECT COUNT(*) FROM #UpdtQry) > 0)
   WHILE EXISTS (SELECT TblID FROM #UpdtQry)

   BEGIN
    SET @RowID = (SELECT TOP 1 TblID FROM #UpdtQry)
    SET @Agt = (SELECT Name FROM @UpdtAgt WHERE ID = @AgtID)

    UPDATE TestUpdate
    SET Assignment = @Agt
    WHERE ShtID = @RowID

    DELETE #UpdtQry WHERE TblID = @RowID

    IF @AgtID < @MaxID
        SET @AgtID = @AgtID + 1
    ELSE 
        SET @AgtID = 1


   END

   DROP TABLE #UpdtQry

这是我第一次尝试深入开展这项工作。 100行的更新大约需要30秒。 UPDATE表TestUpdate只有CLUSTERED索引。我怎样才能提高效率呢?

编辑:我没有在我的解释中很好地定义@UpdtAgt和#UpdtQry表。 @UpdtAgt将保留正在重新分配记录的代理,并且每次使用时都可能会更改。 #UpdtQry将有一个WHERE子句来定义哪些代理记录将被重新分配,同样,这将随着每次使用而改变。我希望能让这一点更加清晰。再一次,为第一次没有做到这一点道歉。

编辑2:我注释掉旧的WHILE子句并插入了HABO建议的子句。再次感谢HABO。

2 个答案:

答案 0 :(得分:1)

我认为这是你正在寻找的东西:

DECLARE @UpdtAgt TABLE 
(
  ID INT, 
  Name VARCHAR(25)
)

INSERT @UpdtAgt
VALUES (1, 'Gandalf')
      ,(2, 'Hank')
      ,(3, 'Icarus')

UPDATE t
SET t.Assignment = a.Name
FROM TestUpdate AS t
INNER JOIN @UpdtAgt AS a
    ON t.ShtID = a.ID

这应该一次完成所有4行。

... P.S

如果你以后创建了原始帖子中的表格,请尝试保持列和变量的命名符合其目的!

在您的示例中,您使用了IDAgtIDShtID以及(最令人困惑的)TblID(我认为他们都是同样的事情? [如果我错了,请纠正我!])。如果你在任何地方调用它AgtID(并且@AgtID为变量[@RowID没有真正的需要],那么一眼就能看到什么&#会更容易39;继续! AssignmentName也是如此。

答案 1 :(得分:0)

因为这是你第一次尝试这样的事情,我想祝贺你有所作为。虽然它不理想(什么是?)但它符合主要目标:它有效。使用称为游标的东西有更好的方法。我使用Microsoft的以下页面提醒自己正确的语法:Click here for full instruction on cursors

话虽如此,这篇文章末尾的代码显示了我对你情况的快速解决方案。请注意以下事项:

  1. 定义@TestUpdate表,以便查询将在MSSQL中运行而不使用永久表。
  2. 只需要将@UpdtAgt表设置为临时表。但是,如果定期使用,最好将其作为永久性表格。
  3. 最后的CLOSEDEALLOCATE语句重要 - 忘记这些会产生相当不愉快的后果。
  4. DECLARE @TestUpdate TABLE (ShtID int, Assignment varchar(25))
    
    INSERT INTO @TestUpdate
       VALUES (1,'Fred')
      ,(2,'Barney')
      ,(3,'Fred')
      ,(4,'Wilma')
      ,(5,'Betty'),(6,'Leopold'),(7,'Frank'),(8,'Fred')
    
    DECLARE @UpdtAgt TABLE (ID INT, Name varchar(25))
    
    INSERT INTO @UpdtAgt
    VALUES (1, 'Gandalf')
          ,(2,'Hank')
          ,(3,'Icarus')
    
    DECLARE @recid int
    DECLARE @AgtID int SET @AgtID=0
    DECLARE @MaxID int SET @MaxID = (SELECT COUNT(*) FROM @UpdtAgt)
    
    DECLARE assignment_cursor CURSOR
    FOR SELECT ShtID FROM @TestUpdate
    
    OPEN assignment_cursor
    FETCH NEXT FROM assignment_cursor
    INTO @recid
    
    WHILE @@FETCH_STATUS = 0
    BEGIN
      SET @AgtID = @AgtID + 1
      IF @AgtID > @MaxID SET @AgtID = 1
    
      UPDATE @TestUpdate
      SET Assignment = (SELECT TOP 1 Name FROM @UpdtAgt WHERE ID=@AgtID)
      FROM @TestUpdate TU   
      WHERE ShtID=@recid
    
      FETCH NEXT FROM assignment_cursor INTO @recid
    END
    
    CLOSE assignment_cursor
    DEALLOCATE assignment_cursor
    
    SELECT * FROM @TestUpdate