Wednesday, 11 April 2012

Basic Cursor and Loop usage with examples

This is a basic cursor, useful for smaller amounts of data.  It enables you to carry out a query against each record independantly before moving on to the next one. 

declare @Parameter1 varchar(50)

declare @Parameter2 varchar(50)

declare cur_cursor cursor for (select fieldname1,fieldname2
                                                           from server.[database].[dbo].[table with data])

open cur_cursor

Fetch Next from cur_cursor into @Parameter1, @Parameter2

While @@fetch_status = 0

begin

update table
set fieldname = @Parameter1
where otherfieldname = @Parameter2

Fetch Next from cur_cursor into @Parameter1, @Parameter2

end
close cur_cursor
deallocate cur_cursor

The following cursor uses dynamic SQL, which can allow you to add columns to a table, or deduce which columns you wish to query. 
declare @Parameter1 varchar(50)

declare @Parameter2 varchar(50)

declare @SQL nvarchar(4000)

declare cur_cursor cursor for (select fieldname1,fieldname2
                              from server.[database].[dbo].[table with data])

open cur_cursor

Fetch Next from cur_cursor into @Parameter1, @Parameter2

While @@fetch_status = 0

begin

set @SQL = 'update tablename
                set fieldname = ''test' + @Parameter2 + '''
           where otherfield = ''' + @Parameter1 + ''''

exec sp_execute @SQL

Fetch Next from cur_cursor into @Parameter1, @Parameter2

end
close cur_cursor
deallocate cur_cursor

There will be more to follow with more detailed explanations, examples of loops for larger datasets and nested loops, after the weekend.


Any questions let me know.

Data-Driven Subcriptions in Standard Edition of SQL Server

Recently I was asked to come up with a solution where we could emulate the usefulness of a data-driven subscription without paying out for the Enterprise edition of SQL Server.  This was the result.

The idea is to create an ‘empty’ standard subscription in SSRS and then, using a stored procedure, manipulate the parameters and run the report.
First create a dummy subscription that isn’t scheduled to run:

Set the parameters and delivery information as above.  For now we will use only one parameter, but the stored procedure can easily be changed to accommodate as many as required.

Once the subscription is saved, you need to get the SubscriptionID from the report server. Use the following code:

SELET
c.Name AS ReportName,   
rs.ScheduleID
FROM ReportServer.dbo.[Catalog] c
INNER JOIN ReportServer.dbo.Subscriptions s ON c.ItemID = s.Report_OID
INNER JOIN ReportServer.dbo.ReportSchedule rs ON c.ItemID = rs.ReportID
AND rs.SubscriptionID = s.SubscriptionID
Where
c.Name = 'Subscription Name'

