如何在没有临时 Table 的情况下使用 SQL 光标跳过一行

How do a Skip a Row With SQL Cursor without a Temp Table

我在 SQL 中有一个很大的 table,包含以下列:

CompanyId(int) Email(Varchar 255) First_Name(Varchar 50) Last_Name(Varchar 50)
1 jim_halpert@dundermifflin.com Jim Halpert
2 bvance@vancerefridgerations.com Bob Vance
1 michael_scott@dundermifflin.com Michael Scott

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

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

"CompanyId" has firstName_lastName@companyName.domain type email

"CompanyId" has firstInitial_lastName@companyName.domain type email

我的雇主告诉我使用 Cursor 来找到我的解决方案,但是一旦我启动了我的游标,我需要检查 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

我看到的一个解决方案使用临时 table 来记录以前使用过的 ID,但我的雇主不希望我使用临时 table。 How Can I Skip a row(an iteration) in MSSQL Cursor based on some condition?

谢谢!

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

这根本不需要游标。一个带有 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来简化一下

Note the use of a local variable for the cursor, this means you don't have to deallocate it

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;

我不太喜欢游标,所以我将提出另一种可能的解决方案(我认为这是一个更好的解决方案,尽管您的雇主出于某种奇怪的原因想要游标)。

您的示例数据:

create table Table_Email (
    CompanyID int, 
    EmployeeID int,
    Email varchar(255)
)

create table Table_People (
    CompanyID int, 
    EmployeeID int,
    First_Name varchar(50),
    Last_Name varchar(50),
)

insert into Table_Email values (1, 1, 'jim_halpert@dundermifflin.com')
insert into Table_Email values (2, 2, 'bvance@vancerefridgerations.com')
insert into Table_Email values (1, 3, 'michael_scott@dundermifflin.com')

insert into Table_People values (1, 1, 'Jim', 'Halpert')
insert into Table_People values (2, 2, 'Bob', 'Vance')
insert into Table_People values (1, 3, 'Michael', 'Scott')

我建议的查询:

SELECT CONVERT(VARCHAR(50), CompanyID) + ' has email like firstName_lastName@CompanyAddress.domain' FROM 
(
 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 
 WHERE Email LIKE P.First_Name + '[_]' + P.Last_Name + '@%' 
) AS a
group by CompanyID
order by CompanyID

它returns:

1 has email like firstName_lastName@CompanyAddress.domain

SQL Fiddle: http://sqlfiddle.com/#!18/4c30e8/1

使光标中的 CompanyId 不同

Declare CoCursor CURSOR
For Select CompanyID, 
            Email, 
            First_Name, 
            Last_Name
FROM (SELECT E.CompanyID, 
            E.Email, 
            P.First_Name, 
            P.Last_Name, 
            ROW_NUMBER() OVER(PARTITION BY E.CompanyID ORDER BY E.Email ASC) as RN
From Table_Emails as E
Left Table_People as P
ON E.EmployeeID = P.EmployeeID) as T
Where RN = 1
Order by CompanyID