Execute T-SQL Scripts in Parallel

James Ma, 2013-08-13 (first published: 2009-08-28)

Transact-SQL does not have a simple method to launch multiple parallel running scripts. The pmaster is a database project that enables you to launch multiple SQL scripts to run in parallel. Just after running a single setup script, you can program your SQL script like the following demo:

use AdventureWorks;

declare @rc int;

— init before launch sqls

exec @rc = sp_exec_init;

if @rc=0 begin

— The following sqls will run in parallel

exec sp_exec 'select @@servername waitfor delay ''00:00:10''';

exec sp_exec 'use AdventureWorks

select * from Person.Address

select @@servername waitfor delay ''00:00:10''';

exec sp_exec 'select @@servername waitfor delay ''00:00:10''';

exec sp_exec 'select @@servername waitfor delay ''00:00:10''';

— Wait until all 'done'.

exec sp_exec_wait;

— The following sqls will run in parallel

exec sp_exec 'select @@servername waitfor delay ''00:00:10''';

exec sp_exec 'select @@servername waitfor delay ''00:00:10''';

— Wait until all 'done'.

exec sp_exec_wait;

end

— end of the session

exec sp_exec_end;

Note: I find the web page's default encoding (Unicode) might change some white space characters in the script, so can cause the script fail to run.

