65.9K
CodeProject 正在变化。 阅读更多。
Home

一个用于在 Oracle 中生成级联删除 SQL 脚本的小型实用工具

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (7投票s)

2011年2月23日

CPOL

2分钟阅读

viewsIcon

28898

downloadIcon

429

本文介绍一个用于生成 Oracle 表级联删除脚本的实用工具。

引言

本文分享一个用于生成 Oracle 数据库中级联删除 SQL 脚本的小工具,并介绍其工作原理。

背景

最近,我为客户进行数据修复,需要删除数十个表中的数据。这些表都有引用表,而这些引用表又有引用表,以此类推。要删除一个表,我首先必须删除它的引用表,要删除它的引用表,我首先必须删除引用表的引用表,依此类推。在一种情况下,有六层深度,并且需要首先删除 220 个表,顺序必须严格。我创建了这个实用工具来生成级联删除脚本,以避免我进行繁琐且容易出错的工作。我将其发布在 CodeProject 上,以便其他人可以使用,因为我从该网站获得了很大的帮助。该实用工具目前支持 Oracle 数据库。但它可以扩展以支持 SQLServer。提供了源代码,以便其他人可以扩展它。

Using the Code

该实用工具的代码结构由三个部分组成。底部部分是获取数据库字典信息,例如主键和引用表。中间部分是实现生成脚本的核心逻辑。顶部部分是 UI,一个 Windows 应用程序界面,允许用户生成脚本。该实用工具使用 VB.NET 和 Visual Studio 2010 实现。

让我们从底部部分开始介绍。在底部部分,引入了一个接口,以便可以扩展它以支持不同的数据库。

Public Interface IDictionaryInfo

    ' Get Primary Key of a table
    Function GetPKColName(ByVal strTableName As String) As String

    ' Get foreign key reference table and the foreign key column in that table
    '   A list of KeyValuePair rather than a dictionary is used as
    '   return type since one reference table may have 1+ reference column.
    Function GetFKReferences(ByVal strTableName As String) _
    	As List(Of KeyValuePair(Of String, String))

End Interface

这是支持 Oracle 数据库的接口实现。

' the implementation of the IDictionaryInfo for oracle
Public Class OracleDictionaryInfo
    Implements IDictionaryInfo

    ' get the primary key for a table
    Public Function GetPKName(ByVal strTableName As String) _
    	As String Implements IDictionaryInfo.GetPKColName
        strTableName = strTableName.ToUpper		'Oracle tables are saved
        				' in uppercase in Oracle dictionary.

        'SQL to retrieve oracle dictionary to get primary key
        Dim strSQl As String = String.Format_
        	("SELECT cols.column_name FROM user_constraints cons, _
        	user_cons_columns cols WHERE cols.table_name = '{0}' _
        	AND cons.constraint_type = 'P' AND cons.constraint_name = _
        	cols.constraint_name", strTableName)

        Dim dt = GetDataTable(strSQl)

        If dt.Rows.Count > 0 Then
            Return dt.Rows(0)(0)
        Else
            Return String.Empty
        End If
    End Function


    ' get the foreign key tables and its reference columns
    Public Function GetFKReferences(ByVal strTableName As String) _
    As System.Collections.Generic.List(Of _
    System.Collections.Generic.KeyValuePair(Of String, String)) _
    Implements IDictionaryInfo.GetFKReferences
        strTableName = strTableName.ToUpper

        Dim strSQl As String = String.Format_
        ("select C.TABLE_NAME, C.COLUMN_NAME from user_cons_columns _
        c where c.CONSTRAINT_NAME IN ( Select constraint_name from_
        (user_constraints) where constraint_type='R'  and _
        r_constraint_name in (select constraint_name from user_constraints _
        where constraint_type in ('P','U') and table_name='{0}'))", _
        strTableName)

        Dim dt = GetDataTable(strSQl)

        Dim tnq = From dr In dt.Rows Select New KeyValuePair_
        (Of String, String)(CType(dr(0), String), CType(dr(1), String))
        Return tnq.ToList()
    End Function

End Class

中间部分收集主键和引用的数据库字典信息。它调用一个内部递归函数以级联方式生成删除脚本。

' Core class to generate script for the table
Public Class GenScriptCore

    Private iDic As IDictionaryInfo

    Private Sub New()   ' no instance is allowed
    End Sub

    ' Main function to generate script.  Prepare database type, _
    connection string and table name to call the function
    Public Shared Function GenScript(ByVal enumDB As DB, _
    ByVal strConnection As String, ByVal strTableName As String) As String
        Dim oc As New GenScriptCore

        Select Case [enumDB]
            Case DB.Oracle
                oc.iDic = New OracleDictionaryInfo(strConnection)
            Case DB.SQlServer
                Throw New Exception("SQL server is not supported yet. _
                But you can implement it by implementing the interface of _
                IDictionaryInfo for SQL Server")
        End Select

        Dim strPKCol = oc.iDic.GetPKColName(strTableName)

        If String.IsNullOrEmpty(strPKCol) Then
            Throw New Exception(String.Format("table '{0}' _
            does not exist or has no primary key / unique key", strTableName))
        End If

        Return oc.gs(strTableName, strPKCol) ' call the main method
    End Function

	' Core function to generate script though recursive calling
    Private Function gs(ByVal strTableName As String, _
    ByVal strFilterColumn As String, _
    Optional ByVal strInFilter As String = Nothing) As String

        Dim sb As New System.Text.StringBuilder
        Dim strPKCol = iDic.GetPKColName(strTableName)

        If String.IsNullOrEmpty(strPKCol) Then
            Throw New Exception(String.Format("table '{0}' _
            has no primary key / unique key", strTableName))
        End If

        ' define where clause for the reference tables
        Dim strNextLevelWhereClause As String
        If Not String.IsNullOrEmpty(strInFilter) Then
            strNextLevelWhereClause = String.Format("{0} _
            in ({1})", strFilterColumn, strInFilter)
        Else
            strNextLevelWhereClause = String.Format("{0} = _
            p_{0}", strFilterColumn)
        End If

        Dim strNextLevelFilter = String.Format("Select {0} from _
        {1} where {2}", strPKCol, strTableName, strNextLevelWhereClause)

        ' find the foreign key reference tables and delete them first
        Dim lstFKTableFKCol = iDic.GetFKReferences(strTableName)
        For Each kv In lstFKTableFKCol
            sb.AppendLine(gs(kv.Key, kv.Value, strNextLevelFilter))
        Next

        '  delete itself
        Dim strDeleteWhereClause As String
        If Not String.IsNullOrEmpty(strInFilter) Then
            strDeleteWhereClause = String.Format_
            ("{0} in ({1})", strFilterColumn, strInFilter)
        Else
            strDeleteWhereClause = String.Format_
            ("{0} = p_{0}", strFilterColumn)
        End If

        sb.AppendFormat("delete {0} where {1};", _
        strTableName, strDeleteWhereClause)
        sb.AppendLine()

        Return sb.ToString
    End Function

End Class

UI 收集用户输入,包括数据库类型、连接字符串和表名。它验证输入并调用中间层以生成脚本,该脚本在窗口内显示,并同时复制到剪贴板。如果应用程序抛出异常,UI 将以红色显示错误消息。

Click to enlarge image

关注点

这个小工具对我帮助很大。我希望这个小工具也能帮助其他人。

历史

  • 初始版本:2011 年 2 月 17 日

作者 Ronald Cao 是 TP Systems Co. 的高级 .NET 开发人员。

© . All rights reserved.