我需要从所有具有电子邮件警报的存储过程中更改配置文件名称。 是否可以通过SQL Server中的查询更改存储过程。
这将帮助我更新所有具有电子邮件警报的存储过程。
SELECT object_definition(object_id) as [Proc Definition]
, OBJECT_NAME(object_id) [Stored Proc Name]
FROM sys.objects
WHERE type='P' and object_definition(object_id) like '%sp_send_dbmail%'
通过上述查询,我需要更改存储过程的主体。
答案 0 :(得分:1)
您需要运行循环以更新所有过程,并使用sp_helptext获取过程代码并将其存储在字符串变量中。替换变量中所需的字符串值,然后使用更新的字符串变量来运行alter命令并更新proc。
答案 1 :(得分:0)
可以对存储过程代码进行大量更改,但是由于T-SQL构造的多种变体,任务可能相当复杂。简单的字符串替换在某些情况下可能有效,但它很脆弱,因为要替换的文本可能会在其他不应更改的上下文中出现。例如,如果您的原始配置文件名为“邮件”,则替换为哑文本也会更改“ sp_send_dbmail”过程的名称。
下面是一个PowerShell示例,该示例使用Microsoft.SqlServer.TransactSql.ScriptDom程序集更智能地解析T-SQL代码并进行有针对性的更改。这不是一个完整的解决方案,它可以更改所有出现的配置文件名称(例如,局部变量分配,位置sp_send_db_mail参数等),但会更改sp_send_dbmail命名参数语法中指定的值,并且比替换文本更可靠。您可以在开发环境中测试和调整代码以适应您的需求。
此外,作为免责声明,以这种方式更改解析器令牌文本是未记录的,因此后果自负。
Function Replace-DatabaseMailProfileNames($script, $originalProfileName, $newProfileName) {
# use the appropriate TSqlParser version for the target SQL Server version
$parser = New-Object Microsoft.SqlServer.TransactSql.ScriptDom.TSql140Parser($true)
$parseErrors = New-Object System.Collections.Generic.List[Microsoft.SqlServer.TransactSql.ScriptDom.ParseError]
$scriptReader = New-Object System.IO.StringReader($script)
$fragment = $parser.Parse($scriptReader, [ref]$parseErrors)
$scriptChanged = $false
if($parseErrors.Count -eq 0) {
$fragment.Batches[0].Statements[0].ScriptTokenStream[$fragment.Batches[0].Statements[0].FirstTokenIndex].Text = "ALTER"
foreach($statement in $fragment.Batches[0].Statements[0].StatementList.Statements) {
switch($statement.GetType().ToString())
{
"Microsoft.SqlServer.TransactSql.ScriptDom.ExecuteStatement" {
if($statement.ExecuteSpecification.ExecutableEntity.ProcedureReference.ProcedureReference.Name.BaseIdentifier.Value.ToLower() -eq "sp_send_dbmail") {
foreach($parameter in $statement.ExecuteSpecification.ExecutableEntity.Parameters) {
if(($parameter.Variable.Name.ToLower() -eq "@profile_name") -and ($parameter.ParameterValue.Value.ToLower() -eq $originalProfileName.ToLower())) {
$parameter.ParameterValue.ScriptTokenStream[$parameter.ParameterValue.FirstTokenIndex].Text = $newProfileName
$scriptChanged = $true
}
}
}
break
}
}
}
if($scriptChanged) {
$fragmentText = New-Object System.Text.StringBuilder
# reconstrunct script from tokens containing new values
for($i = $fragment.FirstTokenIndex; $i -le $fragment.LastTokenIndex; ++$i) {
[void]$fragmentText.Append($fragment.ScriptTokenStream[$i].Text)
}
return $fragmentText.ToString()
}
else {
# return null to indicate script was not changed
return $null
}
}
else {
throw "Error(s) parsing script"
}
}
############
### main ###
############
# Specify path to Microsoft.SqlServer.TransactSql.ScriptDom.dll.
# This assembly is available from the Microsoft DacFx NuGet package: https://www.nuget.org/packages/Microsoft.SqlServer.DacFx.x64/
Add-Type -Path "C:\Temp\Microsoft.SqlServer.TransactSql.ScriptDom.dll"
$originalProfileName = "Original mail profile name"
$newProfileName = "'New mail profile name'"
try {
$connectionString = "Data Source=.;Initial Catalog=YourDatabase;Integrated Security=SSPI;MultipleActiveResultSets=True";
$connection = New-Object System.Data.SqlClient.SqlConnection($connectionString);
$query = @"
SELECT QUOTENAME(OBJECT_SCHEMA_NAME(p.object_id)) + '.' + QUOTENAME(p.name) AS procedure_name, sm.definition
FROM sys.procedures AS p
JOIN sys.sql_modules AS sm ON sm.object_id = p.object_id
WHERE sm.definition LIKE N'%sp_send_dbmail%';
"@
$selectCommand = New-Object System.Data.SqlClient.SqlCommand($query, $connection)
$connection.Open();
$reader = $selectCommand.ExecuteReader()
while($reader.Read()) {
$newScript = Replace-DatabaseMailProfileNames -script "$($reader["definition"])" -originalProfileName $originalProfileName -newProfileName $newProfileName
if($newScript -ne $null) {
$alterCommand = New-Object System.Data.SqlClient.SqlCommand($newScript, $connection)
[void]$alterCommand.ExecuteNonQuery()
"Stored procedure $($reader["procedure_name"]) changed"
}
}
}
catch {
throw
}