Sql Server stored procedure that sends emails stored in table - database

I currently have a stored procedure that is sending out emails, but it's getting an error. Also, is there some error checking that I can put in place to ensure the email is sent? I insert data into a table called outbound_email_queue the table is defined as:
CREATE TABLE dbo.outbound_email_queue(
email_id int IDENTITY(1,1) NOT NULL,
out_type varchar(3) NOT NULL,
send_time datetime NULL,
from_addr varchar(100) NOT NULL,
to_addr varchar(254) NOT NULL,
reply_addr varchar(60) NULL,
subject varchar(254) NOT NULL,
letter_body varchar(8000) NOT NULL
The stored procedure I'm using is below.
It gets an error on the Commit statement.
It seems to do the delete, even though I leave out the commit. Even if I get it to execute the procedure, it empties the table but it doesn't seem to send the emails.
CREATE PROCEDURE [dbo].[uspEmail]
as
BEGIN
Declare #email_id as bigint
Declare #email_type as nvarchar(3)
Declare #from_add as nvarchar(100)
Declare #to_add as nvarchar(254)
Declare #reply_add as nvarchar(60)
Declare #email_prof as nvarchar(80)
Declare #MySubject as nvarchar(254)
Declare #Message as nvarchar(MAX)
Declare EmailCursor CURSOR FAST_FORWARD FOR
Select email_id, out_type, from_addr, to_addr, reply_addr, subject,
letter_body from outbound_email_queue
set #email_prof = (select email_profile from system_file)
OPEN EmailCursor
FETCH NEXT FROM EmailCursor INTO #email_id, #email_type, #from_add, #to_add,
#reply_add, #MySubject, #Message
WHILE ##FETCH_STATUS = 0 BEGIN -- send the email
EXEC msdb.dbo.sp_send_dbmail
#profile_name = #email_prof,
#recipients = #to_add,
#reply_to = #reply_add,
#body_format = 'HTML',
#from_address = #from_add,
#subject = #MySubject,
#body = #Message
DELETE FROM OUTBOUND_EMAIL_QUEUE WHERE EMAIL_ID = #email_id
FETCH NEXT FROM EmailCursor INTO #email_id, #email_type, #from_add, #to_add,
#reply_add, #MySubject, #Message
END CLOSE EmailCursor
DEALLOCATE EmailCursor
COMMIT
END
GO

Related

Sending n HTML email with SQL Server with a preview

I have coded an HTML email to send out through SQL Server. It is currently working however it automatically sends out. I am wondering if there is a way for me to review the email in the default client before it gets sent out?
EXEC msdb.dbo.sp_send_dbmail #recipients = 'test#email.com'
, #subject = 'Action Item Test', #body = #body, #body_format='HTML', #reply_to = 'noreply#myserver.com'
I had same request while ago and I put the e-mails to table and sent an notification to reviewer.
It is not my area but if you have e-mail security services you can put your e-mail(which you use in msdb profile) as spam, you can review it and reject/release with no any other extra process. I have used mimecast. It hold all suspected e-mail on the server until you release them.
In my opinion it is not manageable if you do it manually, but if you still want to do it I have some script to you :
Basically : Create a table and put all e-mail details to that table which you want to review. It will send notification to you. If you are happy execute the latest procedure to release the e-mail.
--Create Reviews table
IF OBJECT_ID('EMailReviews') IS NOT NULL
DROP TABLE EMailReviews;
CREATE TABLE EMailReviews
(
Id INT NOT NULL IDENTITY(1,1)
--ProfileName?
,Recipients VARCHAR(MAX)
--,CopyRecipients VARCHAR(MAX)
,EMailSubject NVARCHAR(255)
,Body NVARCHAR(MAX)
,BodyFormat VARCHAR(20)
,ReplyTo VARCHAR(MAX)
,DateReceived DATETIME NOT NULL DEFAULT(SYSDATETIME())
,DateReviewed DATETIME
,Processed BIT NOT NULL DEFAULT(0)
);
--Create Procedure to insert the e-mail to review table and send notification to you.
IF OBJECT_ID('P_PutEMailToReviewQueue') IS NOT NULL
DROP PROCEDURE P_PutEMailToReviewQueue;
GO
CREATE PROCEDURE P_PutEMailToReviewQueue
#Recipients VARCHAR(MAX)
--,#CopyRecipients VARCHAR(MAX)
,#EMailSubject NVARCHAR(255)
,#Body NVARCHAR(MAX)
,#BodyFormat VARCHAR(20)
,#ReplyTo VARCHAR(MAX)
AS
DECLARE #Reviewer VARCHAR(100)='revieweremail#email.com' /*!!Put your e-mail to review!!*/
DECLARE #ReviewerEMailSubject NVARCHAR(MAX)=''
DECLARE #ReviewerEMailBody NVARCHAR(MAX)=''
--Put validations if necessary
INSERT INTO EMailReviews(Recipients,EMailSubject,Body,BodyFormat,ReplyTo)
VALUES (#Recipients,#EMailSubject,#Body,#BodyFormat,#ReplyTo)
DECLARE #Id VARCHAR(100)=CAST(SCOPE_IDENTITY()AS VARCHAR)
SET #ReviewerEMailBody = 'E-Mail Information below:'
+'<br>'
+'ID='+#Id
+'<br>'
+'Subject:'+ISNULL(#EMailSubject,'')
+'<br>'
+'Body:'+ISNULL(#Body,'')
+'<br>'
+'Recipients:'+ISNULL(#Recipients,'')
+'<br>'
+'ReplyTo:'+ISNULL(#ReplyTo,'')
+'<br>'
+'<br>'
+'<br>'
+'<br>'
+'After your review if you want to release the e-mail you need to run following script:'
+'<br>'
+'P_ReleaseEmail #Id ='+#Id
SET #ReviewerEMailSubject = 'Number '+#Id+' e-mail has been recieved. Review required!'
EXEC msdb.dbo.sp_send_dbmail
#recipients = #Reviewer
, #subject = #ReviewerEMailSubject
, #body = #ReviewerEMailBody
, #body_format ='HTML'
GO
/*
P_PutEMailToReviewQueue
#Recipients ='test#test.com'
,#EMailSubject ='Action Item Test'
,#Body ='BLA BLA'
,#BodyFormat ='HTML'
,#ReplyTo ='noreply#test.com'
*/
--Create procedure to release the e-mail.
IF OBJECT_ID('P_ReleaseEmail') IS NOT NULL
DROP PROCEDURE P_ReleaseEmail;
GO
CREATE PROCEDURE P_ReleaseEmail
#Id INT
AS
DECLARE
#Recipients VARCHAR(MAX)
--,#CopyRecipients VARCHAR(MAX)
,#EMailSubject NVARCHAR(255)
,#Body NVARCHAR(MAX)
,#BodyFormat VARCHAR(20)
,#ReplyTo VARCHAR(MAX)
SELECT #Recipients=Recipients
,#EMailSubject=EMailSubject
,#Body=Body
,#BodyFormat=BodyFormat
,#ReplyTo=ReplyTo
FROM EMailReviews
WHERE Id=#Id
EXEC msdb.dbo.sp_send_dbmail
#recipients = #Recipients
, #subject = #EMailSubject
, #body = #Body
, #body_format = #BodyFormat
, #reply_to = #ReplyTo
UPDATE EMailReviews SET DateReviewed=GETDATE()
,Processed=1
WHERE Id=#Id
GO
/*
P_ReleaseEmail
#Id =1;
select * from EMailReviews;
*/

SQL Server: sp_xml_preparedocument not inserting/updating data in db from xml

I have created a store procedure by which i am trying to insert data from xml. the data is not getting inserted or updated in db. i am not being able to
capture the problem why data is not getting inserted. so please have look at my code with table structure and SP body and guide me what to change in code to
work my SP properly.
Table Structure
CREATE TABLE [dbo].[TickerBrokerStandardDateLineitemValue] (
[TabName] VARCHAR (MAX) NULL,
[StandardDate] VARCHAR (MAX) NULL,
[XFundCode] VARCHAR (MAX) NULL,
[BRTab] VARCHAR (MAX) NULL,
[BRLineItem] VARCHAR (MAX) NULL,
[StandardLineItem] VARCHAR (MAX) NULL,
[StandardValue] DECIMAL (18, 2) NULL,
[ActualProvidedByCompany] VARCHAR (MAX) NULL,
[ID] INT IDENTITY (1, 1) NOT NULL,
CONSTRAINT [PK_TickerBrokerStandardDateLineitemValue] PRIMARY KEY CLUSTERED ([ID] ASC)
);
Store Procedure
CREATE PROCEDURE [dbo].[uspInsertBoggyXmlData]
(
#inputxml VARCHAR,
#Status INT OUTPUT
)
As
Begin
Declare #intCntr as INT
Declare #intError as INT
Declare #XMLFormat as INT
Declare #TabName as Varchar(MAX)
Declare #StandardDate as Varchar(MAX)
Declare #XFundCode as Varchar(MAX)
Declare #BRTab as Varchar(MAX)
Declare #BRLineItem as Varchar(MAX)
Declare #StandardLineItem as Varchar(MAX)
Declare #StandardValue as Varchar(MAX)
Declare #ActualProvidedByCompany as Varchar(MAX)
Exec sp_xml_preparedocument #XMLFormat OUTPUT, #inputxml
-- Create Cursor from XML Table
Declare CurRecord
Cursor For
Select TabName,StandardDate,XFundCode,BRTab,BRLineItem,StandardLineItem,StandardValue,ActualProvidedByCompany
From Openxml (#XMLFormat, '/TickerBrokerStandardDateLineitem/TickerBrokerStandardDateLineitemValues/TickerBrokerStandardDateLineitemValue', 3)
With (
TabName Varchar(MAX),
StandardDate Varchar(MAX),
XFundCode Varchar(MAX),
BRTab Varchar(MAX),
BRLineItem Varchar(MAX),
StandardLineItem Varchar(MAX),
StandardValue Varchar(MAX),
ActualProvidedByCompany Varchar(MAX)
)
BEGIN TRANSACTION
SET #Status=0
SET #intCntr =0
Open CurRecord
Fetch Next From CurRecord Into #TabName,#StandardDate,#XFundCode,#BRTab,#BRLineItem,#StandardLineItem,#StandardValue,#ActualProvidedByCompany
While (##Fetch_Status=0)
Begin
IF(#StandardValue='')
BEGIN
SET #StandardValue = 0
END
SELECT #intCntr = COUNT(*) FROM TickerBrokerStandardDateLineitemValue
WHERE TabName=#TabName AND StandardDate=#StandardDate AND StandardLineItem=#StandardLineItem
IF(#intCntr=0)
BEGIN
INSERT INTO TickerBrokerStandardDateLineitemValue (TabName,StandardDate,XFundCode,BRTab,BRLineItem,StandardLineItem,StandardValue,ActualProvidedByCompany)
VALUES(#TabName,#StandardDate,#XFundCode,#BRTab,#BRLineItem,#StandardLineItem,CAST(#StandardValue AS DECIMAL),#ActualProvidedByCompany)
END
ELSE
BEGIN
UPDATE TickerBrokerStandardDateLineitemValue
SET XFundCode = #XFundCode,
BRTab = #BRTab,
BRLineItem = #BRLineItem,
StandardValue = CAST(#StandardValue AS DECIMAL),
ActualProvidedByCompany = #ActualProvidedByCompany
WHERE TabName=#TabName AND StandardDate=#StandardDate AND StandardLineItem=#StandardLineItem
END
SELECT #intError = ##error if #intError <> 0 Begin ROLLBACK TRANSACTION return #intError End
Fetch Next From CurRecord Into #TabName,#StandardDate,#XFundCode,#BRTab,#BRLineItem,#StandardLineItem,#StandardValue,#ActualProvidedByCompany
End
Close CurRecord
Deallocate CurRecord
select #intError = ##error if #intError <> 0 Begin ROLLBACK TRANSACTION return #intError End
SET #Status=1
COMMIT TRANSACTION
SELECT #Status
END
This way i am calling SP
Declare #inputxml VARCHAR(MAX)
declare #Status int
set #inputxml='<?xml version="1.0" encoding="utf-8"?>
<TickerBrokerStandardDateLineitem>
<Ticker />
<TickerID />
<TickerBrokerStandardDateLineitemValues>
<TickerBrokerStandardDateLineitemValue>
<TabName>CM Model101</TabName>
<StandardDate>2010 FY</StandardDate>
<XFundCode>TRIN0001</XFundCode>
<BRTab></BRTab>
<BRLineItem></BRLineItem>
<StandardLineItem>Net Revenue</StandardLineItem>
<StandardValue>1608.7</StandardValue>
<ActualProvidedByCompany>NO</ActualProvidedByCompany>
</TickerBrokerStandardDateLineitemValue>
<TickerBrokerStandardDateLineitemValue>
<TabName>JP Bank</TabName>
<StandardDate>3Q 2018</StandardDate>
<XFundCode>RD_015</XFundCode>
<BRTab></BRTab>
<BRLineItem></BRLineItem>
<StandardLineItem>Days of Inventory</StandardLineItem>
<StandardValue></StandardValue>
<ActualProvidedByCompany>YES</ActualProvidedByCompany>
</TickerBrokerStandardDateLineitemValue>
<TickerBrokerStandardDateLineitemValue>
<TabName>ZZZZZZZZZZZ</TabName>
<StandardDate>3Q 2018</StandardDate>
<XFundCode>RD_015</XFundCode>
<BRTab></BRTab>
<BRLineItem>991</BRLineItem>
<StandardLineItem>Days of Inventory</StandardLineItem>
<StandardValue>77.30</StandardValue>
<ActualProvidedByCompany>YES</ActualProvidedByCompany>
</TickerBrokerStandardDateLineitemValue>
</TickerBrokerStandardDateLineitemValues>
</TickerBrokerStandardDateLineitem>'
EXEC [dbo].[uspInsertBoggyXmlData] #inputxml, #Status output
Select #Status
Your first procedure could be filing or not returning anything. You can check that.
begin try
begin tran
Exec sp_xml_preparedocument #XMLFormat OUTPUT, #inputxml
commit
end try
begin catch
if ##trancount > 0 rollback tran
select ERROR_MESSAGE()
;throw
end catch
Then of course, before the rest of you code, you could check #XMLFormat to make sure it isn't NULL
Similarly, I would wrap your cursor attempts in their own transaction for each iteration with error handling. Erland Sommarskog has blogged extensively on error handling so I won't duplicate it here.

T-SQL stuck on scope_identity return from stored procedure

I'm sure this is a easy fix, but I can't find an answer here. Been many a years since I have written stored procedures..
These are my procedures:
This first one works, and returns the newly created Id.
ALTER PROCEDURE [dbo].[sp_CreateBytegymType]
#Name NVARCHAR(200),
#Type NCHAR(1),
#Description NVARCHAR(MAX) NULL,
#Comment NVARCHAR(MAX) NULL,
#Source NVARCHAR(MAX) NULL,
#BtId INT OUTPUT
AS
BEGIN
SET NOCOUNT ON
INSERT INTO BytegymType
VALUES (#Name, #Type, #Description, #Comment, #Source)
SET #BtId = CAST(SCOPE_IDENTITY() AS INT)
END
The second one calls the first one:
ALTER PROCEDURE [dbo].[sp_CreateMuscle]
#Name NVARCHAR(200),
#Type NCHAR(1),
#Description NVARCHAR(MAX) NULL,
#Comment NVARCHAR(MAX) NULL,
#Source NVARCHAR(MAX) NULL,
#Group NVARCHAR(20) NULL
AS
BEGIN
DECLARE #BtId int
EXEC sp_CreateBytegymType
#Name = #Name,
#Type = #Type,
#Description = #Description,
#Comment = #Comment,
#Source = #Source,
#BtId = #BtId
INSERT INTO Muscle
VALUES (#BtId, #Group)
END
I get the following error:
Msg 515, Level 16, State 2, Procedure sp_CreateMuscle, Line 20 [Batch Start Line 2]
Cannot insert the value NULL into column 'BtId'
Seems I'm not keeping the #BtId value. Do I need to put it into a new value after executing the sp_CreateBytegymType?
Also I would like to do this in a transactional manner. So if the the insert into Muscle fails, it should rollback the stored procedure insert.
You need to add OUTPUT:
exec sp_CreateBytegymType
#Name = #Name,
#Type = #Type,
#Description = #Description,
#Comment = #Comment,
#Source = #Source,
#BtId = #BtId OUTPUT;
Also prefixing user defined stored procedures with sp_ is not advised. Related article: Is the sp_ prefix still a no-no?

Response data truncation when running SP through job

Subject:
I've got a report that I create every month. The creation of report consists of 2 steps:
Get an XML from our service and store it in DB;
Parse XML and create file.
For the last few month I've created report in manual mode. And now I want to automate this stuff.
But here comes a
Problem:
The second step (parsing XML and file creation) runs like a charm, but with first step I'm observing weird behaviour.
I got Stored Procedure which gets XML:
ALTER PROCEDURE [Structure].[GetXML]
#LastActDate date,
#CurActDate date
AS
BEGIN
SET NOCOUNT ON;
begining:
DECLARE #URI varchar(2000),
#methodName varchar(50),
#objectID int,
#hResult int,
#setTimeouts nvarchar(255),
#serv nvarchar(255) = 'http://example.com/docs/',
#result nvarchar(max) = ''
DECLARE #t TABLE(Resp nvarchar(max))
declare #timeStamp nvarchar(50) = convert(nvarchar(50),CURRENT_TIMESTAMP,127)
declare #CurDate date = dateadd(day,0,getdate())
--EXEC #hResult = sp_OACreate 'WinHttp.WinHttpRequest.5.1', #objectID OUT
EXEC #hResult = sp_OACreate 'MSXML2.XMLHTTP', #ObjectID OUT
SELECT #URI = #serv + '.newchange?ds='+CONVERT(nvarchar(10),#LastActDate,104)+'&df='+CONVERT(nvarchar(10),#CurActDate,104)+'&pardaily=1',
#methodName='GET',
#setTimeouts = 'setTimeouts(9000,90000,900000,9000000)'
EXEC #hResult = sp_OAMethod #objectID, 'open', null, #methodName, #URI, 'false'
EXEC #hResult = sp_OAMethod #objectID, #setTimeouts
EXEC #hResult = sp_OAMethod #objectID, 'send', null
INSERT INTO #t
EXEC sp_OAGetProperty #objectID, 'responseText'
SELECT top 1 #result = Resp
FROM #t
if #result is null
begin
delete from #t
exec sp_OAGetErrorInfo #objectID
exec sp_OADestroy #objectID
goto begining
end
else
begin
INSERT INTO Structure.MonthlyRow
SELECT #timeStamp, #result
end
END
When I run this SP like
EXEC [Structure].[GetXML] '2016-06-01', '2016-07-01'
I got a row in Structure.MonthlyRow table with correct timestamp and response (the average length is ~70k symbols)
Here is creation script of a table:
CREATE TABLE Structure.MonthlyRow(
[timestamp] nvarchar(50) NOT NULL,
[RowResp] nvarchar(max) NULL,
CONSTRAINT [PK_dDayly] PRIMARY KEY CLUSTERED ([timestamp] DESC))
If I create a job that launch this SP I get a row in table with results, and the length of result is 512 symbols! It is a proper part of XML that looks like it was truncated from nvarchar(max) to nvarchar(512), but I have no variables or table columns with length of 512 that are used.
What have I tried:
Run as user with my account in Job Step properties;
Job was started by schedule or manually;
Add WITH EXECUTE AS OWNER in SP;
Tried using WinHttp.WinHttpRequest.5.1 and MSXML2.XMLHTTP.
Question:
What possibly could be a problem? Why I am getting correct results when I run my SP manually, and got only 512 symbols of response when run SP as job step?
Note:
Yes, I know that getting XML from web-service is better handled by PHP, C# or even PowerShell and if I can not find a solution I will use one of them.
add this line at the top of your sp or in the job before EXEC of your sp
SET TEXTSIZE 2147483647;
the problem is that jobs set a default
SET TEXTSIZE 1024
this limits data returned to 1024 chars (512 for nchars)

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;

Resources