ストアド プロシージャ内の sql2000 ループ
-
03-07-2019 - |
質問
私は 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
}
解決
ストアド プロシージャではループを避ける必要があります。
SQL は、よく使われている命令型言語ではなく、宣言型言語です。ループで実行したいほぼすべてのことは、セットベースの操作として実行するか、クライアント コードで実行する必要があります。もちろん例外はありますが、思っているほど多くはありません。
これを参照してください:
T-SQL でループを実行するのはなぜ難しいのですか
セットベースのメソッドを使用してそれを行う方法を尋ねました。最善を尽くしますが、コードの早い段階でバグがあり、コードを正しく読んでいるかどうかを確認するのが困難です。最初の INSERT ステートメントの条件は、FOREACH ループの条件と一致します。したがって、ループは 1 回だけ実行される (1 つのレコードが返される) か、挿入によって反復ごとに複数の新しいレコードが挿入されます (はい、insert ステートメントは一度に複数のレコードを追加できます)。また、複数のレコードを追加している場合、なぜ最後の挿入によって作成された ID のみを取得するのでしょうか?
とはいえ、何かを示すには十分理解していると思います。見積書のコピーを作成しているだけのようです。また、@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 であることを確認できるように、テーブル内の行を一意に識別する列を知る必要があります。
最後に、簡潔にするためにエラー チェックは省略しました。
他のヒント
WHILEステートメントを見てください:
http://msdn.microsoft.com/en -us / library / aa260676(SQL.80).aspx
ただし、あなたがやろうとしていることに応じて、おそらくあなたがやろうとしていることを行うためのより良い、セットベースの方法があり、最初にそれを考慮する必要があります。