【问题标题】:Updating an Access Table with a CSV File Automatically使用 CSV 文件自动更新访问表
【发布时间】:2018-08-09 00:06:27
【问题描述】:

问题背景:

我有一个 Powershell 脚本,我可以从我的 Microsoft Access 表单中执行它扫描包含不同设施信息的文件夹,并生成一个类似于以下内容的 CSV:

SiteCode    FacilityNumber  DocumentType    HyperlinkPath
DKFZ        10              DD1400          C:\FACILITIES DATABASE\path
DKFZ        10              FLRPLN          C:\FACILITIES DATABASE\path
SMQL        17              P1              C:\FACILITIES DATABASE\path
SMQL        17              P2              C:\FACILITIES DATABASE\path

因此,每次将新文件添加到这些文件夹时,我都可以运行此脚本并生成我拥有的所有内容的更新列表:

C:\...\Output\scanResults.csv

我现在需要的是获取该 CSV 文件并更新(甚至覆盖)我在 Access 数据库中的一个表,该表与其他表有关系,并被数据库中的各种查询和表单使用。 CSV 列的命名和格式已与访问表相同。

我查看并尝试复制以下线程:

VBA procedure to import csv file into access

Access Data Project Importing CSV File In VBA

VBA Import CSV file

我找到的最接近的答案是:

Sub Import()
   Dim conn as new ADODB.Connection
   Dim rs as new ADODB.Recordset
   Dim f as ADODB.field

   conn.Open "DRIVER={Microsoft Text Driver (*.txt; *.csv)};DBQ=c:\temp;"
   rs.Open "SELECT * FROM [test.txt]", conn, adOpenStatic, adLockReadOnly, adCmdText

   While Not rs.EOF
      For Each f In rs.Fields
         Debug.Print f.name & "=" & f.Value
      Next
   Wend
End Sub

但这显然不会将数据写入表中,我无法理解作者关于将 Select 更改为 Insert 的意思。

我还发现:

DoCmd.TransferText acImportDelim, "YourCustomSpecificationName", _
    "tblImport", "C:\SomeFolder\DataFile.csv", False

由于这两个都是从 2010 年开始的,我想知道在 Access 2013 中是否没有更好的方法来实现这一点。虽然我可以手动完成这一切,但我想将它合并到我用来告诉 Powershell 生成 CSV,这样我就可以制作它然后立即上传。

非常感谢任何帮助或建议。总的来说,我对 Access、VBA 和 SQL 语句仍然很熟悉,所以这是一个“边走边学”的过程。

【问题讨论】:

  • 作者建议类似:INSERT INTO tablename([field1], [field2], [field3]) SELECT [field1], [field2], [field3] FROM [test.txt]。但是,使用此语句设置记录集对象和代码循环记录集对我来说毫无意义。您是否考虑过仅设置指向 CSV 文件的链接?只要更换同名和结构的文件,每次链接应该是稳定的。然后像使用任何其他表一样使用链接表,但不能编辑数据。
  • 啊,好吧,现在更有意义了。所以这实际上是我尝试做的第一件事,为我的 CSV 创建一个可以在 Access 之外更新的链接表。不幸的是,我遇到了两个问题;在积极使用表单时,我的查询需要更长的时间才能运行,当表单第一次打开时,我收到几个“在此表单上指定的记录源......不存在”错误,这些错误似乎可以通过关闭表单然后解决重新打开它。

标签: csv ms-access vba ms-access-2013


【解决方案1】:

我更喜欢使用 SQL 子句和查询来导入此类数据。详细信息取决于您的确切配置,但通常看起来像这样:

