Execute T-SQL in Parallel
From SQLServerPedia
|
See Also: Transact SQL Code Library
OverviewThis page describes how to create a CLR stored procedure which will allow you to execute T-SQL on a set of databases in parallel. Various issues are addressed such as limiting parallel threads and collecting errors return by each. Requirements
Important ConsiderationsThis is a powerful tool that gives those with access to it the ability to spawn as many threads as they choose on a server. It, like many other tools, could be used for evil and should be properly secured and should be treated with the same care used with sp_executesql or sp_MSforeachdb. Some things to keep in mind as you implement:
Program FlowThe stored procedure will take the following steps:
ImplementationConfiguring SQL ServerFirst the server must have CLR enabled. More information on this can be found here. Enable it by executing this script in SQL Server: sp_configure 'clr enabled', 1 GO RECONFIGURE GO Next the database to which the stored procedure will be deployed must have its trustworthy flag enabled. More information on this can be found here. Enable it by executing this script in SQL Server: ALTER DATABASE AdventureWorks SET TRUSTWORTHY ON GO Finally, the user you deploy as must be a member of the sysadmin fixed server role. Now SQL Server is ready for the stored procedure. Creating the ProjectThis method requires two portions of code. First is the Partial Public Class StoredProcedures which includes all CLR stored procedures contained in the Visual Studio project. Second is a special class we will create that can be run as a separate thread. This class will accept all our parameters and perform the actual T-SQL execution.
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlTypes
Imports System.Threading
Imports Microsoft.SqlServer.Server
Partial Public Class StoredProcedures
Imports System.Data.SqlClient
Imports System.Threading
Public Class ExecSql
Private ExecErrors As Generic.List(Of String)
Private ExecLocker As Object
Private ExecServer As String
Private ExecDB As String
Private ExecTSQL As String
Private ExecRetries As Integer
Public Sub New(ByVal Server As String, ByVal DB As String, ByVal TSQL As String, ByVal retries As Integer, _
ByRef errors As Generic.List(Of String), ByRef locker As Object)
Me.ExecServer = Server
Me.ExecDB = DB
Me.ExecTSQL = TSQL
Me.ExecRetries = retries
Me.ExecErrors = errors
Me.ExecLocker = locker
End Sub
Public Sub run()
Dim tries As Integer = 1
Dim conn As SqlConnection
Retry:
conn = New SqlConnection("Data Source=" & ExecServer & ";Initial Catalog=" & ExecDB & ";Integrated Security=SSPI;")
Try
conn.Open()
If conn.State = ConnectionState.Open Then
Dim cmd As SqlCommand = conn.CreateCommand()
cmd.CommandText = ExecTSQL
cmd.CommandTimeout = 0
cmd.ExecuteNonQuery()
cmd.Dispose()
conn.Close()
conn.Dispose()
Else
Throw New Exception("Unable to connect to SQL Server.")
End If
Catch e As Exception
If conn.State <> ConnectionState.Closed Then conn.Close()
conn.Dispose()
'System.Diagnostics.EventLog.WriteEntry("SQL_EXEC_PARALLEL", e.Message)
If tries <= ExecRetries Then
Thread.Sleep(5000)
tries += 1
GoTo Retry
Else
SyncLock ExecLocker
ExecErrors.Add(Me.ExecDB.PadRight(16, " ") + " : " + e.Message)
End SyncLock
End If
End Try
End Sub
End Class
If all went well, you should now have a stored procedure in your database which can be used like so:
EXEC AdventureWorks.dbo.EXEC_PARALLEL @DBs = 'msdb,AdventureWorks,master', @MaxDOP = 2,
@TSQL = 'EXEC ?.sys.sp_updatestats',
@msDelay = 5000, @Retries = 2
The above statement will update stats on the listed databases, two at a time, waiting 5 seconds between each thread start, and retrying twice for each thread that fails. Any thread which fails its final retry will have its error recorded and printed out when all threads are complete. The ?. is optional since each database is specified as the Initial Catalog when each new thread connects. Here is an example of output that includes some failures. In this example, the first four databases contain the table PERSON, and the last four do not. EXEC Adventureworks.dbo.EXEC_PARALLEL @DBs='foo_live,foo_test,foo_dev,foo_archive,foo_misc,staging,reports,legal', @MaxDOP=4, @TSQL='SELECT TOP 1 * FROM dbo.PERSON', @msDelay=1000, @Retries=1 Results in: 1:23 PM : Start : foo_live 1:23 PM : Stop : foo_live 1:23 PM : Start : foo_test 1:23 PM : Stop : foo_test 1:23 PM : Start : foo_dev 1:23 PM : Stop : foo_dev 1:23 PM : Start : foo_archive 1:23 PM : Stop : foo_archive 1:23 PM : Start : foo_misc 1:23 PM : Start : staging 1:23 PM : Start : reports 1:23 PM : Start : legal 1:23 PM : Stop : foo_misc 1:23 PM : Stop : staging 1:23 PM : Stop : reports 1:23 PM : Stop : legal All threads have stopped with 4 errors: foo_misc : Invalid object name 'dbo.PERSON'. staging : Invalid object name 'dbo.PERSON'. reports : Invalid object name 'dbo.PERSON'. legal : Invalid object name 'dbo.PERSON'. .Net SqlClient Data Provider: Msg 6522, Level 16, State 1, Procedure EXEC_PARALLEL, Line 0 A .NET Framework error occurred during execution of user-defined routine or aggregate "EXEC_PARALLEL": System.Exception: ERRORS OCCURRED. System.Exception: at DBA_SQL_UTIL.StoredProcedures.EXEC_PARALLEL(String DBs, Int32 MaxDOP, String TSQL, Int32 msDelay, Int32 Retries) Related Reading
Author CreditsThis wiki article was authored by Konrad Willmert to document, share and improve upon a method he created for executing T-SQL on multiple databases and in parallel. Konrad Willmert, a Programmer/Analyst for Indiana Wesleyan University since 2006, also serves as a reserve DBA for the institution. He has six years of experience building web applications based on MySQL for web design and hosting company Visionary Web. Konrad acquired the bulk of his SQL Server knowledge and experience at IWU while collaborating with the DBA on building and optimizing a data warehouse and custom ETL process from scratch. Konrad can be reached at: konrad.willmert@indwes.edu.
|