【问题标题】:sql2000 loop in a stored procedure存储过程中的sql2000循环
【发布时间】:2009-03-03 18:01:10
【问题描述】:

我对 SQL 很陌生,我可以相当轻松地处理基本语句,但我还没有弄清楚循环。

Foreach(JobHeaderID AS @OldJobHeaderID in dbo.EstimateJobHeader WHERE EstimateID=@OldEstimateID)  
{  
  INSERT EstimateJobHeader (ServiceID,EstimateID) 
  SELECT ServiceID, @NewEstimateID 
  FROM EstimateJobHeader 
  WHERE EstimateID=@OldEstimateID;  

  SELECT @err = @@error 
  IF @err <> 0 
  BEGIN 
    ROLLBACK TRANSACTION 
    SET @RETURN_VALUE = 4 
    RETURN 4 
  END  

  SET @NewJobHeaderID = CAST(SCOPE_IDENTITY() AS INT)  

  SELECT @err = @@error 
  IF @err <> 0 
  BEGIN 
    ROLLBACK TRANSACTION 
    SET @RETURN_VALUE = 3 
    RETURN 3 
  END  

  INSERT EstimateDetail (JobHeaderID, OtherCols) 
  SELECT (@NewJobHeaderID,OtherCols) 
  FROM EstimateDetail 
  WHERE JobHeaderID=@OldJobHeaderID

  SELECT @err = @@error 
  IF @err <> 0 
  BEGIN 
    ROLLBACK TRANSACTION 
    SET @RETURN_VALUE = 3 
    RETURN 3 
  END  

  INSERT EstimateJobDetail (JobHeaderID, OtherCols) 
  SELECT (@NewJobHeaderID, OtherCols) 
  FROM EstimateJobDetail 
  WHERE JobHeaderID=@OldJobHeaderID  

  SELECT @err = @@error 
  IF @err <> 0 
  BEGIN 
    ROLLBACK TRANSACTION 
    SET @RETURN_VALUE = 3 
    RETURN 3 
  END  
}

【问题讨论】:

  • 更新了我的答案:在这里发帖,这样你就会在你的用户页面上被 ping 通。

标签: sql stored-procedures loops


【解决方案1】:

您应该避免存储过程中的循环。

Sql 是一种声明式语言,而不是您最习惯的命令式语言。几乎所有你想对循环做的事情都应该作为基于集合的操作来完成,或者在客户端代码中完成。当然也有例外,但没有你想象的那么多。

看这个:
Why is it so difficult to do a loop in T-SQL


您询问了如何使用基于集合的方法来做到这一点。我会尽力而为,但是您的代码早期存在一个错误,这使我很难确定我是否正确阅读了它。第一个 INSERT 语句的条件与 FOREACH 循环的条件相匹配。因此,要么循环只运行一次(在那里返回一条记录),要么您的插入在每次迭代中插入几条新记录(是的,插入语句一次可以添加多条记录)。而如果是添加多条记录,为什么只得到最后一次插入创建的标识?

也就是说,我想我理解得足够好,可以向您展示一些东西。看起来您只是在复制估算值。您也没有解释 @NewEstimateID 值的来源。如果有父表,那就这样吧,但了解一下会很有帮助。

/* Where'd @NewEstimateID come from? */
/* If there are several records in EstimateJobHeader with @OldEstimateID,
 *  this will insert one new record for each of them */
INSERT EstimateJobHeader (ServiceID,EstimateID)
     SELECT ServiceID, @NewEstimateID 
     FROM EstimateJobHeader
     WHERE EstimateID= @OldEstimateID

/* Copy EstimateDetail records from old estimate to new estimate */
INSERT EstimateDetail (JobHeaderID, OtherCols) 
    SELECT (new.JobHeaderID,ed.OtherCols) 
    FROM EstimateJobHeader new
    INNER JOIN EstimateJobHeader old ON old.EstimateID= @OldEstimateID 
        AND new.EstimateID= @NewEstimateID AND old.ServiceID=new.ServiceID
    INNER JOIN EstimateDetail ed ON ed.JobHeaderID= old.JobHeaderID

/* Copy EstimateJobDetail records from old estimate to new estimate */
INSERT EstimateJobDetail (JobHeaderID, OtherCols)
    SELECT (new.JobHeaderID,ed.OtherCols) 
    FROM EstimateJobHeader new
    INNER JOIN EstimateJobHeader old ON old.EstimateID= @OldEstimateID 
        AND new.EstimateID= @NewEstimateID AND old.ServiceID=new.ServiceID
    INNER JOIN EstimateJobDetail ejd ON ejd.JobHeaderID= old.JobHeaderID

上面的代码假设 ServiceID+EstimateID 在 EstimateJobHeader 表中是唯一的。如果不是这种情况,我需要知道哪些列或哪些列唯一标识表中的行,以便我可以将旧记录与新记录连接起来,并确定关系是 1:1。

最后,为了简洁,省略了错误检查。

【讨论】:

  • 我已经发布了我想要完成的任务。我将如何在基于集合的操作中做到这一点?
【解决方案2】:

看看 WHILE 语句:

http://msdn.microsoft.com/en-us/library/aa260676(SQL.80).aspx

但是,根据您要执行的操作,可能有更好的、基于集合的方式来执行您要执行的操作,并且应该首先考虑这一点。

【讨论】:

    猜你喜欢
    • 2013-03-04
    • 2015-01-19
    • 1970-01-01
    • 2013-11-13
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多