Automatic birthday email send out of MS SQL - sql-server

I created a table named Customers with the following columns:
ID
Name
Birthdate
Email
Manager
Entries
Name Birthdate Email Manager
---- ---------- -------------- -------
Jan 1986-05-15 jan#gmail.com None
Koos 1986-07-24 Koos#gmail.com 1
Sam 1991-07-24 Sam#gmail.com 1
Meaning that Koos and Sam's manager is Jan.
Now what I want to do is to determine if someone has a birthday today.
Create PROCEDURE [dbo].[EmailData]
AS
SET NOCOUNT ON
BEGIN
Declare #Date date
SELECT GETDATE(), Month(GetDate()), Day(GetDate()), Year(GetDate())
select
a.ID, a.Name,
a.BirthDate as EmpBirthday,
b.Name as Manager,
b.Email as ManEmail
from dbo.Customers a
inner join dbo.Customers b
on a.Manager = b.ID
where Month(a.BirthDate) = Month(GetDate())
and Day(a.BirthDate) = Day(GetDate())
END
Go
The above works fine, I get the results I want 100%.
What I want to do now is send the email of each employee whose birthday it is today to the employee's manager.
I came up with this but it does not seem to work and I have no idea why:
Create PROCEDURE EmailData
AS
BEGIN
Declare #email nvarchar(128)
Declare #Date date
SELECT GETDATE(), Month(GetDate()), Day(GetDate()), Year(GetDate())
DECLARE rcpt_cursor CURSOR FOR
select
b.Email
from dbo.Customers a
inner join dbo.Customers b
on a.Manager = b.ID
where Month(a.BirthDate) = Month(GetDate())
and Day(a.BirthDate) = Day(GetDate())
OPEN rcpt_cursor
FETCH NEXT FROM rcpt_cursor INTO #email
DECLARE #ServerAddr nvarchar(128)
Set #ServerAddr = 'smtp.gmail.com'
DECLARE #From nvarchar(128)
Set #From = 'My Email Address'
DECLARE #Bodytext nvarchar(512)
Set #BodyText = 'Your Employees have a birthday today.'
DECLARE #User nvarchar(128)
Set #User = 'My Email Address'
DECLARE #Password nvarchar(128)
Set #Password = 'My Password'
DECLARE #SSL int
Set #SSL = 0
DECLARE #Port int
Set #Port = 587
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #email
DECLARE #subject nvarchar(255)
SELECT #Subject = 'Happy Birthday' + #email
EXEC EmailData #ServerAddr,
#from, #email, #subject, #BodyText, #User, #Password, #SSL, #Port
FETCH NEXT FROM rcpt_cursor
INTO #email
END
CLOSE rcpt_cursor
DEALLOCATE rcpt_cursor
End
Go
Any help will be appreciated.

Ok I cracked it
First of all I set up a Database mail account
Then I wrote a procedure
USE [Customers]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[usp_SendEmail]
AS
Declare #email nvarchar(128)
Declare #name nvarchar(128)
Declare #Date date
SELECT GETDATE(), Month(GetDate()), Day(GetDate()), Year(GetDate())
DECLARE rcpt_cursor CURSOR FOR
select
a.Name,
b.Email
from dbo.Customers a
inner join dbo.Customers b
on a.Manager = b.ID
where Month(a.BirthDate) = Month(GetDate())
and Day(a.BirthDate) = Day(GetDate())
OPEN rcpt_cursor
FETCH NEXT FROM rcpt_cursor
INTO #name,#email
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #email
Declare #subject nvarchar(255)
Declare #Bodytext nvarchar(512)
Set #BodyText = #Name + '' + ' has a birthday today. Please congrad him'
Set #Subject = 'Employee Birthday'
exec msdb.dbo.sp_send_dbmail
#Profile_Name = 'HappyBirthday',
#Recipients = #email,
#Body = #BodyText,
#Subject = #Subject
FETCH NEXT FROM rcpt_cursor
INTO #name,#email
END
CLOSE rcpt_cursor
DEALLOCATE rcpt_cursor
Go
Exec [usp_SendEmail]
The procedure now run through the hole customer table check if there are birthday dates that much and send the email to the managers whose employees have a birthday.

Related

Question about sending automatic bulk mail in SQL Server

