代码之家  ›  专栏  ›  技术社区  ›  lamcro

在MS Access中导入/导出关系

  •  4
  • lamcro  · 技术社区  · 16 年前

    我有几个具有精确表结构的mdb文件。我必须将主表的主键从“自动编号”更改为“编号”,这意味着我必须:

    1. 删除主表具有的所有关系
    2. 更改主表
    3. 再次创建关系…所有的桌子。

    是否有任何方法可以从一个文件中导出关系并将其导入到所有其他文件中?

    我相信这可以用一些宏/vb代码来完成。有人能举个例子吗?

    谢谢。

    4 回复  |  直到 9 年前
        1
  •  12
  •   Patrick Cuff    16 年前

    不是一个完整的解决方案,但这可能会让你…

    以下函数将打印出所有关系的元数据。更改此项以保存到您喜欢的任何格式(csv、制表符分隔、xml等)的文件:

    Function PrintRelationships()
        For Each rel In CurrentDb.Relations
            With rel
                Debug.Print "Name: " & .Name
                Debug.Print "Attributes: " & .Attributes
                Debug.Print "Table: " & .Table
                Debug.Print "ForeignTable: " & .ForeignTable
    
                Debug.Print "Fields:"
                For Each fld In .Fields
                    Debug.Print "Field: " & fld.Name
                Next
            End With
        Next
    End Function
    

    此函数将删除数据库中的所有关系:

    Function DropRelationships()
        With CurrentDb
            For Each rel In .Relations
                .Relations.Delete Name:=rel.Name
            Next
        End With
    End Function
    

    此函数将创建关系。您必须迭代保存的关系数据的文件。

    Function CreateRelationships()
        With CurrentDb
            Set rel = .CreateRelation(Name:="[rel.Name]", Table:="[rel.Table]", ForeignTable:="[rel.FireignTable]", Attributes:=[rel.Attributes])
            rel.Fields.Append rel.CreateField("[fld.Name for relation]")
            rel.Fields("[fld.Name for relation]").ForeignName = "[fld.Name for relation]"
            .Relations.Append rel
        End With
    End Function
    

    由于时间限制,错误处理和IO被忽略(必须让孩子们睡觉)。

    希望这有帮助。

        2
  •  1
  •   Fionnuala    16 年前

    我突然想到,您可以使用在进行任何更改之前所做的文件备份来恢复索引和关系。这是一些注释。

    Sub RunExamples()
    Dim strCopyMDB As String
    Dim fs As FileSystemObject
    Dim blnFound As Boolean
    Dim i
    
    ' This code is not intended for general users, it is sample code built '
    ' around the OP '
    'You will need a reference to the Microsoft DAO 3.x Object Library '
    'This line causes an error, but it will run '
    'It is not suitable for anything other than saving a little time '
    'when setting up a new database '
    Application.References.AddFromFile ("C:\Program Files\Common Files\Microsoft Shared\DAO\dao360.dll")
    
    'You must first create a back-up copy '
    Set fs = CreateObject("Scripting.FileSystemObject")
    
    strCopyMDB = CurrentProject.Path & "\c.mdb"
    blnFound = fs.FileExists(strCopyMDB)
    
    i = 0
    Do While blnFound
        strCopyMDB = CurrentProject.Path & "\c" & i & ".mdb"
        blnFound = fs.FileExists(strCopyMDB)
    Loop
    
    fs.CopyFile CurrentProject.FullName, strCopyMDB
    
    ChangeTables
    AddIndexesFromBU strCopyMDB
    AddRelationsFromBU strCopyMDB
    End Sub  
    
    
    Sub ChangeTables()
    Dim db As Database
    Dim tdf As DAO.TableDef
    Dim rel As DAO.Relation
    Dim fld As DAO.Field
    Dim ndx As DAO.Index
    Dim i
    
        Set db = CurrentDb
        'In order to programmatically change an autonumber, '
        'it is necessary to delete any relationships that '
        'depend on it. '  
        'When deleting from a collection, it is best '
        'to iterate backwards. '
        For i = db.Relations.Count - 1 To 0 Step -1
            db.Relations.Delete db.Relations(i).Name
        Next
    
        'The indexes must also be deleted or the '
        'number cannot be changed. '
        For Each tdf In db.TableDefs
            If Left(tdf.Name, 4) <> "Msys" Then
                For i = tdf.Indexes.Count - 1 To 0 Step -1
                    tdf.Indexes.Delete tdf.Indexes(i).Name
                Next
    
                tdf.Indexes.Refresh
    
                For Each fld In tdf.Fields
                    'If the field is an autonumber, '
                    'use code supplied by MS to change the type '
                    If (fld.Attributes And dbAutoIncrField) Then
    
                        AlterFieldType tdf.Name, fld.Name, "Long"
    
                    End If
                Next
            End If
    
        Next
    End Sub
    
    
    Sub AddIndexesFromBU(MDBBU)
    Dim db As Database
    Dim dbBU As Database
    Dim tdf As DAO.TableDef
    Dim tdfBU As DAO.TableDef
    Dim ndx As DAO.Index
    Dim ndxBU As DAO.Index
    Dim i
    
    Set db = CurrentDb
    'This is the back-up made before starting '
    Set dbBU = OpenDatabase(MDBBU)
    
        For Each tdfBU In dbBU.TableDefs
            'Skip system tables '
            If Left(tdfBU.Name, 4) <> "Msys" Then
                For i = tdfBU.Indexes.Count - 1 To 0 Step -1
                    'Get each index from the back-up '
                    Set ndxBU = tdfBU.Indexes(i)
                    Set tdf = db.TableDefs(tdfBU.Name)
                    Set ndx = tdf.CreateIndex(ndxBU.Name)
                    ndx.Fields = ndxBU.Fields
                    ndx.IgnoreNulls = ndxBU.IgnoreNulls
                    ndx.Primary = ndxBU.Primary
                    ndx.Required = ndxBU.Required
                    ndx.Unique = ndxBU.Unique
    
                    ' and add it to the current db '
                    tdf.Indexes.Append ndx
                Next
    
                tdf.Indexes.Refresh
            End If
        Next
    
    End Sub
    
    Sub AddRelationsFromBU(MDBBU)
    Dim db As Database
    Dim dbBU As Database
    Dim rel As DAO.Relation
    Dim fld As DAO.Field
    Dim relBU As DAO.Relation
    Dim i, j, f
    
    On Error GoTo ErrTrap
    
        Set db = CurrentDb
        'The back-up again '
        Set dbBU = OpenDatabase(MDBBU)
    
        For i = dbBU.Relations.Count - 1 To 0 Step -1
            'Get each relationship from bu '
            Set relBU = dbBU.Relations(i)
            Debug.Print relBU.Name
            Set rel = db.CreateRelation(relBU.Name, relBU.Table, relBU.ForeignTable, relBU.Attributes)
            For j = 0 To relBU.Fields.Count - 1
                f = relBU.Fields(j).Name
                rel.Fields.Append rel.CreateField(f)
                rel.Fields(f).ForeignName = relBU.Fields(j).ForeignName
            Next
            'For some relationships, I am getting error'
            '3284 Index already exists, which I will try'
            'and track down tomorrow, I hope'
            'EDIT: Apparently this is due to Access creating hidden indexes
            'and tracking these down would take quite a bit of effort
            'more information can be found in this link:
            'http://groups.google.ie/group/microsoft.public.access/browse_thread/thread/ca58ce291bdc62df?hl=en&ie=UTF-8&q=create+relation+3284+Index+already+exists
            'It is an occasional problem, so I've added an error trap
    
             'Add the relationship to the current db'
             db.Relations.Append rel
        Next
    ExitHere:
        Exit Sub
    
    ErrTrap:
        If Err.Number = 3284 Then
            Debug.Print relBU.Name, relBU.Table, relBU.ForeignTable, relBU.Attributes
            Resume Next
        Else
            'this is not a user sub, so may as well ... '
            Stop
    
    End If
    End Sub
    
    Sub AlterFieldType(TblName As String, FieldName As String, _
        NewDataType As String)
    'http://support.microsoft.com/kb/128016'
    
        Dim db As Database
        Dim qdf As QueryDef
        Set db = CurrentDb()
    
        ' Create a dummy QueryDef object.'
        Set qdf = db.CreateQueryDef("", "Select * from PROD1")
    
        ' Add a temporary field to the table.'
        qdf.SQL = "ALTER TABLE [" & TblName & "] ADD COLUMN AlterTempField " & NewDataType
        qdf.Execute
    
        ' Copy the data from old field into the new field.'
        qdf.SQL = "UPDATE DISTINCTROW [" & TblName _
            & "] SET AlterTempField = [" & FieldName & "]"
        qdf.Execute
    
        ' Delete the old field.'
        qdf.SQL = "ALTER TABLE [" & TblName & "] DROP COLUMN [" _
           & FieldName & "]"
        qdf.Execute
    
        ' Rename the temporary field to the old field's name.'
        db.TableDefs("[" & TblName & "]").Fields("AlterTempField").Name = FieldName
    
    End Sub
    
        3
  •  1
  •   Community CDub    8 年前

    基于@patrick cuff的答案,我创建了一对脚本:一个导出到XML,另一个读取该XML并将其解析到数据库中。

    用于将关系从MSAccess导出到XML的vbscript

    'supply the Access Application object into this function and path to file to which the output should be written
    Function ExportRelationships(oApplication, sExportpath)
     Dim relDoc, myObj
     Set relDoc = CreateObject("Microsoft.XMLDOM")
     relDoc.appendChild relDoc.createElement("Relations") 'create root xml element
    
     'loop though all the relations
     For Each myObj In oApplication.CurrentDb.Relations
      If Not Left(myObj.Name, 4) = "MSys" Then 'exclude system relations
       Dim relName, relAttrib, relTable, relFoTable, fld
    
       relDoc.childNodes(0).appendChild relDoc.createElement("Relation")
    
       Set relName = relDoc.createElement("Name")
       relName.Text = myObj.Name
       relDoc.childNodes(0).lastChild.appendChild relName
    
       Set relAttrib = relDoc.createElement("Attributes")
       relAttrib.Text = myObj.Attributes
       relDoc.childNodes(0).lastChild.appendChild relAttrib
    
       Set relTable = relDoc.createElement("Table")
       relTable.Text = myObj.Table
       relDoc.childNodes(0).lastChild.appendChild relTable
    
       Set relFoTable = relDoc.createElement("ForeignTable")
       relFoTable.Text = myObj.ForeignTable
       relDoc.childNodes(0).lastChild.appendChild relFoTable
    
       'in case the relationship works with more fields
       For Each fld In myObj.Fields
        Dim lf, ff
        relDoc.childNodes(0).lastChild.appendChild relDoc.createElement("Field")
    
        Set lf = relDoc.createElement("Name")
        lf.Text = fld.Name
        relDoc.childNodes(0).lastChild.lastChild.appendChild lf
    
        Set ff = relDoc.createElement("ForeignName")
        ff.Text = fld.ForeignName
        relDoc.childNodes(0).lastChild.lastChild.appendChild ff
       Next
      End If
     Next
     relDoc.insertBefore relDoc.createProcessingInstruction("xml","version='1.0'"), relDoc.childNodes(0)
     relDoc.Save sExportpath
    End Function
    

    用于从XML将关系导入MSAccess的vbscript

    'supply the Access Application object into this function and path to file from which the input should be read
    Function ImportRelationships(oApplication, sImportpath)
     Dim relDoc, myObj
     Set relDoc = CreateObject("Microsoft.XMLDOM")
     relDoc.Load(sImportpath)
     Dim xmlRel, xmlField, accessRel, relTable, relName, relFTable, relAttr, i
    
     'loop through every Relation node inside .xml file
     For Each xmlRel in relDoc.selectNodes("/Relations/Relation")
      relName = xmlRel.selectSingleNode("Name").Text
      relTable = xmlRel.selectSingleNode("Table").Text
      relFTable = xmlRel.selectSingleNode("ForeignTable").Text
      relAttr = xmlRel.selectSingleNode("Attributes").Text
    
      'remove any possible conflicting relations or indexes
      On Error Resume next
      oApplication.CurrentDb.Relations.Delete (relName)
      oApplication.CurrentDb.TableDefs(relTable).Indexes.Delete(relName)
      oApplication.CurrentDb.TableDefs(relFTable).Indexes.Delete(relName)
      On Error Goto 0
    
      'create the relationship object
      Set accessRel = oApplication.CurrentDb.CreateRelation(relName, relTable, relFTable, relAttr)
    
      'in case the relationship works with more fields
      For Each xmlField In xmlRel.selectNodes("Field")
       accessRel.Fields.Append accessRel.CreateField(xmlField.selectSingleNode("Name").Text)
       accessRel.Fields(xmlField.selectSingleNode("Name").Text).ForeignName = xmlField.selectSingleNode("ForeignName").Text
      Next
    
      'and finally append the newly created relationship to the database
      oApplication.CurrentDb.Relations.Append accessRel
     Next
    End Function
    

    笔记

    只是为了澄清什么是应该传递的 应用程序 参数

    Set oApplication = CreateObject("Access.Application")
    oApplication.NewCurrentDatabase path   'new database
    oApplication.OpenCurrentDatabase path  'existing database
    

    如果您是从vba而不是vbscript运行此命令,则可以删除参数并仅删除 应用 对象代码中的任何位置 应用程序 正在被使用。


    我开始处理这段代码,因为我需要在一个非常复杂的MSAccess项目上实现版本控制。 This post 让我搬家,还有一些关于如何导出/导入MSAccess项目其他部分的好建议。

        4
  •  0
  •   A--C    12 年前

    感谢您提供代码段。 为了消除你的3284错误,我改变了一些事情。 如果您复制示例mdb中的所有索引,然后尝试放置关系,则会引发异常,因为当您放置关系时,它不需要关系的IDEX,而是放置自己的索引。 我遵循的步骤是(假定target.mdb和source.mdb):

    1. 在target.mdb中运行此代码,删除所有索引和关系 FRMO target.mdb 通过调用changetables
    2. 呼叫 AddIndexesFromBU source.mdb和使用条件
      如果 ndxBU.Unique 然后 tdf.Indexes.Append ndx 如果只放置唯一索引,则结束
    3. 呼叫AddRelationsFromBu source.mdb 把所有的关系
    4. 再次调用addindexesfrombu source.mdb并将条件更改为if 不 独特的 然后

    我还添加了与AddRelationsFromBu相同的错误陷阱,并为if ans else继续next。

    这对我有用。

    推荐文章