You’ll need the SubscriptionID when calling the report.
Next create the following stored procedure that will be called to run the report(s):
(I found this SP on the internet, but can’t remember where so can’t credit where it’s due), either way it has a couple of changes and I hope it helps out other people)
USE [ReportServer]
GO
/****** Object:  StoredProcedure [dbo].[data_driven_subscription]    Script Date: 04/11/2012 16:38:33 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER   procedure [dbo].[data_driven_subscription_1_Param]
       ( @scheduleID uniqueidentifier,
         @TO varchar (2000) = ' ',
         @CC varchar (2000) = ' ',
         @BCC varchar (2000) = ' ',
         @ReplyTO varchar (2000) = ' ',
         @BODY varchar (8000) = ' ',
         @param1 varchar (256) = ' '
       )
as

DECLARE
       @ptrval binary(16),
       @PARAMptrval binary(16),
       @TOpos int,
       @CCpos int,
       @BCCpos int,
       @RTpos int,
       @BODYpos int,
       @PARAM1Pos int,
       @length int,
       @subscriptionID uniqueidentifier


       -- set the subscription ID
       SELECT @subscriptionID = SubscriptionID
       FROM ReportSchedule WHERE ScheduleID = @scheduleID
      
       -- set the text point for this records Email info
       SELECT @ptrval = TEXTPTR(ExtensionSettings)
       FROM Subscriptions WHERE SubscriptionID = @subscriptionID


       -- set the text point for this records Parameter info
       SELECT @PARAMptrval = TEXTPTR(Parameters)
       FROM Subscriptions WHERE SubscriptionID = @subscriptionID
      
              -- set the start position for the TO Address
              SELECT @TOpos = patindex('%|EmailTO|%', ExtensionSettings) - 1
              FROM Subscriptions WHERE SubscriptionID = @subscriptionID
             

                     IF isnull(@TOpos, '') <> '' and @TOpos > 0 and len(@To) > 0
                           -- change the TO address
                           UPDATETEXT Subscriptions.ExtensionSettings
                                   @ptrval
                                  @TOpos
                                  4
                                  @To

              -- set the start position for the CC Address
              SELECT @CCpos = patindex('%|CarbonCopy|%', ExtensionSettings) - 1
              FROM Subscriptions WHERE SubscriptionID = @subscriptionID
             

                     IF isnull(@CCpos, '') <> '' and @CCpos > 0 and len(@CC) > 0
                           -- change the TO address
                           UPDATETEXT Subscriptions.ExtensionSettings
                                  @ptrval
                                  @CCpos
                                  4
                                  @CC
      
              -- set the start position for the BCC Address
              SELECT @BCCpos = patindex('%|BlindCopy|%', ExtensionSettings) - 1
              FROM Subscriptions WHERE SubscriptionID = @subscriptionID
             

                     IF isnull(@BCCpos, '') <> '' and @BCCpos > 0 and len(@BCC) > 0
                           -- change the TO address
                           UPDATETEXT Subscriptions.ExtensionSettings
                                  @ptrval
                                  @BCCpos
                                  4
                                  @BCC

              -- set the start position for the REPLY TO Address
              SELECT @RTpos = patindex('%|ReplyTo|%', ExtensionSettings) - 1
              FROM Subscriptions WHERE SubscriptionID = @subscriptionID
             

                     IF isnull(@RTpos, '') <> '' and @RTpos > 0 and len(@ReplyTO) > 0
                           -- change the REPLY TO address
                           UPDATETEXT Subscriptions.ExtensionSettings
                                  @ptrval
                                  @RTpos
                                  4
                                  @ReplyTO

              -- set the start position for the BODY Text
              SELECT @BODYpos = patindex('%|Comment|%', ExtensionSettings) - 1
              FROM Subscriptions WHERE SubscriptionID = @subscriptionID
             
      
                     IF isnull(@BODYpos, '') <> '' and @BODYpos > 0 and len(@BODY) > 0
                           -- change the REPLY TO address
                           UPDATETEXT Subscriptions.ExtensionSettings
                                  @ptrval
                                  @BODYpos
                                  4
                                  @BODY

              -- set the start position for the Parameter 1
              SELECT @PARAM1Pos = patindex('%|Parameter1|%', Parameters) - 1
              FROM Subscriptions WHERE SubscriptionID = @subscriptionID
     
              IF isnull(@PARAM1Pos, '') <> '' and @PARAM1Pos > 0 and len(@parameter1) > 0
                           -- change the Parameter 1 value
                           UPDATETEXT Subscriptions.Parameters
                                  @PARAMptrval
                                  @PARAM1Pos
                                  4
                                  @parameter1

       -- run the job
       exec msdb..sp_start_job @job_name = @scheduleID


       -- this give the report server time to execute the job.
       -- there is probably a better way to do this, so let me know if you know it ...
       WAITFOR DELAY '00:00:10'

       -- now change everything back so you can run this again

              -- set the start position for the TO Address
              SELECT @TOpos = patindex('%' + @TO + '%', ExtensionSettings) - 1
              FROM Subscriptions WHERE SubscriptionID = @subscriptionID
             
              SELECT @length = len(@TO)

        IF @length > 0
                     -- replace the addresses with the original |TO|
                     UPDATETEXT Subscriptions.ExtensionSettings
                           @ptrval
                           @TOpos
                           @length
                           '|EmailTO|'

              -- set the start position for the TO Address
              SELECT @CCpos = patindex('%' + @CC + '%', ExtensionSettings) - 1
              FROM Subscriptions WHERE SubscriptionID = @subscriptionID
             
              SELECT @length = len(@CC)

        IF @length > 0
                     -- replace the addresses with the original |CC|
                     UPDATETEXT Subscriptions.ExtensionSettings
                           @ptrval
                           @CCpos
                           @length
                           '|CarbonCopy|'

              -- set the start position for the TO Address
              SELECT @BCCpos = patindex('%' + @BCC + '%', ExtensionSettings) - 1
              FROM Subscriptions WHERE SubscriptionID = @subscriptionID
             
              SELECT @length = len(@BCC)

        IF @length > 0
                     -- replace the addresses with the original |BC|
                     UPDATETEXT Subscriptions.ExtensionSettings
                           @ptrval
                           @BCCpos
                           @length
                           '|BlindCopy|'

              -- set the start position for the REPLY TO Address
              SELECT @RTpos = patindex('%' + @ReplyTO + '%', ExtensionSettings) - 1
              FROM Subscriptions WHERE SubscriptionID = @subscriptionID
             
              SELECT @length = len(@ReplyTO)

        IF @length > 0
                     -- replace the addresses with the original |RT|
                     UPDATETEXT Subscriptions.ExtensionSettings
                           @ptrval
                           @RTpos
                           @length
                           '|ReplyTo|'

              -- set the start position for the BODY Text
              SELECT @BODYpos = patindex('%' + @BODY + '%', ExtensionSettings) - 1
              FROM Subscriptions WHERE SubscriptionID = @subscriptionID
             
              SELECT @length = len(@BODY)

        IF @length > 0
                     -- replace the addresses with the original |BD|
                     UPDATETEXT Subscriptions.ExtensionSettings
                           @ptrval
                           @BODYpos
                           @length
                           '|Comment|'

              -- set the start position for the Parameter
              SELECT @PARAM1Pos = patindex('%' + @parameter1 + '%', Parameters) - 1
              FROM Subscriptions WHERE SubscriptionID = @subscriptionID
      
              SELECT @length = len(@param1)

        IF @length > 0
                     -- replace the addresses with the original |P1|
                     UPDATETEXT Subscriptions.Parameters
                           @PARAMptrval
                           @PARAM1Pos
                           @length
                           '|Parameter1|'

You’ll notice the name of the SP ends with ‘1_Param’.  To add more use the following code in the relevant part of the SP:
              -- set the start position for the Parameter 2
              SELECT @PARAM2Pos = patindex('%|Parameter2|%', Parameters) - 1
              FROM Subscriptions WHERE SubscriptionID = @subscriptionID
      

             IF isnull(@PARAM2Pos, '') <> '' and @PARAM2Pos > 0 and len(@parameter2) > 0
                           -- change the Parameter 1 value
                           UPDATETEXT Subscriptions.Parameters
                                  @PARAMptrval
                                  @PARAM2Pos
                                  4
                                  @parameter2

                SELECT @length = len(@param2)
         IF @length > 0
                     -- replace the addresses with the original |P2|
                     UPDATETEXT Subscriptions.Parameters
                           @PARAMptrval
                           @PARAM1Pos
                           @length
                           '|Parameter2|'


It would be prudent to create a few versions of this SP with carrying numbers of parameters to meet all your needs.
The next SP is the one that runs and controls your new data-driven subscription capabilities.

declare @ReportParameter varchar(50)
declare @email_address varchar(255)
declare cur_cursor cursor for (select report_parameter,email_address

                                          from server.[database].[dbo].[tablewith data])
open cur_cursor
Fetch Next from cur_cursor into @ReportParameter, @email_address
While @@fetch_status = 0
begin

exec data_driven_subscription_1_Param
@scheduleID = 'FE98A2D4-4430-4D5C-806C-717470CB1332',
@TO = @email_address,
@CC = '',
@BCC = 'AdministrativeEmail@CompanyName.com',
@ReplyTO = 'RelevantTeam@YourCompany.com',
@BODY = 'Hello testing 123',
@param1 = @ReportParameter

Fetch Next from cur_cursor into @ReportParameter, @email_address
end
close cur_cursor
deallocate cur_cursor

This cursor uses a table directly, but you can create any complicated query you wish to determinethe parameters and emails, just as you would with a normal data-driven subscription.
Notice the @scheduleID – this is the scheduleID you collected after you saved the standard report subscription.

The above code can either be turned into an SP, or placed directly into a SQL Agent Job.

Once you have an Agent job to run this procedure you can schedule it when and as required.

Any questions please don’t hesitate to contact me.