Workaround: First change the page encoding to be "Western European (Windows)", secondly copy and paste the code to a SSMS window, then change the strange character (it's   on my screen) to white space.

    /*
 [Purpose] Enable to execute sql scripts in parallel. This is the all-in-one setup script
 
 [Author] James Ma
 
 [Disclaimer] The user of this code must take full responsibility for any changes to the system. The author is
 exempt from any liabilities if this script could cause any damage.
 
 [Setup] Don't change anything here and make sure you're sysadmin. Then simply run this script. 
 
 [Smoke Test] Go to the last page of this script, highlight the smoke test script and run.
 
 [Uninstall] Go to the last page of this script, highlight the uninstallation script and run.
 
 [Usage] Simply init, exec sqls, wait, and end. For example,
 
 use AdventureWorks;
 declare @rc int;

 -- Call init before launch sqls
 exec @rc=sp_exec_init

 -- The following sqls will run in parallel
 if @rc=0 begin
 exec sp_exec 'select @@servername waitfor delay ''00:00:10'''
 exec sp_exec 'use AdventureWorks
 select * from Person.Address
 select @@servername waitfor delay ''00:00:10''',
 exec sp_exec 'select @@servername waitfor delay ''00:00:10'''
 exec sp_exec 'select @@servername waitfor delay ''00:00:10'''

 -- Wait until all 'done'.
 exec sp_exec_wait

 exec sp_exec 'select @@servername waitfor delay ''00:00:10'''
 exec sp_exec 'select @@servername waitfor delay ''00:00:10'''
 end
 
 exec sp_exec_end;
 
 
 [Change History]
 Date By Description
 -------- ------------ ------------------------------
 20090609 James Ma Version 1.0.

*/

use master;
go
create database pmaster;
go
ALTER DATABASE [pmaster] SET RECOVERY SIMPLE WITH NO_WAIT
GO
use pmaster;
go
EXEC sp_grantlogin [NT Authority\System]
EXEC sp_addsrvrolemember @loginame = [NT Authority\System], @rolename = 'sysadmin'
Go
EXEC sp_changedbowner 'NT AUTHORITY\SYSTEM'
go
use master
go
ALTER DATABASE pmaster SET ENABLE_BROKER
alter database pmaster set trustworthy on
go


USE [pmaster]
GO
/****** Object: Table [dbo].[sysparameter] Script Date: 06/11/2009 15:58:24 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[sysparameter](
 [parameter_name] [varchar](512) NOT NULL,
 [parameter_value] [varchar](max) NOT NULL
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
/****** Object: StoredProcedure [dbo].[p_printerror] Script Date: 06/11/2009 15:58:25 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[p_printerror]
AS
BEGIN
SET NOCOUNT ON;

-- Print error information. 
PRINT 'Error ' + CONVERT(varchar(50), ERROR_NUMBER()) +
', Severity ' + CONVERT(varchar(5), ERROR_SEVERITY()) +
', State ' + CONVERT(varchar(5), ERROR_STATE()) + 
', Procedure ' + ISNULL(ERROR_PROCEDURE(), '-') + 
', Line ' + CONVERT(varchar(5), ERROR_LINE());
PRINT ERROR_MESSAGE();
END;
GO
/****** Object: StoredProcedure [dbo].[_p_exec_dropqueue] Script Date: 06/11/2009 15:58:25 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[_p_exec_dropqueue]
 @master_spid smallint = null
 ,@options varchar(1024) = ''
as
set nocount on

set nocount on
if @master_spid is null set @master_spid=@@SPID;

declare @sql varchar(max) set @sql=replace('set nocount on;
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N''[dbo].[_p_exec_worker_<id>]'') AND type in (N''P'', N''PC''))
 DROP PROCEDURE [dbo].[_p_exec_worker_<id>]
IF EXISTS (SELECT * FROM sys.services WHERE name = N''//pmaster/exec/TargetService_<id>'')
 DROP SERVICE [//pmaster/exec/TargetService_<id>];
IF EXISTS (SELECT * FROM sys.service_queues WHERE name = N''pmaster_exec_TargetQueue_<id>'')
 DROP QUEUE [dbo].[pmaster_exec_TargetQueue_<id>];'
 ,'<id>',convert(varchar,@master_spid));
 
if @options like '%debug%' print @sql;
if @options not like '%printonly%' exec(@sql);
return @@error;
GO
/****** Object: StoredProcedure [dbo].[_p_exec_createqueue] Script Date: 06/11/2009 15:58:25 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[_p_exec_createqueue]
 @master_spid smallint = null
 ,@worker_num smallint = 4
 ,@options varchar(1024) = ''
as
set nocount on
if @master_spid is null set @master_spid=@@SPID;

declare @sql varchar(max) 
select @sql=replace(replace(replace(ROUTINE_DEFINITION
 ,'dbo._p_exec_worker','dbo._p_exec_worker_'+convert(varchar,@master_spid))
 ,'[dbo].[_p_exec_worker]','dbo._p_exec_worker_'+convert(varchar,@master_spid))
 ,'dbo.pmaster_exec_TargetQueue','dbo.pmaster_exec_TargetQueue_'+convert(varchar,@master_spid))
from INFORMATION_SCHEMA.ROUTINES where ROUTINE_SCHEMA='dbo' and ROUTINE_NAME='_p_exec_worker'
if @options like '%debug%' print @sql;
if @options not like '%printonly%' exec(@sql);

set @sql=replace(replace('set nocount on;
CREATE QUEUE pmaster_exec_TargetQueue_<id> WITH STATUS=ON,
 ACTIVATION (PROCEDURE_NAME=_p_exec_worker_<id>,MAX_QUEUE_READERS=<wn>,EXECUTE AS SELF);
CREATE SERVICE [//pmaster/exec/TargetService_<id>]
 ON QUEUE pmaster_exec_TargetQueue_<id>([//pmaster/exec/Contract]);'
 ,'<id>',convert(varchar,@master_spid))
 ,'<wn>',convert(varchar,@worker_num));
if @options like '%debug%' print @sql;
if @options not like '%printonly%' exec(@sql);
return @@error;
GO
/****** Object: Table [dbo].[exec_master] Script Date: 06/11/2009 15:58:25 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[exec_master](
 [master_spid] [smallint] NOT NULL,
 [worker_num] [smallint] NOT NULL,
 [create_time] [datetime] NOT NULL,
 [dialog_handle] [uniqueidentifier] NULL,
CONSTRAINT [PK_exec_master] PRIMARY KEY CLUSTERED 
(
 [master_spid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[exec_log] Script Date: 06/11/2009 15:58:25 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[exec_log](
 [log_id] [int] IDENTITY(1,1) NOT NULL,
 [log_spid] [smallint] NOT NULL,
 [exec_queue_id] [bigint] NULL,
 [log_msg] [varchar](max) NULL,
 [log_time] [datetime] NOT NULL,
CONSTRAINT [PK_exec_log] PRIMARY KEY CLUSTERED 
(
 [log_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
/****** Object: StoredProcedure [dbo].[p_exec_cleanqueue] Script Date: 06/11/2009 15:58:25 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create procedure [dbo].[p_exec_cleanqueue]
as
set nocount on

declare @conversation uniqueidentifier;
while exists (select * from sys.conversation_endpoints where [state] in ('CD','DI')) begin
 set @conversation = (select top 1 conversation_handle from sys.conversation_endpoints where [state] in ('CD','DI'))
 end conversation @conversation with cleanup
end

return @@error
GO
/****** Object: UserDefinedFunction [dbo].[f_exec_option] Script Date: 06/11/2009 15:58:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE function [dbo].[f_exec_option](@options varchar(max),@arg varchar(512)) returns varchar(512)
as
begin
 declare @startPos int,@endPos int
 set @startPos=charindex(@arg,@options,1)
 if @startPos=0 return null
 set @endPos=charindex(';',@options,@startPos)
 if (@endPos=0) set @endPos=len(@options)+1
 return substring(@options,@startPos+len(@arg)+1,(@endPos-@startPos-len(@arg)-1))
end
GO
/****** Object: Table [dbo].[exec_queue] Script Date: 06/11/2009 15:58:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[exec_queue](
 [exec_queue_id] [bigint] IDENTITY(-9223372036854775808,1) NOT NULL,
 [master_spid] [smallint] NOT NULL,
 [worker_spid] [smallint] NULL,
 [wait_type] [tinyint] NOT NULL,
 [create_time] [datetime] NOT NULL,
 [send_time] [datetime] NULL,
 [worker_start_time] [datetime] NULL,
 [worker_end_time] [datetime] NULL,
 [return_code] [int] NULL,
 [return_msg] [nvarchar](max) NULL,
CONSTRAINT [PK_exec_queue] PRIMARY KEY CLUSTERED 
(
 [exec_queue_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_exec_queue_master_spid] ON [dbo].[exec_queue] 
(
 [master_spid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
/****** Object: StoredProcedure [dbo].[p_exec_log] Script Date: 06/11/2009 15:58:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[p_exec_log]
 @exec_queue_id bigint
 ,@msg nvarchar(max)
as
set nocount on

insert dbo.exec_log(log_spid,exec_queue_id,log_msg,log_time)
select @@SPID,@exec_queue_id,@msg,GETDATE()

return @@error
GO
/****** Object: StoredProcedure [dbo].[_p_exec_worker] Script Date: 06/11/2009 15:58:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[_p_exec_worker]
as
set nocount on

DECLARE @RecvReqDlgHandle UNIQUEIDENTIFIER;
DECLARE @RecvReqMsg NVARCHAR(max); set @RecvReqMsg=null
DECLARE @RecvReqMsgName sysname;

BEGIN TRANSACTION;
WAITFOR(RECEIVE TOP(1)
 @RecvReqDlgHandle = conversation_handle,
 @RecvReqMsg = message_body,
 @RecvReqMsgName = message_type_name
 FROM dbo.pmaster_exec_TargetQueue
 ), TIMEOUT 1000;
COMMIT TRANSACTION;

IF (@RecvReqMsgName = N'//pmaster/exec/RequestMessage') AND (@RecvReqMsg is not null) 
 and (@RecvReqMsg like '--//pmaster/exec/request:%') 
begin
 declare @header varchar(2048),@qid bigint,@msg nvarchar(max),@rc int;
 ----//pmaster/exec/request: is the prefix
 set @header=substring(@RecvReqMsg,26,charindex(N'--',@RecvReqMsg,26)-26);
 set @qid=dbo.f_exec_option(@header,'@exec_queue_id');
 update dbo.exec_queue set worker_spid=@@SPID,worker_start_time=GETDATE() where exec_queue_id=@qid;

 begin try 
 --exec dbo.p_exec_log @RecvReqMsg
 exec @rc=sp_executesql @RecvReqMsg;
 set @msg='';
 end try
 begin catch
 select @rc=ERROR_NUMBER(),@msg=ERROR_MESSAGE();
 exec dbo.p_exec_log @qid,@RecvReqMsg
 end catch

 update dbo.exec_queue set return_code=@rc,return_msg=@msg,worker_end_time=GETDATE() where exec_queue_id=@qid;
end

return @@error
GO
/****** Object: StoredProcedure [dbo].[_p_exec_clean] Script Date: 06/11/2009 15:58:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[_p_exec_clean]
 @master_spid smallint = null
 ,@options varchar(1024) = ''
as
set nocount on
if @master_spid is null set @master_spid=@@SPID;

declare @conversation uniqueidentifier

select @conversation=dialog_handle from dbo.exec_master where master_spid=@master_spid;
if @conversation is not null end conversation @conversation with cleanup;
update dbo.exec_master set dialog_handle=null where master_spid=@master_spid

exec dbo._p_exec_dropqueue @master_spid,@options

delete from dbo.exec_queue where master_spid=@master_spid
delete from dbo.exec_master where master_spid=@master_spid

while exists (select * from sys.conversation_endpoints where [state] in ('CD','DI')) begin
 set @conversation = (select top 1 conversation_handle from sys.conversation_endpoints where [state] in ('CD','DI'))
 end conversation @conversation with cleanup
end

return @@error
GO
/****** Object: StoredProcedure [dbo].[p_exec] Script Date: 06/11/2009 15:58:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[p_exec]
/*
 [Purpose] Execute sql in parallel
 
 [Owner] James Ma
 
 [Usage in an example] Simply init, exec sqls, and wait.
 
 use AdventureWorks;
 declare @rc int,@db varchar(256) set @db=DB_NAME();

 -- init before launch sqls
 exec @rc=sp_exec_init

 -- The following sqls will run in parallel
 if @rc=0 begin
 exec sp_exec 'select @@servername waitfor delay ''00:00:10''',0,@db
 exec sp_exec 'use AdventureWorks
 select * from Person.Address
 select @@servername waitfor delay ''00:00:10''',1
 exec sp_exec 'select @@servername waitfor delay ''00:00:10''',2
 exec sp_exec 'select @@servername waitfor delay ''00:00:10''',2

 -- Wait until all 'done'.
 exec sp_exec_wait
 end
 
 -- end the exec in the end
 exec sp_exec_end
 
 
 [Change History]
 Date By Description
 -------- ------------ ------------------------------
 20090609 James Ma Version 1.0.

*/
 @sql nvarchar(max)
 ,@wait_type tinyint = 2 -- 0: send only and no wait; 
 -- 1: wait till worker starts;
 -- 2: wait till worker finishes.
 ,@db varchar(256) = null
as
set nocount on
declare @master_spid smallint set @master_spid=@@SPID;
declare @rc int set @rc=null;
DECLARE @InitDlgHandle UNIQUEIDENTIFIER,@exec_queue_id bigint,@header varchar(2048)

begin try
 begin transaction;
 
 insert dbo.exec_queue(master_spid,wait_type,create_time)
 select @master_spid,@wait_type,GETDATE();
 set @exec_queue_id=SCOPE_IDENTITY();
 declare @tmp nvarchar(2048)
 
 select @InitDlgHandle=dialog_handle from dbo.exec_master where master_spid=@master_spid
 if @InitDlgHandle is null begin
 set @tmp=replace('set nocount on
BEGIN DIALOG @InitDlgHandle
 FROM SERVICE [//pmaster/exec/InitiatorService]
 TO SERVICE N''//pmaster/exec/TargetService_<Id>''
 ON CONTRACT [//pmaster/exec/Contract]
 WITH ENCRYPTION = OFF;','<Id>',@master_spid);
 exec sp_executesql @tmp,N'@InitDlgHandle UNIQUEIDENTIFIER output',@InitDlgHandle output;
 
 update dbo.exec_master set dialog_handle=@InitDlgHandle where master_spid=@master_spid
 end;
 
 set @sql=replace(replace(replace(replace(replace(
N'--//pmaster/exec/request:@exec_queue_id=<qid>;@master_spid=<id>;@wait_type=<wt>--
use [<db>];
<sql>'
,'<id>',convert(varchar,@master_spid)),'<qid>',CONVERT(varchar,@exec_queue_id)),'<wt>',CONVERT(varchar,@wait_type))
,'<db>',isnull(@db,DB_NAME())),'<sql>',@sql);

 SEND ON CONVERSATION @InitDlgHandle MESSAGE TYPE [//pmaster/exec/RequestMessage](@sql);
 update dbo.exec_queue set send_time=getdate() where exec_queue_id=@exec_queue_id;
 commit transaction;
 set @rc=@@error;
end try
begin catch
 exec dbo.p_printerror;
 set @rc=ERROR_NUMBER();
 IF XACT_STATE() <> 0 ROLLBACK TRANSACTION;
end catch

return @rc
GO
/****** Object: StoredProcedure [dbo].[p_exec_wait] Script Date: 06/11/2009 15:58:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[p_exec_wait]
as
set nocount on
declare @master_spid smallint set @master_spid=@@SPID

while exists(select * from dbo.exec_queue where master_spid=@master_spid 
 and ( (wait_type=0 and worker_spid is null) or
 (wait_type=1 and worker_start_time is null) or
 (wait_type=2 and worker_end_time is null)))
 waitfor delay '00:00:03';
 
exec dbo.p_exec_cleanqueue;

return @@error
GO
/****** Object: StoredProcedure [dbo].[p_exec_reset] Script Date: 06/11/2009 15:58:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[p_exec_reset]
as
set nocount on

declare @rc int set @rc=null;

BEGIN TRY
 declare @master_spid smallint
 
 BEGIN TRANSACTION;
 
 declare curId cursor local forward_only 
 for select master_spid from dbo.exec_master
 
 open curId
 fetch next from curId into @master_spid
 while (@@FETCH_STATUS=0) begin
 exec dbo._p_exec_clean @master_spid,''
 fetch next from curId into @master_spid
 end
 
 COMMIT TRANSACTION;
 set @rc=0;
END TRY
BEGIN CATCH
exec dbo.p_printerror;
 set @rc = -1;
 IF XACT_STATE() <> 0 ROLLBACK TRANSACTION;
END CATCH;

exec dbo.p_exec_cleanqueue;

return @rc
GO
/****** Object: StoredProcedure [dbo].[p_exec_init] Script Date: 06/11/2009 15:58:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[p_exec_init]
 @worker_num smallint = 4
as
set nocount on
declare @master_spid smallint set @master_spid=@@SPID 

exec dbo.p_exec_cleanqueue;

--last session has not ended yet, cannot proceed!
if exists(select * from dbo.exec_queue where master_spid=@master_spid and worker_spid is null)
begin
 RAISERROR ('Previous running has not finished, cannot init the pmaster exec system!',16,1);
 return -1;
end

if (@worker_num<1 or @worker_num>16) 
begin
 RAISERROR ('@worker_num parameter must be between 1 and 16.',16,1);
 return -1;
end

declare @rc int set @rc=null

begin try
 begin transaction;
 exec dbo._p_exec_clean @master_spid,''
 exec dbo._p_exec_createqueue @master_spid,@worker_num,'';
 insert dbo.exec_master(master_spid,worker_num,create_time)
 select @master_spid,@worker_num,GETDATE();
 commit transaction;
 set @rc=@@error;
end try
begin catch
 exec dbo.p_printerror;
 set @rc=-1;
 IF XACT_STATE() <> 0 ROLLBACK TRANSACTION;
end catch

return @rc;
GO
/****** Object: StoredProcedure [dbo].[p_exec_end] Script Date: 06/11/2009 15:58:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[p_exec_end]
as
set nocount on
declare @master_spid smallint set @master_spid=@@SPID

while exists(select * from dbo.exec_queue where master_spid=@master_spid and worker_spid is null)
 waitfor delay '00:00:03';
 
begin try
 begin transaction;
 exec dbo._p_exec_clean @master_spid,'';
 commit transaction;
end try
begin catch
 exec dbo.p_printerror;
 IF XACT_STATE() <> 0 ROLLBACK TRANSACTION;
 return Error_number();
end catch

exec dbo.p_exec_cleanqueue;

return @@error
GO
/****** Object: ForeignKey [FK_exec_queue_exec_master] Script Date: 06/11/2009 15:58:26 ******/
ALTER TABLE [dbo].[exec_queue] WITH CHECK ADD CONSTRAINT [FK_exec_queue_exec_master] FOREIGN KEY([master_spid])
REFERENCES [dbo].[exec_master] ([master_spid])
GO
ALTER TABLE [dbo].[exec_queue] CHECK CONSTRAINT [FK_exec_queue_exec_master]
GO


USE pmaster
GO
CREATE MESSAGE TYPE [//pmaster/exec/RequestMessage] VALIDATION = NONE;
CREATE MESSAGE TYPE [//pmaster/exec/ReplyMessage] VALIDATION = NONE;
GO

CREATE CONTRACT [//pmaster/exec/Contract](
 [//pmaster/exec/RequestMessage] SENT BY INITIATOR,
 [//pmaster/exec/ReplyMessage] SENT BY TARGET);
GO

CREATE QUEUE pmaster_exec_TargetQueue WITH STATUS=ON,
 ACTIVATION (PROCEDURE_NAME=_p_exec_worker,MAX_QUEUE_READERS=16,EXECUTE AS SELF );
CREATE SERVICE [//pmaster/exec/TargetService] ON QUEUE pmaster_exec_TargetQueue([//pmaster/exec/Contract]);
GO
CREATE QUEUE pmaster_exec_InitiatorQueue;
CREATE SERVICE [//pmaster/exec/InitiatorService] ON QUEUE pmaster_exec_InitiatorQueue;
GO


use master
go

create procedure dbo.sp_exec_init
 @worker_num smallint = 4
as
set nocount on;
declare @rc int;
exec @rc=pmaster.dbo.p_exec_init @worker_num;
return @rc;
go
EXECUTE sp_ms_marksystemobject 'sp_exec_init'
go

create procedure dbo.sp_exec
/*
 [Purpose] Execute sql in parallel
 
 [Owner] James Ma
 
 [Usage in an example] Simply init, exec sqls, and wait.
 
 use AdventureWorks;
 declare @rc int,@db varchar(256) set @db=DB_NAME();

 -- Call init before launch sqls
 exec @rc=sp_exec_init

 -- The following sqls will run in parallel
 if @rc=0 begin
 exec sp_exec 'select @@servername waitfor delay ''00:00:10''',0,@db
 exec sp_exec 'use AdventureWorks
 select * from Person.Address
 select @@servername waitfor delay ''00:00:10''',1
 exec sp_exec 'select @@servername waitfor delay ''00:00:10''',2
 exec sp_exec 'select @@servername waitfor delay ''00:00:10''',2

 -- Wait until all 'done'.
 exec sp_exec_wait;
 end
 
 exec sp_exec_end;
 
 
 
 [Change History]
 Date By Description
 -------- ------------ ------------------------------
 20090609 James Ma Version 1.0.

*/
 @sql nvarchar(max)
 ,@wait_type tinyint = 2 -- 0: send only and no wait; 
 -- 1: wait till worker starts;
 -- 2: wait till worker finishes.
 ,@db varchar(256) = null
as
set nocount on;
declare @rc int;
exec @rc=pmaster.dbo.p_exec @sql,@wait_type,@db;
return @rc;
go
EXECUTE sp_ms_marksystemobject 'sp_exec'
go

create procedure dbo.sp_exec_wait
as
set nocount on;
declare @rc int;
exec @rc=pmaster.dbo.p_exec_wait;
return @rc;
go
EXECUTE sp_ms_marksystemobject 'sp_exec_wait'
go

create procedure dbo.sp_exec_end
as
set nocount on;
declare @rc int;
exec @rc=pmaster.dbo.p_exec_end;
return @rc;
go
EXECUTE sp_ms_marksystemobject 'sp_exec_end'
go


/* Smoke Test

use tempdb

declare @rc int
exec @rc=sp_exec_init 8
if (@rc=0) begin
 exec sp_exec 'select @@servername waitfor delay ''00:00:30'''
 exec sp_exec 'select @@servername waitfor delay ''00:00:30'''
 exec sp_exec 'select @@servername waitfor delay ''00:00:30'''
 exec sp_exec 'select @@servername waitfor delay ''00:00:30'''
 exec sp_exec_wait

 exec sp_exec 'select @@servername waitfor delay ''00:00:30'''
 exec sp_exec 'select @@servername waitfor delay ''00:00:30'''
 exec sp_exec_wait
end
exec sp_exec_end

--use another window to monitor the progress
select * from pmaster.sys.conversation_endpoints
select * from pmaster.dbo.exec_queue
select * from pmaster.dbo.exec_log

*/


/* Uninstall & Cleanup

 [Disclaimer] The user of this code must take full responsibility for any changes to the system. The author is
 exempt from any liability if this script could cause any damage.
 
 [Warning] Are you sure of what you are going to drop?

------use master
------go

------drop procedure dbo.sp_exec 
------drop procedure dbo.sp_exec_init 
------drop procedure dbo.sp_exec_wait
------drop procedure dbo.sp_exec_end
------go

------DROP DATABASE [pmaster]
------GO

*/

Rate

4.67 (12)

Share

Share

Rate

4.67 (12)