5

問題:

1つのテーブルのレコードをループして、従業員番号を取得し、この従業員番号を別のテーブルと比較して、それらがまだアクティブな従業員であるかどうかを確認する必要があります。彼らがアクティブな従業員ではなくなった場合は、この行のデータを別のストアドプロシージャに渡す必要があります。

リサーチ:

私はかなりググってみましたが、これにはカーソルを使うべきではないことに気づきました。しかし、私は次の例を見つけました。

  1. http://ask.sqlservercentral.com/questions/7969/loop-through-records-in-a-temporary-table.html
  2. http://eedle.com/2010/11/11/looping-through-records-in-sql-server-stored-procedure/

ただし、レコードをループするためにpkを使用しているようです。私のシナリオでは、従業員数は複数のレコードで同じにすることができます

質問:

  1. カーソルなしで私が試みていることを達成することは可能ですか?
  2. 可能であれば、一意でない列を持つ各行をフェッチするにはどうすればよいですか?
4

5 に答える 5

6

状況の完全な説明を提供していないため、完全な回答を提供することはできませんが、一般的に、カーソル自体ではなく、SQLなどのセットベースの言語で回避したいのはループです(問題Cursosrは、ループが必要なことです)。

コメントでは、「最初のテーブルをループして2番目のテーブルと比較し、比較が失敗した場合は最初のテーブルからレコードを削除します。本質的には、最初のテーブルからレコードを削除します。もはや会社にいない従業員。

SQLでこれを行う方法は次のとおりです。

DELETE  From FirstTable
WHERE   FirstTable.EmployeeID NOT IN
    (
        SELECT  SecondTable.EmployeeID 
        FROM    SecondTable
        WHERE   SecondTable.Flag = 'Y'
    )

ループは必要ありません...


問題が、既存のストアドプロシージャを使用して削除を実行することである場合は、いくつかの可能性があります。

まず、ストアドプロシージャの内容を抽出し、これらの先行するWHERE条件に合わせて書き直すことができます。これはコードの重複であり、一部の人々のDRYの本能に違反していることを理解していますが、SQLはオブジェクト指向の開発環境ではなく、コードの重複が発生する場合があることを理解しています。

2番目のオプションは、ストアドプロシージャをリファクタリングして、EmployeeIdの削除対象のTableParameterを受け入れることができるようにすることです。ただし、これは複雑であり、そのストアドプロシージャを確認してアドバイスする必要があります。

3番目のオプションは、文字列集約を使用して動的SQLを構築し、次のように削除する各EmployeeIDのストアドプロシージャを呼び出すことです。

DECLARE @sql As NVarchar(MAX);  
SET     @sql = N'';

SELECT  @sql = @sql + ' 
    EXEC YourProc ''' + CAST(EmployeeID As NVARCHAR(MAX)) + '''; '
FROM    FirstTable
WHERE   FirstTable.EmployeeID IN
    (
        SELECT  SecondTable.EmployeeID 
        FROM    SecondTable
        WHERE   SecondTable.Flag = 'Y'
    )

EXEC(@sql);

これにより、ループとCusrorの両方の問題が回避されますが、多くの人はそれも嫌います。主にその一般性のために、私はこのソリューションを自分で好みます。

于 2012-10-22T13:24:11.270 に答える
3

これにより、現在の従業員テーブルに一致する行がない場合、従業員データテーブルからすべてのレコードが削除されます。

を置き換えて、結果を削除してよろしければ、次のように変更してくださいDELETE FROMSELECT * FROMDELETE

DELETE FROM
    EmployeeDataTable
WHERE
    NOT EXISTS
    (SELECT 
        NULL
    FROM
        CurrentEmployees
    WHERE
        EmployeeDataTable.EmployeeID = CurrentEmployees.EmployeeID
    )

編集:アクティブフラグについてのコメントを見たばかりです。これは、クエリを次のように変更できることを意味します。

DELETE FROM
    EmployeeDataTable
WHERE
    EXISTS
    (SELECT 
        NULL
    FROM
        CurrentEmployees
    WHERE
        EmployeeDataTable.EmployeeID = CurrentEmployees.EmployeeID
        CurrentEmployees.IsActive <> 'Y'
    )
于 2012-10-22T13:24:53.340 に答える
2

これを行うには、カーソルをループします。カーソルに一意のIDを追加して、現在どの行にいるかを確認することもできます。

DECLARE @id uniqueidentifier 
DECLARE @empName   VARCHAR(50)

SELECT newId() AS Id, *
    INTO #mytemp
    FROM MyEmployees
    ORDER BY EmpName

while EXISTS(SELECT TOP 1 1 FROM #mytemp)
BEGIN
    --Get row from cursor to process
    SELECT TOP 1 @id = Id, @empName = EmpName FROM #mytemp  

    --Do you processing here. Call other stored proc.

    --Remove processed row from cursor.
    DELETE FROM #mytemp WHERE Id = @id  
END
DROP TABLE #mytemp
于 2012-10-22T13:37:12.417 に答える
1

MERGE次のステートメントの使用を検討しましたか: http ://technet.microsoft.com/en-us/library/bb510625.aspx

次の句があります:

  1. データが両方のテーブル間で一致する場合
  2. データがソースに存在するが、ターゲットには存在しない場合
  3. データがターゲットに存在するが、ソースには存在しない場合

その後、一致タイプに応じてレコードを挿入、更新、または削除できます。追加のカスタム操作のために、一致から一時テーブルにアクションを出力することもできます。

たとえば、次のことができます。

MERGE INTO Table1
USING Table2 ON Table1.EmployeeID = Table2.EmployeeID
WHEN MATCHED 
    THEN UPDATE SET Table1.SomeField = Table2.SomeOtherField
WHEN NOT MATCHED BY SOURCE
    THEN DELETE
WHEN NOT MATCHED BY TARGET
    THEN Insert (Name, Status) VALUES (EmployeeName, 'Active')
于 2012-10-22T13:47:33.763 に答える
0
  1. カーソル内で呼び出したいsprocからコンテンツを取得し、そのロジックを現在のsproc内に実装すると可能になります。この時点で、セットベースのロジックを使用できるようになります。呼び出しているsprocは非常に複雑ですか?
  2. 一時テーブルにID列を追加するのはどうですか?

さまざまな理由から、カーソルが適切なソリューションである場合があります。

于 2012-10-22T13:25:33.673 に答える