如何在没有pk的存储过程中循环临时表

时间:2012-10-22 13:10:17

标签: sql sql-server tsql stored-procedures

问题:

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

研究

我已经搜索了很多,并意识到我不应该使用游标。不过我找到了以下例子:

  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/
  3. 然而,似乎他们使用pk循环记录。对于我的方案中的多个recod,员工编号可以相同

    问题:

    1. 是否有可能实现我没有游标的尝试?
    2. 如果可能,我将如何使用非唯一列获取每一行?

5 个答案:

答案 0 :(得分:6)

由于您尚未完整描述您的情况,因此我们无法给出完整的答案,但一般来说,您希望避免 循环 在基于集合的语言中,如SQL而不是Cursors 本身(Cursosr的问题在于它们需要循环)。

在你的评论中,你提供了一些信息,你想要“循环通过第一个表,与第二个表进行比较,当比较失败时,我从第一个表中删除记录。在essense我正在删除从第一个不再与公司合作的员工表中记录。

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

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

不需要循环......


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

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

第二个选项是重构存储过程,以便它可以接受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问题,尽管许多人也不喜欢它。我自己更喜欢这个解决方案,主要是因为它的普遍性。

答案 1 :(得分:3)

如果当前员工表中没有匹配的行,这将删除员工数据表中的所有记录。

我认为您将DELETE FROM替换为SELECT * 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'
    )

答案 2 :(得分: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

答案 3 :(得分:1)

您是否考虑过使用MERGE声明:http://technet.microsoft.com/en-us/library/bb510625.aspx

你有以下条款:

  1. 当两个表格之间的数据匹配时
  2. 当数据存在于源中但不存在于目标
  3. 中时
  4. 当数据存在于目标中但不存在于源
  5. 中时

    然后,您可以根据匹配类型插入,更新或删除记录。您还可以将匹配操作输出到临时表以进行其他自定义操作。

    例如,你可以这样做:

    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')
    

答案 4 :(得分:0)

  1. 如果你从你想要在光标内部调用的sproc中获取内容并在当前的sproc中实现该逻辑,那么这是可能的。此时,您应该能够使用基于集合的逻辑。你称之为的是非常复杂吗?
  2. 如何将标识列添加到临时表?
  3. 有时,由于各种原因,游标是正确的解决方案。