I have a database in SQL Server that has a user table with 3 columns: name, date of birth and email.
I have a stored procedure that goes through this table looking for who is having a birthday today, and sends him a congratulatory email, so far it works at 100.
However, I want to add within the same procedure that I send an email to all the users of the database (except the one with a birthday), notifying them that x person are having a birthday today.
This is my current SQL Server code:
CREATE PROCEDURE spSendEmail
AS
DECLARE #email nvarchar(128)
DECLARE #name nvarchar(128)
DECLARE #Date date
SELECT
GETDATE(),
MONTH(GETDATE()), DAY(GETDATE()), YEAR(GETDATE())
DECLARE email_cursor CURSOR FOR
SELECT u.name, u.email
FROM dbo.users u
WHERE MONTH(u.dateofbirth) = MONTH(GETDATE())
AND DAY(u.dateofbirth) = DAY(GETDATE())
OPEN email_cursor
FETCH NEXT FROM email_cursor INTO #name, #email
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #email
DECLARE #subject nvarchar(255)
DECLARE #Bodytext nvarchar(512)
SET #BodyText = #Name + '' + 'we wish you happy birthday'
SET #Subject = 'Happy Birthday'
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'Birthday Alert',
#recipients = #email,
#body = #Bodytext,
#subject = #subject;
FETCH NEXT FROM email_cursor INTO #name, #email
END
CLOSE email_cursor
DEALLOCATE email_cursor
CREATE PROCEDURE spSendEmail AS
DECLARE #email nvarchar(128)
DECLARE #name nvarchar(128)
DECLARE #Date date
DECLARE #Num_Users_Having_Birthday varchar(100) = (SELECT COUNT(*) FROM dbo.users WHERE MONTH(u.dateofbirth) = MONTH(GETDATE())
AND DAY(u.dateofbirth) = DAY(GETDATE()))
SELECT
GETDATE(),
MONTH(GETDATE()), DAY(GETDATE()), YEAR(GETDATE())
DECLARE email_cursor CURSOR FOR
SELECT u.name, u.email
FROM dbo.users u
WHERE MONTH(u.dateofbirth) <> MONTH(GETDATE())
AND DAY(u.dateofbirth) <> DAY(GETDATE())
OPEN email_cursor
FETCH NEXT FROM email_cursor INTO #name, #email
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #email
DECLARE #subject nvarchar(255)
DECLARE #Bodytext nvarchar(512)
SET #BodyText = #Name
SET #Subject = 'Number of People having Birthday today:' + #Num_Users_Having_Birthday
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'Birthday Alert',
#recipients = #email,
#body = #Bodytext,
#subject = #subject;
FETCH NEXT FROM email_cursor INTO #name, #email
END
CLOSE email_cursor
DEALLOCATE email_cursor
Try this:
CREATE PROCEDURE spSendEmail
AS
BEGIN
DECLARE #email nvarchar(128)
DECLARE #name nvarchar(128)
DECLARE #has_birthday BIT
DECLARE #Date date
DECLARE #birthday_boys INT -- update this to
SELECT
GETDATE(),
MONTH(GETDATE()), DAY(GETDATE()), YEAR(GETDATE())
IF(OBJECT_ID('tempdb..users') IS NOT NULL)
DROP TABLE #users;
-- For Sql Server version >= 2016: DROP TABLE IF EXISTS #users
SELECT u.name, u.email
, IIF(MONTH(u.dateofbirth) = MONTH(GETDATE()) AND DAY(u.dateofbirth) = DAY(GETDATE()),1,0) has_birthday
INTO #users
FROM dbo.users u
--Get all birthday boys list
SELECT #birthday_boys = COUNT(u.name)
FROM #users u
WHERE has_birthday = 1
DECLARE email_cursor CURSOR FOR
SELECT u.name, u.email, u.has_birthday
FROM #users
OPEN email_cursor
FETCH NEXT FROM email_cursor INTO #name, #email, #has_birthday
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #email
DECLARE #subject nvarchar(255)
DECLARE #Bodytext nvarchar(512)
SET #BodyText = IIF(#has_birthday = 1, #Name + '' + 'we wish you happy birthday', CONCAT('Birthday boys'' count is ', #birthday_boys))
SET #Subject = IIF(#has_birthday = 1, 'Happy Birthday', 'Birthday Boys'' Count')
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'Birthday Alert',
#recipients = #email,
#body = #BodyText,
#subject = #subject
FETCH NEXT FROM email_cursor INTO #name, #email
END
CLOSE email_cursor
DEALLOCATE email_cursor
IF(OBJECT_ID('tempdb..users') IS NOT NULL)
DROP TABLE #users;
-- For Sql Server version >= 2016: DROP TABLE IF EXISTS #users
END

How to make an automatic reminder with jobs with TSQL?

