Introduction
As I covered in my previous article, there are many limitations of the subscription feature that comes with SQL Reporting Services 2000 and 2005 Standard Editions. I dug into the RS database and came across a way to push my information into the subscription, call it and then leave as if I was never there. After my initial article, I came across several ways to improve its’ stability and performance. The following method/procedure is much more stable.
Although I received many requests to handle multiple parameters, I quickly realized that there are countless ways to address this and it really depends on your personal preference and each report’s possible requirements.
- Are some or all parameters set based upon other parameters?
- Are some or all parameters static for all or some instances of the particular subscription run?
- What parameters are defined in a set table, or come from other sources in your database?
Another request was to handle the “File Share” delivery method. To be quite honest, the username and password encryption has me stumped. So if any of you have a suggestion or solution, let us all know.
However, I did make a few key improvements to the original procedure.
These Include:
- Comprehensive error handling
- A more efficient/stable method of updating the subscription settings and returning them to the original state.
- A method that allows the naming of your procedures, therefore reducing the headaches of administration and maintenance.
- Better commented code for your benefit.
- The ability to specify the rendering format of the report.
- These may be different depending on the installation and configuration of your server, but these are listed in the "reportServer.config" file.
- This file is located in a folder similar to "C:\Program Files\Microsoft SQL Server\MSSQL.2\Reporting Services\ReportServer\"
- The standard formats are:
- XML
- IMAGE
- EXCEL
- CSV
Assumptions
- You know how to create a standard subscription.
- You are familiar with executing stored procedures.
Instructions
- Create or Replace the stored procedure and History table.
- Create a standard subscription, placing the name of your subscription in the subject line of the email.
NOTE: It is strongly suggested that you define a naming convention that will not allow duplicate subscription names.
- Set the beginning and ending date to dates prior to “today”. This will keep your subscription from running unless you explicitly call it.
- Execute the procedure with the (procedure’s) parameters you require.
Conclusion
As there is no way in telling what Microsoft will change in upcoming releases this method may become obsolete if the Reporting Services database changes, so be careful when implementing this as a full scale solution to your subscription needs. However, this procedure works under all versions of SQL RS 2000 and all current versions of RS 2005, so the chances are good that it will continue to work.
Again, any comments, suggestions or improvements are welcome, email me.
Acknowledgements
Thanks to Hugh Thomas for discovering the method to “wait” for a report’s execution.
Stored Procedure
/* DATE CREATED: 12/21/2006 AUTHOR: Jason L. Selburg PURPOSE: This procedure extends the functionality of the subscription feature in Microsoft SQL Reporting Services 2005, allowing the subscriptions to be triggered via code. The code supplied will function with reports that have one parameter. Reports that have multiple parameters must be addressed individually or with another method. There are many possible ways to handle multi-parameter reports, which is why it is not addressed here. However, one suggestion: - Create a subscription table that will hold subscription names and IDs. - Create a table to hold subscription IDs mapped to the previous table and hold the parameter names and values. - These tables would be queried and looped through to populate the parameter XML string below. NOTES: This procedure does not address "File Server Share" subscriptions. PARAMETERS: @scheduleName = This is the NAME that is put into the subject line of the subscription when created. It is STRONGLY suggested that you use a naming convention that will prevent duplicate names. @emailTO = The TO of the email (not required.) @emailCC = The CC of the email (not required.) |---One of these are REQUIRED! @emailBCC = The BCC of the email (not required.) / @emailReplyTO = The reply to address that will appear in the email. @emailBODY = The text in the body of the email. @parameterName = The paramerter name. This MUST match the parameter name in the report definition. @parameterValue = The parameter value. @sub = The subject line of the email. @renderFormat = The rendering format of the report. VALID VALUES : May be different depending on the installation and configuration of your server, but these are listed in the "reportServer.config" file. This file is located in a folder similar to "C:\Program Files\Microsoft SQL Server\MSSQL.2\Reporting Services\ReportServer\" XML IMAGE PDF EXCEL CSV @exitCode = The returned integer value of the procedure's execution result. -1 'A recipient is required.' -2 'The subscription does not exist.' -3 'No delivery settings were supplied.' -4 'A data base error occurred inserting the subscription history record.' -5 'A data base error occurred clearing the previous subscription settings.' -6 'A data base error occurred retrieving the TEXT Pointer of the Delivery Values.' -7 'A data base error occurred updating the Delivery settings.' -8 'A data base error occurred retrieving the TEXT Pointer of the Parameter Values.' -9 'A data base error occurred updating the Parameter settings.' -10 'A data base error occurred updating the subscription history record.' -11 'A data base error occurred resetting the previous subscription settings.' @exitMessage = The text description of the failure or success of the procedure. PRECONDITIONS: The subscription being called must exist and the SUBJECT line of the subscription MUST contain the exact name that is passed into this procedure. If any of the recipients email address are outside of the report server's domain, then you may need to contact your Network Administrator to allow email forwarding from your email server. POST CONDITIONS: The report is delivered or an error code and message is returned. SECURITY REQUIREMENTS: The user which calls this stored procedure must have execute permissions. DEPENDANCES: Tables: ReportSchedule = Installed with SQL RS 2005 Subscription_History = Must be created using the following script. --------------------------------------------------------------------- CREATE TABLE [dbo].[Subscription_History]( [nDex] [int] IDENTITY(1,1) NOT NULL, [SubscriptionID] [uniqueidentifier] NULL, [ScheduleName] [nvarchar](260) COLLATE Latin1_General_CI_AS_KS_WS NULL, [parameterSettings] [varchar](8000) COLLATE Latin1_General_CI_AS_KS_WS NULL, [deliverySettings] [varchar](8000) COLLATE Latin1_General_CI_AS_KS_WS NULL, [dateExecuted] [datetime] NULL, [executeStatus] [nvarchar] (260) NULL, [dateCompleted] [datetime] NULL, [executionTime] AS (datediff(second,[datecompleted],[dateexecuted])), CONSTRAINT [PK_Subscription_History] PRIMARY KEY CLUSTERED ( [nDex] ASC )WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] ) ON [PRIMARY] --------------------------------------------------------------------- Subscriptions = Installed with SQL RS 2005 Schedule = Installed with SQL RS 2005 */ALTER procedure [dbo].[data_driven_subscription] ( @scheduleName nvarchar(255), @emailTO nvarchar (2000) = NULL, @emailCC nvarchar (2000) = NULL, @emailBCC nvarchar (2000) = NULL, @emailReplyTO nvarchar (2000) = NULL, @emailBODY nvarchar (4000) = NULL, @parameterName nvarchar(4000) = NULL, @parameterValue nvarchar (256) = NULL, @sub nvarchar(1000) = NULL, @renderFormat nvarchar(50) = 'PDF', @exitCode int output, @exitMessage nvarchar(255) output ) AS DECLARE @ptrval binary(16), @PARAMptrval binary(16), @subscriptionID uniqueidentifier, @scheduleID uniqueidentifier, @starttime datetime, @lastruntime datetime, @execTime datetime, @dVALUES nvarchar (4000), @pVALUES nvarchar (4000), @previousDVALUES nvarchar (4000), @previousPVALUES nvarchar (4000), @lerror int, @insertID int, @lretval int, @rowcount int SET @starttime = DATEADD(second, -2, getdate()) SET @emailTO = rtrim(IsNull(@emailTO, '')) SET @emailCC = rtrim(IsNull(@emailCC, '')) SET @emailBCC = rtrim(IsNull(@emailBCC, '')) SET @emailReplyTO = rtrim(IsNull(@emailReplyTO, '')) SET @emailBODY = rtrim(IsNull(@emailBODY, '')) SET @parameterValue = rtrim(IsNull(@parameterValue, '')) SET @lerror = 0 SET @rowcount = 0 IF @emailTO = '' AND @emailCC = '' AND @emailBCC = '' BEGIN SET @exitCode = -1 SET @exitMessage = 'A recipient is required.' RETURN 0 END -- get the subscription ID SELECT @subscriptionID = rs.subscriptionID, @scheduleID = rs.ScheduleID FROM ReportSchedule rs INNER JOIN subscriptions s ON rs.subscriptionID = s.subscriptionID WHERE extensionSettings like '%' + @scheduleName + '%' IF @subscriptionID Is Null BEGIN SET @exitCode = -2 SET @exitMessage = 'The subscription does not exist.' RETURN 0 END /* just to be safe */SET @dVALUES = '' SET @pVALUES = '' SET @previousDVALUES = '' SET @previousPVALUES = '' /* apply the settings that are defined */IF IsNull(@emailTO, '') '' SET @dVALUES = @dVALUES + '<ParameterValue><Name>TO</Name><Value>' + @emailTO + '</Value></ParameterValue>' IF IsNull(@emailCC, '') <> '' SET @dVALUES = @dVALUES + '<ParameterValue><Name>CC</Name><Value>' + @emailCC + '</Value></ParameterValue>' IF IsNull(@emailBCC, '') <> '' SET @dVALUES = @dVALUES + '<ParameterValue><Name>BCC</Name><Value>' + @emailBCC + '</Value></ParameterValue>' IF IsNull(@emailReplyTO, '') <> '' SET @dVALUES = @dVALUES + '<ParameterValue><Name>ReplyTo</Name><Value>' + @emailReplyTO + '</Value></ParameterValue>' IF IsNull(@emailBODY, '') <> '' SET @dVALUES = @dVALUES + '<ParameterValue><Name>Comment</Name><Value>' + @emailBODY + '</Value></ParameterValue>' IF IsNull(@sub, '') <> '' SET @dVALUES = @dVALUES + '<ParameterValue><Name>Subject</Name><Value>' + @sub + '</Value></ParameterValue>' IF IsNull(@dVALUES , '') <> '' SET @dVALUES = '<ParameterValues>' + @dVALUES + '<ParameterValue><Name>IncludeReport</Name><Value>True</Value></ParameterValue>' IF IsNull(@dVALUES , '') <> '' SET @dVALUES = @dVALUES +'<ParameterValue><Name>RenderFormat</Name><Value>' + @renderFormat + '</Value></ParameterValue>' + '<ParameterValue><Name>IncludeLink</Name><Value>False</Value></ParameterValue></ParameterValues>' IF IsNull(@parameterName, '') <> '' and IsNull(@parameterValue, '') <> '' SET @pVALUES = '<ParameterValues><ParameterValue><Name>' + @parameterName + '</Name><Value>' + @parameterValue + '</Value></ParameterValue></ParameterValues>' /* verify that some delivery settings where passed in */-- @pVALUES are not checked as they may all be defaults IF IsNull(@dVALUES , '') = '' BEGIN SET @exitCode = -3 SET @exitMessage = 'No delivery settings were supplied.' RETURN 0 END /* get the current parameter values and delivery settings */SELECT @previousDVALUES = extensionSettings FROM Subscriptions WHERE SubscriptionID = @SubscriptionID SELECT @previousPVALUES = parameters FROM Subscriptions WHERE SubscriptionID = @SubscriptionID UPDATE Subscriptions SET extensionSettings = '', parameters = '' WHERE SubscriptionID = @SubscriptionID SELECT @lerror=@@error, @rowCount=@@rowCount IF @lerror 0 OR IsNull(@rowCount, 0) = 0 BEGIN SET @exitcode = -5 SET @exitMessage = 'A data base error occurred clearing the previous subscription settings.' RETURN IsNull(@lerror, 0) END -- set the text point for this record SELECT @ptrval = TEXTPTR(ExtensionSettings) FROM Subscriptions WHERE SubscriptionID = @SubscriptionID SELECT @lerror=@@error IF @lerror 0 OR @ptrval Is NULL BEGIN SET @exitcode = -6 SET @exitMessage = 'A data base error occurred retrieving the TEXT Pointer of the Delivery Values.' RETURN IsNull(@lerror, 0) END UPDATETEXT Subscriptions.ExtensionSettings @ptrval null null @dVALUES SELECT @lerror=@@error IF @lerror 0 BEGIN SET @exitcode = -7 SET @exitMessage = 'A data base error occurred updating the Delivery settings.' RETURN IsNull(@lerror, 0) END -- set the text point for this record SELECT @PARAMptrval = TEXTPTR(Parameters) FROM Subscriptions WHERE SubscriptionID = @SubscriptionID SELECT @lerror=@@error IF @lerror 0 OR @ptrval Is NULL BEGIN SET @exitcode = -8 SET @exitMessage = 'A data base error occurred retrieving the TEXT Pointer of the Parameter Values.' RETURN IsNull(@lerror, 0) END UPDATETEXT Subscriptions.Parameters @PARAMptrval null null @pVALUES SELECT @lerror=@@error IF @lerror 0 BEGIN SET @exitcode = -9 SET @exitMessage = 'A data base error occurred updating the Parameter settings.' RETURN IsNull(@lerror, 0) END /* insert a record into the history table */SET @execTime = getdate() INSERT Subscription_History (subscriptionID, scheduleName, ParameterSettings, DeliverySettings, dateExecuted, executeStatus) VALUES (@subscriptionID, @scheduleName, @parameterValue, @dVALUES , @execTime, 'incomplete' ) SELECT @lerror=@@error, @insertID=@@identity IF @lerror 0 OR IsNull(@insertID, 0) = 0 BEGIN SET @exitcode = -4 SET @exitMessage = 'A data base error occurred inserting the subscription history record.' RETURN IsNull(@lerror, 0) END -- run the job EXEC msdb..sp_start_job @job_name = @scheduleID -- this gives the report server time to execute the job SELECT @lastruntime = LastRunTime FROM ReportServer..Schedule WHERE ScheduleID = @scheduleID WHILE (@starttime > @lastruntime) BEGIN WAITFOR DELAY '00:00:01' SELECT @lastruntime = LastRunTime FROM ReportServer..Schedule WHERE ScheduleID = @scheduleID END /* update the history table with the completion time */UPDATE Subscription_History SET dateCompleted = getdate() WHERE subscriptionID = @subscriptionID and scheduleName = @scheduleName and ParameterSettings = @parameterValue and dateExecuted = @execTime SELECT @lerror=@@error, @rowCount=@@rowCount IF @lerror 0 OR IsNull(@rowCount, 0) = 0 BEGIN SET @exitcode = -10 SET @exitMessage = 'A data base error occurred updating the subscription history record.' RETURN IsNull(@lerror, 0) END /* reset the previous delivery and parameter values */UPDATE Subscriptions SET extensionSettings = @previousDVALUES , parameters = @previousPVALUES WHERE SubscriptionID = @SubscriptionID SELECT @lerror=@@error, @rowCount=@@rowCount IF @lerror 0 OR IsNull(@rowCount, 0) = 0 BEGIN SET @exitcode = -11 SET @exitMessage = 'A data base error occurred resetting the previous subscription settings.' RETURN IsNull(@lerror, 0) END /* return the result of the subscription */SELECT @exitMessage = LastStatus FROM subscriptions WHERE subscriptionID = @subscriptionID SET @exitCode = 1 RETURN 0 GO SET ANSI_NULLS OFF GO SET QUOTED_IDENTIFIER OFF GO