【问题标题】:How to automate changes in stored procedures in SQL Server?如何自动更改 SQL Server 中的存储过程?
【发布时间】:2016-10-10 23:28:35
【问题描述】:

我在 SQL Server 中有两个数据库,其中一个数据库 (ConfigDb) 引用其他数据库 (DataDb)。 ConfigDb 中的许多存储过程和函数使用DataDb 中的表和函数/SP。它们被引用为[DataDb].[dbo].[the_object]

现在,我需要克隆数据库以测试版本,即将它们从备份中恢复为 ConfigDb_TestDataDb_Test。显然,ConfigDb_Test 引用了DataDb,而不是DataDb_Test

有什么技巧可以比通过 SP 打开 SP 并手动编辑更好地处理这个问题?

编辑

作为参考,我将实用程序放在GitHub

【问题讨论】:

  • 出于这个确切原因,我尽我所能避免交叉引用。可能有一个“正确”的解决方案,但我会尝试使用可配置的连接字符串将这些流程重新工作到 SSIS 包中,或者使用 CLR 存储过程,或者获取一个新的 SQL 实例,这样你就可以不用管数据库名称了。动态 SQL 也是一种选择,但这是一个非常核心的解决方案。
  • 你可以访问 VS/c# 吗?你可以编写一个应用程序来自动打开/更改每个 proc(我以前做过,我可以给你代码)。
  • @JiggsJedi 是的,我有,能有一些东西开始就好了
  • @TT 是的,我最终在内部使用了 SMO。基本上它是一个简单的单一用途 WinForm 应用程序,类似于 JiggsJedi 解决方案。谢谢。
  • @TT。我也不知道 SMO,谢谢!

标签: sql-server


【解决方案1】:

好的,它在 VB(不是 c#)中——而且我不是 .net 人,所以我确信有更好的方法来做到这一点。

我提醒您逐步执行并观察代码的工作情况,但我经常使用它,而且效果很好!

输入服务器名称/凭据后,将出现一个数据库列表(不包括系统数据库)。选择要运行的数据库(检查列表)并输入查找/替换文本字符串。当您单击开始时,将打开/扫描每个 proc,并在匹配的字符串上运行 REPLACE() - 所以要小心您在 FIND/REPLACE 文本框中输入的内容!

来了……

请谨慎使用,使用风险自负。

form2.vb

对象名称(按顺序)

  • cmbServer(组合框)
  • chkSSPI(复选框)
  • txtUser(文本框)
  • txtPass(文本框)
  • btnConnect(按钮)
  • btnExit(按钮)

代码背后:

Imports System.Data.SqlClient

Public Class Form2
    Public objConn As SqlConnection = New SqlConnection()

    Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        cmbServer.SelectedIndex = 0
        chkSSPI.Checked = True
    End Sub

    Private Sub btnExit_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExit.Click
        Application.Exit()
    End Sub

    Private Sub chkSSPI_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles chkSSPI.CheckedChanged
        txtUser.Enabled = Not chkSSPI.Checked
        txtPass.Enabled = Not chkSSPI.Checked
        txtUser.Select()
    End Sub

    Private Sub btnConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConnect.Click
        If chkSSPI.Checked = False Then
            objConn.ConnectionString = String.Format("Data Source={0};Initial Catalog=master;User ID={1};Password={2};", cmbServer.Text, txtUser.Text, txtPass.Text)
        Else
            objConn.ConnectionString = String.Format("Data Source={0}; Initial Catalog=master; Integrated Security=SSPI", cmbServer.Text)
        End If
        Dim frm2 As Form = New frmDBList()
        Try
           objConn.Open()
        Catch ex As Exception
            MessageBox.Show(ex.Message)
        End Try

        Hide()
        frm2.Show()
    End Sub

    Private Sub txtUser_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles txtUser.TextChanged

    End Sub
End Class

frmDBList.vb

对象名称(按顺序):

  • clbDatabase(选中列表框)
  • txt查找(文本框)
  • txtReplace(文本框)
  • lblDBName(标签)
  • lblProcNum(标签)[0]
  • lblProcCount(标签)[123]
  • lblProcName(标签)
  • btnCheckAll(按钮)
  • btnUnCheckAll(按钮)
  • btnStart(按钮)
  • btnClose(按钮)

代码背后:

Imports System.Data.SqlClient
Imports System.IO

Public Class frmDBList
    Dim procText As String
    Dim procList As New ArrayList
    Dim errorLog As New ArrayList
    Dim dbcount As Int16
    Dim proccount As Int16
    Dim replacecount As Int16
    Dim procupdate As Boolean

    Public Sub LogError(ByVal text As String, ByVal dbname As String, ByVal procname As String)
        errorLog.Add("db=" + dbname + " proc=" + procname + " error=" + text)
    End Sub

    Public Sub SaveLog()
        Dim datetime As String = Now.ToString()
        datetime = Replace(datetime, "/", "")
        datetime = Replace(datetime, ":", "")
        Dim filename As String = "c:\procchanger_errorlog " + datetime + ".txt"
        Dim objWriter As New System.IO.StreamWriter(filename)
        For c As Int16 = 0 To errorLog.Count - 1
            objWriter.WriteLine(errorLog(c).ToString())
        Next
        objWriter.Close()
    End Sub

    Public Sub AddListBoxItem(ByVal Item As Object, ByVal Check As Boolean)
        clbDatabase.Items.Add(Item, Check)
    End Sub

    Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
        btnStart.Enabled = False
        btnClose.Enabled = False
        errorLog.Clear()
        dbcount = 0
        proccount = 0
        replacecount = 0
        grpProgress.Visible = True
        If clbDatabase.SelectedItems.Count = 0 Then
            MsgBox("Please select at least one database to process.", vbOKOnly)
        Else
            For i As Integer = 0 To clbDatabase.Items.Count - 1
                If clbDatabase.GetItemChecked(i) = True Then
                    lblDBName.Text = clbDatabase.Items(i).ToString()
                    dbcount += 1
                    procList.Clear()
                    GetProcList(clbDatabase.Items(i).ToString())
                End If
            Next
            MsgBox("Complete.  Replaced " + replacecount.ToString() + " occurrences, in " + proccount.ToString() + " stored procedures, across " + dbcount.ToString() + " databases.")
            If errorLog.Count > 0 Then
                SaveLog()
            End If

            grpProgress.Visible = False
            For i As Integer = 0 To clbDatabase.Items.Count - 1
                clbDatabase.SetItemChecked(i, CheckState.Unchecked)
            Next
        End If
        If Form2.objConn.State = ConnectionState.Open Then Form2.objConn.Close()
        btnStart.Enabled = True
        btnClose.Enabled = True
    End Sub

    Public Sub GetProcList(ByVal dbname As String)
        If Form2.objConn.State = ConnectionState.Closed Then
            If Form2.chkSSPI.Checked = False Then
                Form2.objConn.ConnectionString = String.Format("Data Source={0};Initial Catalog=" + dbname + ";User ID={1};Password={2};", Form2.cmbServer.Text, Form2.txtUser.Text, Form2.txtPass.Text)
            Else
                Form2.objConn.ConnectionString = String.Format("Data Source={0}; Initial Catalog=" + dbname + "; Integrated Security=SSPI", Form2.cmbServer.Text)
            End If
            Try
                Form2.objConn.Open()
            Catch ex As Exception
                LogError(ex.Message, dbname, "")
            End Try
        End If

        Try
            Dim sqlcmd = "select name from sysobjects where xtype='P' and name not like 'dt_%'"

            Using cmd As New SqlCommand(sqlcmd, Form2.objConn)
                Using reader = cmd.ExecuteReader()
                    If reader.HasRows Then
                        While reader.Read()
                            procList.Add(reader("name").ToString())
                        End While
                    End If
                End Using
            End Using
        Catch ex As Exception
            LogError(ex.Message, dbname, "")
        End Try
        lblProcCount.Text = procList.Count
        proccount = procList.Count
        For c = 0 To procList.Count - 1
            lblProcNum.Text = c.ToString()
            lblProcName.Text = procList(c).ToString()
            Refresh()
            procupdate = False
            AlterProc(dbname, procList(c).ToString())
        Next
        If Form2.objConn.State = ConnectionState.Open Then Form2.objConn.Close()
    End Sub

    Public Sub AlterProc(ByVal dbname As String, ByVal procname As String)
        If Form2.objConn.State = ConnectionState.Closed Then
            If Form2.chkSSPI.Checked = False Then
                Form2.objConn.ConnectionString = String.Format("Data Source={0};Initial Catalog=" + dbname + ";User ID={1};Password={2};", Form2.cmbServer.Text, Form2.txtUser.Text, Form2.txtPass.Text)
            Else
                Form2.objConn.ConnectionString = String.Format("Data Source={0}; Initial Catalog=" + dbname + "; Integrated Security=SSPI", Form2.cmbServer.Text)
            End If
            Try
                Form2.objConn.Open()
            Catch ex As Exception
                LogError(ex.Message, dbname, "")
            End Try
        End If
        Try
            Dim sqlcmd = "select * from " + dbname + ".dbo.sysobjects o inner join " + dbname + ".dbo.syscomments c on o.id = c.id where name='" + procname + "'"

            Using cmd As New SqlCommand(sqlcmd, Form2.objConn)
                procText = ""
                Using reader = cmd.ExecuteReader()
                    If reader.HasRows Then
                        While reader.Read()
                            procText = procText + reader("text")
                        End While
                    End If
                End Using

                Dim arrProcData() = Split(procText, vbNewLine)

                Dim c As Integer
                procText = ""
                For c = 0 To UBound(arrProcData)
                    If InStr(UCase(arrProcData(c)), "CREATE") > 0 And InStr(UCase(arrProcData(c)), "PROCEDURE") > 0 Then
                        arrProcData(c) = Replace(Replace(Replace(arrProcData(c), "CREATE", "ALTER"), "create", "alter"), "Create", "Alter")
                    End If
                    If InStr(UCase(arrProcData(c)), UCase(txtFind.Text)) > 0 Then
                        arrProcData(c) = Replace(UCase(arrProcData(c)), UCase(txtFind.Text), UCase(txtReplace.Text))
                        replacecount += 1
                        procupdate = True
                    End If
                    procText = procText + arrProcData(c) + vbNewLine
                Next

            End Using
        Catch ex As Exception
            LogError(ex.Message, dbname, procname)
        End Try

        If procupdate = True Then
            Try
                Dim sqlcmd = procText
                Using cmd As New SqlCommand(sqlcmd, Form2.objConn)
                    cmd.ExecuteNonQuery()
                End Using
            Catch ex As Exception
                LogError(ex.Message, dbname, procname)
            End Try
        End If
    End Sub

    Private Sub frmDBList_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        grpProgress.Visible = False
        Try
            Dim sqlcmd = "select name from master.dbo.sysdatabases where name not in ('msdb','master','temdb')"

            Using cmd As New SqlCommand(sqlcmd, Form2.objConn)
                Using reader = cmd.ExecuteReader()
                    If reader.HasRows Then
                        While reader.Read()
                            AddListBoxItem(reader("name").ToString(), CheckState.Unchecked)
                        End While
                    End If
                End Using
            End Using

        Catch ex As Exception
            LogError(ex.Message, "master", "")
        End Try
        Form2.objConn.Close()
    End Sub

    Private Sub btnClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClose.Click
        Application.Exit()
    End Sub

    Private Sub btnCheckAll_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCheckAll.Click
        For i As Integer = 0 To clbDatabase.Items.Count - 1
            clbDatabase.SetItemChecked(i, CheckState.Checked)
        Next
    End Sub

    Private Sub btnUnCheckAll_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnUnCheckAll.Click
        For i As Integer = 0 To clbDatabase.Items.Count - 1
            clbDatabase.SetItemChecked(i, CheckState.Unchecked)
        Next
    End Sub

End Class

【讨论】:

    猜你喜欢
    • 2012-03-15
    • 2020-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多