Is there a way to make an automatic reminder via email when those dates comes and send the rows info from the same table, for example:
In the following table we have to columns with different dates, DUEDATE need to be a reminder from expedition and EXPDV is another reminder 15 day before DUEDATE, So when one of those dates from any row comes I want to make to send and automatic Reminder via email with the row info (PSLOT,PSQRCV,ETC) From that table , any ideas?
USE [database_name]
GO
SET NOCOUNT ON;
DECLARE #count INT =
(
SELECT COUNT(*) FROM dbo.TableName
WHERE DATEDIFF(DAY, EXPDV, DUEDATE) = 15
)
IF #count > 0
BEGIN
DECLARE #html NVARCHAR(max)
, #subject NVARCHAR(max) = 'Your email subject here'
, #email NVARCHAR(255)
, #PSLOT NVARCHAR(255)
, #PSQRCV NVARCHAR(255)
, #ETC NVARCHAR(255)
DECLARE send_remider CURSOR FOR
SELECT email, PSLOT,PSQRCV,ETC FROM dbo.TableName
WHERE DATEDIFF(DAY, EXPDV, DUEDATE) = 15
OPEN send_remider
FETCH NEXT FROM send_remider INTO #email, #PSLOT, #PSLOT, #ETC
WHILE ##FETCH_STATUS = 0
BEGIN
SET #html = 'Your message body text here....' + #PSLOT, #PSQRCV, #ETC
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'profile_name'
, #recipients = #email
, #subject = #subject
, #body = #html
, #body_format = 'HTML'
FETCH NEXT FROM send_remider INTO #email, #PSLOT, #PSLOT, #ETC
END
CLOSE send_remider
DEALLOCATE send_remider
GO
You can create a job and run this cursor. I haven't tested it. So, you should make yourself a recipient during testing.

SQL Insert multiple records to Triggered table

