Home > OS >  How do a Skip a Row With SQL Cursor without a Temp Table
How do a Skip a Row With SQL Cursor without a Temp Table

Time:11-04

I have a large table in SQL with the following columns:

CompanyId(int) Email(Varchar 255) First_Name(Varchar 50) Last_Name(Varchar 50)
1 [email protected] Jim Halpert
2 [email protected] Bob Vance
1 [email protected] Michael Scott

CompanyId can repeat several times as the companies are attached to various emails.

My employer wants me to find Company emails that belong to a specific type. After several IF statements, the end result would print a message like:

"CompanyId" has [email protected] type email

"CompanyId" has [email protected] type email

My employers told me to use Cursor to find my solution, but once I start my cursor I need to check the CompanyId to see if that id has already been looped and a type found. If the CompanyId has already gone through the cycle, I want to skip it.

This is my code thus far

      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 [email protected]' 
                -- 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

One solution I saw used a temp table to log ids that have been used before, but my employer doesn't want me using temp tables. How Can I Skip a row(an iteration) in MSSQL Cursor based on some condition?

Thank you!

CodePudding user response:

Cursors are slow and inefficient, and are rarely needed.

This doesn't need a cursor at all. A simple filtered join with a group by will suffice

SELECT
  CONCAT(e.CompanyID), ' has email like [email protected]')
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;

Apparently, for some very strange reason, your employers have mandated a cursor, and I'm sure you will tell them all the reasons why not to use them. But here you go anyway:

Your original code is still very convoluted, and you can simplifiy it by using the code above as the SELECT for the cursor.

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 [email protected]');

    FETCH NEXT FROM @crsr
      INTO @CompanyID;
END;

CLOSE @crsr;

CodePudding user response:

I do not like cursors too much, so I am going to propose another possible solution (I think it is a better one, although your employers want a cursor for some strange reason).

Your sample data:

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, '[email protected]')
insert into Table_Email values (2, 2, '[email protected]')
insert into Table_Email values (1, 3, '[email protected]')

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

My proposed query:

SELECT CONVERT(VARCHAR(50), CompanyID)   ' has email like [email protected]' 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

It returns:

1 has email like [email protected]

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

CodePudding user response:

Make CompanyId Distinct in the the Cursor

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
  • Related