首页 > 解决方案 > 如何在没有临时表的情况下使用 SQL 游标跳过一行

问题描述

我在 SQL 中有一个包含以下列的大表:

公司 ID(整数) 电子邮件(Varchar 255) 名字(Varchar 50) 姓氏(Varchar 50)
1 jim_halpert@dundermifflin.com 吉姆 哈尔珀特
2 bvance@vancerefridgerations.com 鲍勃 万斯
1 michael_scott@dundermifflin.com 迈克尔 斯科特

CompanyId 可以重复多次,因为公司附加到各种电子邮件中。

我的雇主希望我找到属于特定类型的公司电子邮件。在几个 IF 语句之后,最终结果将打印如下消息:

“CompanyId”具有 firstName_lastName@companyName.domain 类型的电子邮件

“CompanyId”具有 firstInitial_lastName@companyName.domain 类型的电子邮件

我的雇主告诉我使用光标找到我的解决方案,但是一旦我启动光标,我需要检查 CompanyId 以查看该 id 是否已经被循环并找到了类型。如果 CompanyId 已经经历了这个循环,我想跳过它。

到目前为止,这是我的代码

      DECLARE @CoId INT
            ,@Email VARCHAR(255)
            ,@FName varchar(50)
            ,@LName varchar(50)
        Declare @condition bit = 1 
    
    Declare CoCursor CURSOR
    For SELECT E.CompanyID, E.Email, P.First_Name, P.Last_Name 
    From Table_Email as E
    Left Join Table_People as P
    ON E.EmployeeID = P.EmployeeID
    Order by CompanyID, Email
    
    -- Loop through the rows with Cursors for CompanyId and Email
    
    OPEN CoCursor
        Fetch NEXT FROM CoCursor
            INTO @CoId, @Email, @FName, @LName
    
        While @@FETCH_STATUS = 0
            BEGIN
            -- Check to see if CompanyId has been logged before, if not, proceed
                -- Check to see if email matches criteria
                IF @Email LIKE @FName + '[_]' + @LName + '@%'
                    BEGIN
                -- If yes, check next email where CompanyIds match
                    ;WITH EmailTable(CompanyID, Email, First_Name, Last_Name) AS (
                         SELECT E.CompanyID, E.Email, P.First_Name, P.Last_Name 
                         From Table_Emails as E
                         Left Table_People as P
                         ON E.EmployeeID = P.EmployeeID
                         Where E.CompanyID = @CoId 
                         And E.Email LIKE P.First_Name + '[_]' + P.Last_Name + '@%')
                    Select TOP(2) @condition = 0
                    FROM EmailTable         
                    Having COUNT(*) > 1
                    -- If email matches previous email, log Company Id with the email type
                    IF @condition = 0
                    PRINT CONVERT(VARCHAR(50), @CoId) + ' has email like firstName_lastName@CompanyAddress.domain' 
                -- If not, go to next Email check                           
                    END
                IF @Email LIKE LEFT(@FName,1) + @LName + '@%'
                -- If yes, check next email where CompanyIds match
                -- If email matches previous email, log Company Id with the email type
        -- If it has been logged move onto next record.
            Fetch NEXT FROM CoCursor
                INTO @CoId, @Email, @FName, @LName
        -- Once the result is logged go to next row 
            END         
    CLOSE CoCursor
    DEALLOCATE CoCursor

我看到的一个解决方案使用临时表来记录以前使用过的 id,但我的雇主不希望我使用临时表。 如何根据某些条件在 MSSQL 游标中跳过一行(迭代)?

谢谢!

标签: sqlsql-serverdatabase-cursor

解决方案


游标速度慢且效率低,很少需要。

这根本不需要游标。一个简单的过滤连接group by就足够了

SELECT
  CONCAT(e.CompanyID), ' has email like firstName_lastName@CompanyAddress.domain')
FROM Table_Email e
JOIN Table_People p ON p.EmployeeID = e.EmployeeID
WHERE e.Email LIKE p.First_Name + '[_]' + p.Last_Name + '@%'
GROUP BY
  e.CompanyID
ORDER BY
  e.CompanyID;

显然,出于某种非常奇怪的原因,您的雇主要求使用光标,我相信您会告诉他们所有不使用光标的原因。但不管怎样,你去吧:

您的原始代码仍然非常复杂,您可以使用上面的代码作为SELECT光标来简化它。

注意游标的局部变量的使用,这意味着您不必释放它

DECLARE @CompanyID int;

DECLARE @crsr CURSOR;
SET @crsr = CURSOR FAST_FORWARD FOR
SELECT
  e.CompanyID
FROM Table_Email e
JOIN Table_People p ON p.EmployeeID = e.EmployeeID
WHERE e.Email LIKE p.First_Name + '[_]' + p.Last_Name + '@%'
GROUP BY
  e.CompanyID
ORDER BY
  e.CompanyID;

OPEN @crsr;

FETCH NEXT FROM @crsr
  INTO @CompanyID;

WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT CONCAT(e.CompanyID), ' has email like firstName_lastName@CompanyAddress.domain');

    FETCH NEXT FROM @crsr
      INTO @CompanyID;
END;

CLOSE @crsr;

推荐阅读