On SQL Agent job I'm downloading data from main table (X). If the status equal 'WW' or 'WXY' I just want to all records insert into another table (Y) where I have created a trigger. Could you give me advise what should I change in my trigger definition? I receive error message when I try insert multiple records to DB2.Y table (agent job failed)
First code (downloading new data)
DECLARE #STARTTIME datetime
DECLARE #TIME datetime
DECLARE #ENDTIME datetime
SET #TIME=(SELECT MAX(Time) FROM DB2.Y)
SET #STARTTIME=(select dateadd(hour,1,getdate()))
SET #ENDTIME=(SELECT MAX(TIME) FROM DB1.X where TIME is not null)
IF #TIME = #ENDTIME
BEGIN
TRUNCATE TABLE DB2.Y;
INSERT INTO DB2.Y (Time) Values (#TIME)
END
ELSE
BEGIN
TRUNCATE TABLE DB2.Y
INSERT INTO DB2.Y ([Serv],[Status])
SELECT [SERV],[STATUS] FROM DB1.X WHERE TIME > #TIME and [SERV]='Description' and ([STATUS]='WW' or [STATUS]='WXY') ;
UPDATE DB2.Y
SET [Time]= #ENDTIME
END
The trigger code:
USE DB2
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[TriggerName] ON Y
AFTER INSERT AS
DECLARE #SERV varchar(40)
DECLARE #STATUS varchar(3)
SET #SERV=(SELECT [SERV] FROM inserted)
SET #STATUS=(SELECT [STATUS] FROM inserted)
IF #STATUS in ('WW', 'WXY')
BEGIN
DECLARE #MSG varchar(500)
SET #MSG = 'Job "' + #SERV + '" failed!!!'
EXEC msdb.dbo.sp_send_dbmail #recipients=N'myemail#domain.com', #body= #MSG, #subject = #MSG, #profile_name = 'profilename'
END
INSERTED can contain multiple rows for multi-row INSERTs - it runs once per operation, not once per row.
Try a trigger like this instead:
USE DB2
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[TriggerName] ON Y
AFTER INSERT AS
DECLARE #SERV varchar(40)
DECLARE #STATUS varchar(3)
IF EXISTS(SELECT [Status] FROM Inserted WHERE [STATUS] in ('WW', 'WXY'))
BEGIN
DECLARE #MSG varchar(8000)
INSERT INTO JobLog(Serv, Status)
SELECT Serv, Status FROM Inserted WHERE [STATUS] in ('WW', 'WXY')
SET #MSG = CAST(##ROWCOUNT as nvarchar) + 'Job(s) failed - see log for details'
EXEC msdb.dbo.sp_send_dbmail #recipients=N'myemail#domain.com', #body= #MSG, #subject = #MSG, #profile_name = 'profilename'
END
SELECT
Try something like this...
CREATE TRIGGER [dbo].[TriggerName] ON Y
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SERV varchar(40),#STATUS varchar(3), #MSG varchar(500)
Select [SERV], [STATUS] INTO #Temp
FROM inserted
Where [STATUS] IN ('WW', 'WXY')
Declare Cur CURSOR FOR
SELECT [SERV], [STATUS] FROM #Temp
OPEN Cur
FETCH NEXT FROM Cur INTO #SERV, #STATUS
WHILE ##FETCH_STATUS = 0
BEGIN
SET #MSG = 'Job "' + #SERV + '" failed!!!'
EXEC msdb.dbo.sp_send_dbmail #recipients=N'myemail#domain.com'
, #body= #MSG
, #subject = #MSG
, #profile_name = 'profilename'
FETCH NEXT FROM Cur INTO #SERV, #STATUS
END
CLOSE Cur
DEALLOCATE Cur
END
You could use a cursor to resolve your error. The cursor below iterates over all inserted records with a STATUS of either WW or WXY and sends out an email for each of them.
DECLARE error_cursor CURSOR FOR
SELECT [SERV],[STATUS] FROM inserted
WHERE [STATUS] in ('WW', 'WXY')
OPEN error_cursor
FETCH NEXT FROM error_cursor
INTO #SERV, #STATUS
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE MSG varchar(500)
SET #MSG = 'Job "' + #SERV + '" failed!!!'
EXEC msdb.dbo.sp_send_dbmail #recipients=N'myemail#domain.com', #body= #MSG, #subject = #MSG, #profile_name = 'profilename'
FETCH NEXT FROM vendor_cursor
INTO #SERV, #STATUS
END
CLOSE error_cursor;
DEALLOCATE error_cursor;

sp_send_dbmail just partially working

Ive got the next query, and the problem is that it work for mail 1 and 2, but not to 3 and 4 (and 3,4 exists in the select table). some idea?
[I looked for help and i guess that the right way to do it, it's cursor, but I did not make it also with cursor).
ALTER PROCEDURE [DBMAIL].[Mail_FOR_USER]
#Mail_1 NVARCHAR (100),
#Mail_2 NVARCHAR (100),
#Mail_3 NVARCHAR (100),
#Mail_4 NVARCHAR (100)
AS
BEGIN TRY
EXEC sp_configure 'show advanced', 1;
RECONFIGURE;
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'Database Mail XPs', 1;
RECONFIGURE
DECLARE
#ID INT=1,
#RN INT,
#xCol XML,
#XML_Creation_Date DATETIME ,
#File_Path NVARCHAR(200),
#USER_1 NVARCHAR(100),
#USER_2 NVARCHAR(100),
#USER_3 NVARCHAR(100),
#USER_4 NVARCHAR(100),
#Recipients_List NVARCHAR(MAX);
---============================================
WHILE #ID<= (SELECT MAX (ID) FROM Report_FOR_Users)
BEGIN
SELECT [ID], [RN], [xCol], [XML_Creation_Date], [File_Path], [USER_1], [USER_2], [USER_3], [USER_4]
FROM Report_FOR_Users WHERE ID=#ID
SET #File_Path= (SELECT File_Path FROM Report_FOR_Users WHERE ID=#ID)
SET #Mail_1= CASE
WHEN (SELECT USER_1 FROM Report_FOR_Users WHERE ID=#ID) IS NULL THEN ' '
ELSE #Mail_1
END
SET #Mail_2= CASE
WHEN (SELECT USER_2 FROM Report_FOR_Users WHERE ID=#ID) IS NULL THEN ' '
ELSE #Mail_2
END
SET #Mail_3= CASE
WHEN (SELECT USER_3 FROM Report_FOR_Users WHERE ID=#ID) IS NULL THEN ' '
ELSE #Mail_3
END
SET #Mail_4= CASE
WHEN (SELECT USER_4 FROM Report_FOR_Users WHERE ID=#ID) IS NULL THEN ' '
ELSE #Mail_4
END
SET #Recipients_List= #Mail_1+';'+#Mail_2+';'+#Mail_3+';'+#Mail_4
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'SEmailProfile',
#recipients = #Recipients_List,
#body = 'ola?',
#subject = 'Hopa',
#file_attachments = #File_Path;
SET #ID=#ID+1
END
ALTER PROCEDURE [DBMAIL].[Mail_FOR_USER]
(
#Mail_1 NVARCHAR(100),
#Mail_2 NVARCHAR(100),
#Mail_3 NVARCHAR(100),
#Mail_4 NVARCHAR(100)
)
AS BEGIN
SET NOCOUNT ON;
DECLARE
#UserID INT
, #File_Path NVARCHAR(200)
, #USER_1 NVARCHAR(100)
, #USER_2 NVARCHAR(100)
, #USER_3 NVARCHAR(100)
, #USER_4 NVARCHAR(100)
DECLARE cur CURSOR FAST_FORWARD READ_ONLY LOCAL FOR
SELECT ID, File_Path
FROM dbo.Report_FOR_Users
OPEN cur
FETCH NEXT FROM cur INTO #UserID, #File_Path
WHILE ##FETCH_STATUS = 0 BEGIN
SELECT
#USER_1 = CASE WHEN USER_1 IS NOT NULL THEN #Mail_1 END
, #USER_2 = CASE WHEN USER_2 IS NOT NULL THEN #Mail_2 END
, #USER_3 = CASE WHEN USER_3 IS NOT NULL THEN #Mail_3 END
, #USER_4 = CASE WHEN USER_4 IS NOT NULL THEN #Mail_4 END
FROM dbo.Report_FOR_Users
WHERE ID = #ID
SELECT #Recipients_List = STUFF((
SELECT ';' + val
FROM (
VALUES (#USER_1), (#USER_2), (#USER_3), (#USER_4)
) t(val)
WHERE t.val IS NOT NULL
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'SEmailProfile',
#recipients = #Recipients_List,
#body = 'ola?',
#subject = 'Hopa',
#file_attachments = #File_Path
FETCH NEXT FROM cur INTO #UserID, #File_Path
END
CLOSE cur
DEALLOCATE cur
END

Sending Database emails to multiple recipients while tweaking the body

i need to send emails to approx, twenty recipients each day, i build a temp table with two columns
Email_Body
Email_address
there are at max 50 rows in the table each day
i want to loop though this table and execute sp_send_email
EXEC msdb.dbo.sp_send_dbmail
#Profile_name = 'DBA',
#recipients = #email_address,
#body = #Email_Body,
#subject = 'Test Email'
is there a way to do this without a cursor?
any links to an example would be appreciated, i have searched and can't find such an example. i am sure it is a very common process.
You might try the solution found here.
I think this code sample would help:
while (#count <=(select COUNT(*) from #table))
begin
select top 1 #Recepient_Email=Emp_Email,#mailBody=body from #table where ID=#count
EXEC msdb.dbo.sp_send_dbmail
#profile_name='Profile1',
#recipients=#Recepient_Email,
#subject = 'This is subject of test Email'
#body = #mailbody,
#body_format = 'HTML'
set #count =#count +1
END
--Email Campaign.
declare #HtmlModel varchar(max)
declare #HtmlBody varchar(max)
SELECT #HtmlModel = EmailBody
FROM EmailCampaigns
WHERE ID = 1
--a different process will have created an awesome looking well formed html with our known
--placeholders for [FirstName] [LastName] [Phone] [Email] that might ber substituted for individualization
--PRINT #HtmlModel;
SET #HtmlBody=''
declare
#mailID int,
#id int,
#first varchar(64),
#last varchar(64),
#phone varchar(64),
#email varchar(64)
declare c1 cursor for
--################################
SELECT ID,
ISNULL(FirstName,'Friend') AS FirstName,
ISNULL(LastName,'') AS LastName,
ISNULL(Phone,''),
ISNULL(Email ,'')
--the row_number is so that if i put your name in the database five times, you only get a single email.
FROM (
select ROW_NUMBER() over (partition by email order by email,len(firstname)desc,len(lastname)desc ) AS RW, *
from RawContacts
where email <> '') x
WHERE RW = 1
--this WHERe stays in place until we are ready to go LIVE with the email.
and email IN('lowell#somedomain.com','otherReviewer#somedomain.com')
--################################
open c1
fetch next from c1 into #id,#first,#last,#phone,#email
While ##fetch_status <> -1
begin
SET #HtmlBody = REPLACE(#HTMLModel,'[FirstName]',#first)
SET #HtmlBody = REPLACE(#HtmlBody,'[LastName]', #last)
SET #HtmlBody = REPLACE(#HtmlBody,'[Phone]', #phone)
SET #HtmlBody = REPLACE(#HtmlBody,'[Email]', #email)
--EXEC msdb.dbo.sp_send_dbmail
#Profile_name = 'Database Mail Profile Name',
#recipients=#email,
#subject = 'Our non profits Call for Volunteers',
#body = #HtmlBody,
#body_format = 'HTML',
#mailitem_id = #mailID OUTPUT
--#body_format = 'TEXT'
INSERT INTO CampaignRecipients(Campaign,RawContactID,MailSentID,MailSentDate)
SELECT 1,#id,#mailID,GETDATE()
fetch next from c1 into #id,#first,#last,#phone,#email
end
close c1
deallocate c1
GO

Resources