5

问题:

我需要遍历一个表中的记录,提取员工编号并将此员工编号与另一个表进行比较,以查看他们是否仍然是活跃员工。如果他们不再是活跃的员工,我需要将此行中的数据传递到另一个存储过程中。

研究:

我已经用谷歌搜索了很多,并意识到我不应该为此使用游标。但是,我确实找到了以下示例:

  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 等基于集合的语言中避免循环,而不是游标本身(问题与光标是他们需要循环)。

在您的评论中,您提供了更多信息,因为您希望“循环遍历第一个表,与第二个表进行比较,当比较失败时,我从第一个表中删除记录。本质上,我正在从第一个表中删除记录不再在公司工作的员工。

以下是在 SQL 中执行此操作的方法:

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

不需要循环...


如果问题是您想使用预先存在的存储过程进行删除,那么有几种可能性:

首先,您可以提取存储过程的内容并针对这些前面的 WHERE 条件重新编写它们。我知道这是代码重复,它违反了某些人的 DRY 直觉,但是,我知道 SQL不是面向对象的开发环境,有时代码重复必须发生。

第二个选项是重构存储过程,以便它可以接受 TableParameter 为其 EmployeeId 删除。不过这很复杂,我们需要查看该存储过程来为您提供建议。

第三个选项是使用字符串聚合来构建动态 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 * FROM然后当您愿意删除结果时将其更改回DELETE

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

编辑:刚刚看到您对 Active 标志的评论,这意味着可以将查询更改为

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. 如果您从要在光标内调用的存储过程中获取内容并在当前存储过程中实现该逻辑,那么这是可能的。此时,您应该能够使用基于集合的逻辑。您调用的存储过程非常复杂吗?
  2. 向临时表添加标识列怎么样?

有时,出于各种原因,游标是正确的解决方案。

于 2012-10-22T13:25:33.673 回答