![]() |
Database »
Database »
SQL Server
Intermediate
License: The Code Project Open License (CPOL)
Asynchronous T-SQL Execution Without Service BrokerBy Alan KaplanSet of SQL CLR Stored Procedures to execute T-SQL asynchronously without using a Service Broker. |
C# (C# 2.0), SQL, .NET (.NET 2.0), SQL Server (SQL 2005), Architect, DBA, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
Quite often, SQL Server developers need to execute long running SQLs such as updates, inserts, bulk inserts, index creation, and other non-query types of T-SQL. At the end, the total execution time will be a sum of each SQL’s execution time. With such powerful multi-processors, SQL Server developers do not utilize that power. This is where multi-tasking comes in place. SQL Server 2005 and up provides database developers with one of the tools to execute SQL asynchronously- a Service Broker. It has its purpose, but sometimes, it does not solve the problem. A Service Broker is a messaging framework, and if a developer needs to wait until all parallel processes are complete in order to move to another task in the T-SQL application, some extra work has to be done to implement this: wait till all the messages are processed, place the next step in the Service Broker queue, and other techniques. I am not going to elaborate on multiple ways to accomplish this with a Service Broker, instead, I will present you with a different way to execute multiple T-SQLs at the same time.
Let's say you are developing an ETL process which has to bulk load multiple tables, create a number of indexes, move data from one database/server to another, and your process has to move a lot of rows. And, at the end, your process runs hours and hours, even though you tuned up all the indexes and optimized the infrastructure as much as you can. But, what if, let's say, you have to copy 10 tables, and you will copy all of them at the same time. You will save a lot of time if you do this in parallel. And, here is a solution for that - a very simple one, and does not involve messing up with a Service Broker and synchronizing the sequence of your SQL. After you copy all the 10 tables, you have to create indexes on each table you copied. So, you have two major steps in this simple project:
I will show in this example below how it can be done using Stored Procedures from the deployment script attached.
The attached deployment script has SQL CLR Stored Procedures and user defined functions which implement parallel SQL execution.
@name. The parameter is for information only, in case you have to get an error message.@mode. This parameter can have varchar(20) values: Serializable, RepeatableRead, ReadCommitted, ReadUncommitted, Snapshot, Chaos.@commandTimeout of type int. The default value is 120 seconds. If set to 0, the execution of any SQL in the parallel block will not timeout.@maxThreads of type int. The default value is 10. The valid values are from 1 to 64.nvarchar(128) @key, and nvarchar(MAX) @sql, where @key is a unique key identifying the SQL in the array of SQLs to be executed, and @sql is the actual SQL to be executed. The SQL can be any T-SQL, but it can not contain GO statements.Parallel_UseTransaction for the current block execution, the results from all the SQLs will be committed in the case of success, or all will be rolled-back in the case of any SQL failure. Here is a major point of all these procedures: the Parallel_Execute procedure will not end until all SQLs are complete, or any SQL has failed.nvarchar(MAX) if any SQL in the parallel block has failed; otherwise, it will return NULL.To use these Stored Procedures, you have to apply the provided script to your database. For example, I have setup a ClrLibDb database. Make sure the instance of the server is configured with CLR support turned on. Details of how to do this are omitted.
TRUSTWORTHY on.Also, I created a [test] database, and a table in this database [Table_1] with two fields of type int, and varchar(50).
Here are examples of how to use it:
-- First you have to declare parallel block.
-- You can name it anyway you like. THIS IS A MUST
exec ClrLibDb.dbo.Parallel_Declare 'test block'
-- optionally you can setup options for your block
exec ClrLibDb.dbo.Parallel_SetOption_MaxThreads 9
exec ClrLibDb.dbo.Parallel_SetOption_CommandTimeout 30
-- if you need all sqls run under single
-- transaction you can call this procedure
-- if any sql failed, everything will be rollback,
-- otherwise everything will be committed
-- after Parallel_Execute procedure completes
exec ClrLibDb.dbo.Parallel_UseTransaction 'ReadUncommitted'
-- and then you can add sqls.
exec ClrLibDb.dbo.Parallel_AddSql 'sql1',
'insert into test.dbo.table_1 values (1, ''test1'')'
exec ClrLibDb.dbo.Parallel_AddSql 'sql2',
'insert into test.dbo.table_1 values (2, ''test2'')'
exec ClrLibDb.dbo.Parallel_AddSql 'sql3',
'insert into test.dbo.table_1 values (3, ''test3'')'
exec ClrLibDb.dbo.Parallel_AddSql 'sql4',
'insert into test.dbo.table_1 values (4, ''test4'')'
-- after all sqls are added you call Parallel_Execute
-- to start execution of all sqls in parallel
DECLARE @RC int
exec @RC = ClrLibDb.dbo.Parallel_execute
-- you can analyze error from return code from procedure
IF @RC != 0
BEGIN
DECLARE @ErrorMessage varchar(MAX)
SET @ErrorMessage = ClrLibDb.dbo.Parallel_GetErrorMessage()
print @ErrorMessage
END
-- or you can view execution results
-- using Parallel_GetExecutionResult() function
select * from ClrLibDb.dbo.parallel_GetExecutionResult()
And, here are 10 tables copying and indexing; a pseudo-example (for simplicity, just three tables :)):
exec ClrLibDb.dbo.Parallel_Declare 'Copy tables'
exec ClrLibDb.dbo.Parallel_UseTransaction 'ReadUncommitted'
exec ClrLibDb.dbo.Parallel_AddSql 'sql1',
'SELECT * INTO targetdb.dbo.Table1 FROM sourcedb.dbo.table1'
exec ClrLibDb.dbo.Parallel_AddSql 'sql2',
'SELECT * INTO targetdb.dbo.Table2 FROM sourcedb.dbo.table2'
exec ClrLibDb.dbo.Parallel_AddSql 'sql3',
'SELECT * INTO targetdb.dbo.Table3 FROM sourcedb.dbo.table3'
DECLARE @RC int
exec @RC = ClrLibDb.dbo.Parallel_execute
IF @RC != 0
BEGIN
DECLARE @ErrorMessage varchar(MAX)
SET @ErrorMessage = ClrLibDb.dbo.Parallel_GetErrorMessage()
RAISERROR(@ErrorMessage, 16, 1)
END
-- tables copy is complete, let's create indexes
exec ClrLibDb.dbo.Parallel_Declare 'Create indexes #1'
exec ClrLibDb.dbo.Parallel_UseTransaction 'ReadUncommitted'
exec ClrLibDb.dbo.Parallel_AddSql 'index1',
'CREATE INDEX IX01_Table1 ON targetdb.Table1 (<fields>)'
exec ClrLibDb.dbo.Parallel_AddSql 'index2',
'CREATE INDEX IX02_Table1 ON targetdb.Table1 (<fields>)'
exec ClrLibDb.dbo.Parallel_AddSql 'index3',
'CREATE INDEX IX03_Table1 ON targetdb.Table1 (<fields>)'
DECLARE @RC int
exec @RC = ClrLibDb.dbo.Parallel_execute
IF @RC != 0
BEGIN
DECLARE @ErrorMessage varchar(MAX)
SET @ErrorMessage = ClrLibDb.dbo.Parallel_GetErrorMessage()
RAISERROR(@ErrorMessage, 16, 1)
END
exec ClrLibDb.dbo.Parallel_Declare 'Create indexes #2'
exec ClrLibDb.dbo.Parallel_UseTransaction 'ReadUncommitted'
exec ClrLibDb.dbo.Parallel_AddSql 'index1',
'CREATE INDEX IX01_Table2 ON targetdb.Table2 (<fields>)'
exec ClrLibDb.dbo.Parallel_AddSql 'index2',
'CREATE INDEX IX02_Table2 ON targetdb.Table2 (<fields>)'
exec ClrLibDb.dbo.Parallel_AddSql 'index3',
'CREATE INDEX IX03_Table2 ON targetdb.Table2 (<fields>)'
DECLARE @RC int
exec @RC = ClrLibDb.dbo.Parallel_execute
IF @RC != 0
BEGIN
DECLARE @ErrorMessage varchar(MAX)
SET @ErrorMessage = ClrLibDb.dbo.Parallel_GetErrorMessage()
RAISERROR(@ErrorMessage, 16, 1)
END
exec ClrLibDb.dbo.Parallel_Declare 'Create indexes #3'
exec ClrLibDb.dbo.Parallel_UseTransaction 'ReadUncommitted'
exec ClrLibDb.dbo.Parallel_AddSql 'index1',
'CREATE INDEX IX01_Table3 ON targetdb.Table3 (<fields>)'
exec ClrLibDb.dbo.Parallel_AddSql 'index2',
'CREATE INDEX IX02_Table3 ON targetdb.Table3 (<fields>)'
exec ClrLibDb.dbo.Parallel_AddSql 'index3',
'CREATE INDEX IX03_Table3 ON targetdb.Table3 (<fields>)'
DECLARE @RC int
exec @RC = ClrLibDb.dbo.Parallel_execute
IF @RC != 0
BEGIN
DECLARE @ErrorMessage varchar(MAX)
SET @ErrorMessage = ClrLibDb.dbo.Parallel_GetErrorMessage()
RAISERROR(@ErrorMessage, 16, 1)
END
You can use your creativity to run parallel blocks inside other parallel blocks. Yes. These parallel blocks can be nested by placing some parallel blocks in Sotred Procedures. But, a transaction started in one parallel block will not be passed to nested parallel blocks. This is a limitation of SQL Server CLR integration.
What you have to know about how this is working:
master] and you have to provide a database name to the tables, Stored Procedures, and other objects you use in your SQL. You can see from the examples that I have used the targetdb and sourcedb databases.| You must Sign In to use this message board. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 15 Sep 2008 Editor: Smitha Vijayan |
Copyright 2008 by Alan Kaplan Everything else Copyright © CodeProject, 1999-2009 Web16 | Advertise on the Code Project |