Advertisement
Guest User

Untitled

a guest
Dec 23rd, 2016
125
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.75 KB | None | 0 0
  1. <#
  2. .SYNOPSIS
  3. Indexes tables in a database if they have a high fragmentation
  4.  
  5. .DESCRIPTION
  6. This runbook indexes all of the tables in a given database if the fragmentation is
  7. above a certain percentage.
  8. It highlights how to break up calls into smaller chunks,
  9. in this case each table in a database, and use checkpoints.
  10. This allows the runbook job to resume for the next chunk of work even if the
  11. fairshare feature of Azure Automation puts the job back into the queue every 30 minutes
  12.  
  13. .PARAMETER SqlServer
  14. Name of the SqlServer
  15.  
  16. .PARAMETER Database
  17. Name of the database
  18.  
  19. .PARAMETER SQLCredentialName
  20. Name of the Automation PowerShell credential setting from the Automation asset store.
  21. This setting stores the username and password for the SQL Azure server
  22.  
  23. .PARAMETER FragPercentage
  24. Optional parameter for specifying over what percentage fragmentation to index database
  25. Default is 20 percent
  26.  
  27. .PARAMETER RebuildOffline
  28. Optional parameter to rebuild indexes offline if online fails
  29. Default is false
  30.  
  31. .PARAMETER Table
  32. Optional parameter for specifying a specific table to index
  33. Default is all tables
  34.  
  35. .PARAMETER SqlServerPort
  36. Optional parameter for specifying the SQL port
  37. Default is 1433
  38.  
  39. .EXAMPLE
  40. Update-SQLIndexRunbook -SqlServer "server.database.windows.net" -Database "Finance" -SQLCredentialName "FinanceCredentials"
  41.  
  42. .EXAMPLE
  43. Update-SQLIndexRunbook -SqlServer "server.database.windows.net" -Database "Finance" -SQLCredentialName "FinanceCredentials" -FragPercentage 30
  44.  
  45. .EXAMPLE
  46. Update-SQLIndexRunbook -SqlServer "server.database.windows.net" -Database "Finance" -SQLCredentialName "FinanceCredentials" -Table "Customers" -RebuildOffline $True
  47.  
  48. .NOTES
  49. AUTHOR: System Center Automation Team
  50. LASTEDIT: Oct 8th, 2014
  51. #>
  52. workflow Update-SQLIndexRunbook
  53. {
  54. param(
  55. [parameter(Mandatory=$True)]
  56. [string] $SqlServer,
  57.  
  58. [parameter(Mandatory=$True)]
  59. [string] $Database,
  60.  
  61. [parameter(Mandatory=$True)]
  62. [string] $SQLCredentialName,
  63.  
  64. [parameter(Mandatory=$False)]
  65. [int] $FragPercentage = 20,
  66.  
  67. [parameter(Mandatory=$False)]
  68. [int] $SqlServerPort = 1433,
  69.  
  70. [parameter(Mandatory=$False)]
  71. [boolean] $RebuildOffline = $False,
  72.  
  73. [parameter(Mandatory=$False)]
  74. [string] $Table
  75.  
  76. )
  77.  
  78. # Get the stored username and password from the Automation credential
  79. $SqlCredential = Get-AutomationPSCredential -Name $SQLCredentialName
  80. if ($SqlCredential -eq $null)
  81. {
  82. throw "Could not retrieve '$SQLCredentialName' credential asset. Check that you created this first in the Automation service."
  83. }
  84.  
  85. $SqlUsername = $SqlCredential.UserName
  86. $SqlPass = $SqlCredential.GetNetworkCredential().Password
  87.  
  88. $TableNames = Inlinescript {
  89.  
  90. # Define the connection to the SQL Database
  91. $Conn = New-Object System.Data.SqlClient.SqlConnection("Server=tcp:$using:SqlServer,$using:SqlServerPort;Database=$using:Database;User ID=$using:SqlUsername;Password=$using:SqlPass;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;")
  92.  
  93. # Open the SQL connection
  94. $Conn.Open()
  95.  
  96. # SQL command to find tables and their average fragmentation
  97. $SQLCommandString = @"
  98. SELECT a.object_id, avg_fragmentation_in_percent
  99. FROM sys.dm_db_index_physical_stats (
  100. DB_ID(N'$Database')
  101. , OBJECT_ID(0)
  102. , NULL
  103. , NULL
  104. , NULL) AS a
  105. JOIN sys.indexes AS b
  106. ON a.object_id = b.object_id AND a.index_id = b.index_id;
  107. "@
  108. # Return the tables with their corresponding average fragmentation
  109. $Cmd=new-object system.Data.SqlClient.SqlCommand($SQLCommandString, $Conn)
  110. $Cmd.CommandTimeout=120
  111.  
  112. # Execute the SQL command
  113. $FragmentedTable=New-Object system.Data.DataSet
  114. $Da=New-Object system.Data.SqlClient.SqlDataAdapter($Cmd)
  115. [void]$Da.fill($FragmentedTable)
  116.  
  117.  
  118. # Get the list of tables with their object ids
  119. $SQLCommandString = @"
  120. SELECT t.name AS TableName, t.OBJECT_ID FROM sys.tables t
  121. "@
  122.  
  123. $Cmd=new-object system.Data.SqlClient.SqlCommand($SQLCommandString, $Conn)
  124. $Cmd.CommandTimeout=120
  125.  
  126. # Execute the SQL command
  127. $TableSchema =New-Object system.Data.DataSet
  128. $Da=New-Object system.Data.SqlClient.SqlDataAdapter($Cmd)
  129. [void]$Da.fill($TableSchema)
  130.  
  131.  
  132. # Return the table names that have high fragmentation
  133. ForEach ($FragTable in $FragmentedTable.Tables[0])
  134. {
  135. Write-Verbose ("Table Object ID:" + $FragTable.Item("object_id"))
  136. Write-Verbose ("Fragmentation:" + $FragTable.Item("avg_fragmentation_in_percent"))
  137.  
  138. If ($FragTable.avg_fragmentation_in_percent -ge $Using:FragPercentage)
  139. {
  140. # Table is fragmented. Return this table for indexing by finding its name
  141. ForEach($Id in $TableSchema.Tables[0])
  142. {
  143. if ($Id.OBJECT_ID -eq $FragTable.object_id.ToString())
  144. {
  145. # Found the table name for this table object id. Return it
  146. Write-Verbose ("Found a table to index! : " + $Id.Item("TableName"))
  147. $Id.TableName
  148. }
  149. }
  150. }
  151. }
  152.  
  153. $Conn.Close()
  154. }
  155.  
  156. # If a specific table was specified, then find this table if it needs to indexed, otherwise
  157. # set the TableNames to $null since we shouldn't process any other tables.
  158. If ($Table)
  159. {
  160. Write-Verbose ("Single Table specified: $Table")
  161. If ($TableNames -contains $Table)
  162. {
  163. $TableNames = $Table
  164. }
  165. Else
  166. {
  167. # Remove other tables since only a specific table was specified.
  168. Write-Verbose ("Table not found: $Table")
  169. $TableNames = $Null
  170. }
  171. }
  172.  
  173. # Interate through tables with high fragmentation and rebuild indexes
  174. ForEach ($TableName in $TableNames)
  175. {
  176. Write-Verbose "Creating checkpoint"
  177. Checkpoint-Workflow
  178. Write-Verbose "Indexing Table $TableName..."
  179.  
  180. InlineScript {
  181.  
  182. $SQLCommandString = @"
  183. EXEC('ALTER INDEX ALL ON $Using:TableName REBUILD with (ONLINE=ON)')
  184. "@
  185.  
  186. # Define the connection to the SQL Database
  187. $Conn = New-Object System.Data.SqlClient.SqlConnection("Server=tcp:$using:SqlServer,$using:SqlServerPort;Database=$using:Database;User ID=$using:SqlUsername;Password=$using:SqlPass;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;")
  188.  
  189. # Open the SQL connection
  190. $Conn.Open()
  191.  
  192. # Define the SQL command to run. In this case we are getting the number of rows in the table
  193. $Cmd=new-object system.Data.SqlClient.SqlCommand($SQLCommandString, $Conn)
  194. # Set the Timeout to be less than 30 minutes since the job will get queued if > 30
  195. # Setting to 25 minutes to be safe.
  196. $Cmd.CommandTimeout=1500
  197.  
  198. # Execute the SQL command
  199. Try
  200. {
  201. $Ds=New-Object system.Data.DataSet
  202. $Da=New-Object system.Data.SqlClient.SqlDataAdapter($Cmd)
  203. [void]$Da.fill($Ds)
  204. }
  205. Catch
  206. {
  207. if (($_.Exception -match "offline") -and ($Using:RebuildOffline) )
  208. {
  209. Write-Verbose ("Building table $Using:TableName offline")
  210. $SQLCommandString = @"
  211. EXEC('ALTER INDEX ALL ON $Using:TableName REBUILD')
  212. "@
  213.  
  214. # Define the SQL command to run.
  215. $Cmd=new-object system.Data.SqlClient.SqlCommand($SQLCommandString, $Conn)
  216. # Set the Timeout to be less than 30 minutes since the job will get queued if > 30
  217. # Setting to 25 minutes to be safe.
  218. $Cmd.CommandTimeout=1500
  219.  
  220. # Execute the SQL command
  221. $Ds=New-Object system.Data.DataSet
  222. $Da=New-Object system.Data.SqlClient.SqlDataAdapter($Cmd)
  223. [void]$Da.fill($Ds)
  224. }
  225. Else
  226. {
  227. # Will catch the exception here so other tables can be processed.
  228. Write-Error "Table $Using:TableName could not be indexed. Investigate indexing each index instead of the complete table $_"
  229. }
  230. }
  231. # Close the SQL connection
  232. $Conn.Close()
  233. }
  234. }
  235.  
  236. Write-Verbose "Finished Indexing"
  237. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement