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