SELECT *
INTO MyTable
FROM [Text;FMT=CSVDelimited;HDR=No;DATABASE=C:\...\Output].[scanResults#csv]

或者将信息附加到表格中:

INSERT INTO MyTable
(SiteCode,    FacilityNumber,  DocumentType,    HyperlinkPath)
SELECT *
FROM [Text;FMT=CSVDelimited;HDR=No;DATABASE=C:\...\Output].[scanResults#csv]

这允许您在导入之前进行检查(使用WHERE 子句),仅导入特定值,并允许您在不使用外部文件的情况下进行大量自定义。

DATABASE= 后跟您的文件夹名称(如果其中有需要转义的字符,请使用 {}),然后是您的文件名,将 . 替换为 #

您可以通过将其保存为查询或将其用作 VBA 或宏中的字符串来执行它。请注意,我很少推荐宏,但您可以使用计划任务执行它们并在导入后关闭 Access。

要在更新前后备份和恢复关系,可以使用以下功能:

Public Function DeleteRelationsGiveBackup(strTablename As String) As Collection
    Dim ReturnCollection As Collection
    Set ReturnCollection = New Collection
    Dim i As Integer
    Dim o As Integer
    Do While i <= (CurrentDb.Relations.Count - 1)
        Select Case strTablename 
            Case Is = CurrentDb.Relations(i).Table
                ReturnCollection.Add DuplicateRelation(CurrentDb.Relations(i))
                o = o + 1
                CurrentDb.Relations.Delete CurrentDb.Relations(i).NAME
            Case Is = CurrentDb.Relations(i).ForeignTable
                ReturnCollection.Add DuplicateRelation(CurrentDb.Relations(i))
                o = o + 1
                CurrentDb.Relations.Delete CurrentDb.Relations(i).NAME
            Case Else
                i = i + 1
        End Select
    Loop
    Set DeleteRelationsGiveBackup = ReturnCollection
End Function

Public Sub RestoreRelationBackup(collRelationBackup As Collection)
    Dim relBackup As Variant
    If collRelationBackup.Count = 0 Then Exit Sub
    For Each relBackup In collRelationBackup
        CurrentDb.Relations.Append relBackup
    Next relBackup
End Sub

Public Function DuplicateRelation(SourceRelation As Relation) As Relation
    Set DuplicateRelation = CurrentDb.CreateRelation(SourceRelation.NAME, SourceRelation.Table, SourceRelation.ForeignTable)
    DuplicateRelation.Attributes = SourceRelation.Attributes
    Dim i As Integer
    Dim fldLoop As Field
    Do While i < SourceRelation.Fields.Count
        Set fldLoop = DuplicateRelation.CreateField(SourceRelation.Fields(i).NAME)
        fldLoop.ForeignName = SourceRelation.Fields(i).ForeignName
        DuplicateRelation.Fields.Append fldLoop
        i = i + 1
    Loop
End Function

然后,在导入时:

Dim colRelBackup As Collection
Set colRelBackup = DeleteRelationsGiveBackup("MyTable")
'Delete MyTable
'Import new version
RestoreRelationBackup colRelBackup 

(请注意,代码很长,是几年前为一个项目开发的,并没有经过广泛的测试。如果字段名称/类型与导入前的情况不完全,则备份的恢复可能会失败,并且关系将永久丢失)。

【讨论】:

  • 谢谢,我刚刚在 Query 中对其进行了测试,但是我收到以下错误:“您无法删除表 'TblFacilityHyperlinks';它正在参与一个或多个关系”,是否存在一种强制 INTO 的方法,还是应该运行此查询以从 CSV 收集数据并使用它来更新或附加原始表?
  • 这根本不是描述性的。删除表的错误可能取决于表是否链接、数据库是否共享、安全设置、关系、依赖关系等。除非您提供更多信息,否则您需要自己弄清楚。
  • 我很抱歉,但错误消息中唯一的附加行有效地重复了第一行:“如果要删除此表,请先在关系窗口中删除其关系”它不会让我将数据从原始表中插入而不先删除它,它不会让我在不删除它所属的关系的情况下删除它。
  • 查看当前编辑。您可以在插入新信息之前清空表格。
  • 我只需要更改 HDR=Yes,所有内容都可以快速轻松地导入!虽然我仍然需要解决替换表时删除关系的问题。我想保留所有的表关系,这样我就不必在放入新信息后重新创建它们。
【解决方案2】:

因此,一些高级架构师的建议:替换数据与替换表

替换数据更容易——新传入的数据必须与现有表具有完全相同的结构(即相同的字段名称且没有新字段)。

    • 只需对现有表执行删除查询即可清除所有记录
    • 然后向链接的 CSV 文件触发追加查询,将所有这些记录写入现有表中

真的很简单。

如果需要,您可以替换这些表格 - 而且您已经走上了这条路。您可以完全删除这些表关系。该表关系功能很有用 - 但不是强制性的。作为替代,您可以在查询级别创建关系。本质上,表关系只是自动创建查询级别的关系。如果您删除表关系,则必须手动在查询级别创建表关系 - 它们不会自动出现。但是请注意,如果依赖于级联删除或引用完整性,那么删除表关系将撤消该操作 - 因此您应该检查这些点。

删除表关系不会破坏任何现有查询。它们的表关系连接线将保持不变。

【讨论】:

  • 这非常有帮助。我希望我从一开始就知道这一点,对于应用程序,我有查询级别的关系可能会工作得很好。我没有真正明确地强制引用完整性,因为主键和外键 ID 基于实际的设施代码和数字组合,无论如何都不会重复或修改。
猜你喜欢
  • 2022-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-16
  • 1970-01-01
相关资源
最近更新 更多