【问题标题】:Escape double quotes in Access VBA - INSERT INTO ... SELECT在 Access VBA 中转义双引号 - INSERT INTO ... SELECT
【发布时间】:2015-12-24 13:55:33
【问题描述】:

我在下面拥有 VBA 代码,这对 Access 表有很多文本文件。但是对于包含带双引号的文本的 .TXT 文件的情况,会出现问题,因此会使用空值打破该记录的所有其他字段。

我尝试在产品字段的选择中放置替换功能,但对双引号不起作用。与其他字符一起使用,但双引号(否)...

您建议进行哪些调整?任何建议将不胜感激。

*注:实际数据超过100万条记录...

SCHEMA.INI
[Test_temp.csv]
ColNameHeader=false
格式=分隔(;)
Col1="产品" 文本
Col2="price" 双倍

文本文件 CSV:test01.txt
电视 三星 21" 宽屏 LED;170
电视飞利浦 27" 宽屏 LED;200
高清希捷 1TB 7200RPM;150


代码 VBA 访问:

Sub TableImport()

    Dim strSQL As String
    Dim db As DAO.Database

    Dim strFolder As String
    strFolder = CurrentProject.Path

    Set db = CurrentDb

    strSQL = "DELETE FROM tbTest"
    db.Execute strSQL, dbFailOnError

    Dim strFile As String
    strFile = Dir(strFolder & "\test*.txt", vbNormal)

    Do Until strFile = ""

        FileCopy strFolder & "\" & strFile, strFolder & "\Test_temp.csv"

        strSQL = ""

        strSQL = " INSERT INTO tbTEST(product,price)"
        strSQL = strSQL & " SELECT fncReplace(product),price"
        strSQL = strSQL & " FROM [Text;HDR=no;FMT=Delimited;DATABASE=" & strFolder & "].Test_temp.csv"

        db.Execute strSQL, dbFailOnError

        strFile = Dir

    Loop

    db.Close

End Sub


Public Function fncReplace(varStr As Variant) As String
    If IsNull(varStr) Then
        fncReplace = ""
    Else
        fncReplace = Replace(Trim(varStr), """", "''")
    End If
End Function


更新 - 成功了 - 建议:Andre451

Sub TableImport()

    Dim strSQL As String
    Dim db As DAO.Database

    Dim strFolder As String
    strFolder = CurrentProject.Path

    Set db = CurrentDb

    strSQL = "DELETE FROM tbTest"
    db.Execute strSQL, dbFailOnError

    Dim strFile As String
    strFile = Dir(strFolder & "\test*.txt", vbNormal)

    Do Until strFile = ""

        FileCopy strFolder & "\" & strFile, strFolder & "\Test_temp.csv"

        DoCmd.TransferText acLinkDelim, "specIMPORTAR", "linkData", strFolder & "\Test_temp.csv", False

        strSQL = ""
        strSQL = " INSERT INTO tbTEST(product,price)"
        strSQL = strSQL & " SELECT product,price"
        strSQL = strSQL & " FROM linkData"

        db.Execute strSQL, dbFailOnError

        strFile = Dir

        DoCmd.DeleteObject acTable, "linkData"

    Loop

    db.Close

End Sub

【问题讨论】:

    标签: excel csv ms-access vba


    【解决方案1】:

    读取 csv 文件时,双引号被解释为文本分隔符。在 SCHEMA.INI 中似乎没有办法明确告诉 Access“没有文本分隔符!”。

    所以我建议改用导入规范。您可以通过文本导入向导手动导入 csv 文件并保存它来创建导入规范,例如作为“产品进口规范”。详情见this answer中的1.

    在规范中,您将“无”设置为文本分隔符。德语访问:

    然后你链接文本文件并从中导入数据:

    Public Sub ImportProducts()
    
        Dim S As String
    
        ' Link csv file as temp table
        DoCmd.TransferText acLinkDelim, "Product import specification", "linkData", "D:\temp\Test01.csv", False
    
        ' Insert from temp table into product table
        S = "INSERT INTO tbProduct (product, price) SELECT product, price FROM linkData"
        CurrentDb.Execute S
    
        ' Remove temp table
        DoCmd.DeleteObject acTable, "linkData"
    
    End Sub
    

    编辑:

    我创建了一个 1.000.000 行 (36 MB) 的 csv 文件并将其用作导入文件:

    Const cFile = "G:\test.csv"
    
    Public Sub CreateCSV()
    
        Dim S As String
        Dim i As Long
    
        Open cFile For Output As #1
        For i = 1 To 1000000
            Print #1, "Testing string number " & CStr(i) & ";" & CStr(i)
        Next i
        Close #1
    
    End Sub
    
    Public Sub ImportProducts()
    
        Dim S As String
        Dim snTime As Single
    
        snTime = Timer
    
        ' Clean up product table
        CurrentDb.Execute "DELETE * FROM tbProduct"
        Debug.Print "DELETE: " & Timer - snTime
    
        ' Link csv file as temp table
        DoCmd.TransferText acLinkDelim, "Product import specification", "linkData", cFile, False
        Debug.Print "TransferText: " & Timer - snTime
    
        ' Insert from temp table into product table
        S = "INSERT INTO tbProduct (product, price) SELECT product, price FROM linkData"
        CurrentDb.Execute S
        Debug.Print "INSERT: " & Timer - snTime
    
        ' Remove temp table
        DoCmd.DeleteObject acTable, "linkData"
    
    End Sub
    

    结果:

    DELETE: 0
    TransferText: 0,6640625
    INSERT: 4,679688
    

    将自动编号字段作为主键添加到 tbProduct 后:

    TransferText: 0,6640625
    INSERT: 8,023438
    

    8 秒并不是真的那么慢。
    确保 Access 数据库和导入的 CSV 文件都在本地磁盘上,而不是在网络驱动器上。如果可能,在 SSD 上。

    【讨论】:

    • 成功了。但是代码太慢了。想象一下,当使用 100 万条记录时。看到你建议的上面的代码。是离开它更快吗?为什么代码很慢?
    • @RalphMacLand:见编辑。有 100 万条记录,对我来说,仅使用两个字段运行 4.5 秒,使用额外的自动编号主键字段运行 8 秒。
    • @RalphMacLand: P.S.您可以省略 FileCopy 并直接链接 test*.txt 文件。导入规范相对于 schema.ini 的另一个优势。 --- 除非文件在网络驱动器上,否则最好先将它们复制到本地磁盘。
    • @Andrea451:我同意你的看法! 8 秒并不是真的那么慢。我将检查我的机器配置(内存、CPU、进程、高清、虚拟内存等)。谢谢!
    【解决方案2】:

    既然您要将文件从 test01.txt 复制到 temp_test.csv,为什么不趁机破解它并用一个 Unicode“智能引号”字符(例如 )替换不需要的引号,而不是会破坏 CSV 读取?

    Sub TableImport()
    
        Dim strSQL As String, f As Long, strm As String, ln as long
        Dim db As DAO.Database, rs As DAO.Recordset
    
        Dim strFolder As String
        strFolder = Environ("TEMP") 'CurrentProject.Path
    
        Set db = CurrentDb
    
        strSQL = "DELETE FROM tbTest"
        db.Execute strSQL, dbFailOnError
    
        Dim strFile As String
        strFile = Dir(strFolder & "\test*.txt", vbNormal)
    
        Do Until strFile = ""
    
            strm = vbNullString
            f = FreeFile
            Open strFolder & "\" & strFile For Binary Access Read As #f
            strm = Input$(LOF(f), f)
            Close #f
            strm = Replace(strm, Chr(34), ChrW(8221))   '<~~ replace double-quote character with Unicode right smart quote character
            'optionally strip off the first 5 lines
            for ln = 1 to 5
                strm = mid$(strm, instr(1, strm, chr(10)) + 1)
            next ln
            Kill strFolder & "\Test_temp.csv"
            f = FreeFile
            Open strFolder & "\Test_temp.csv" For Binary Access Write As #f
            Put #f, , strm
            Close #f
    
            strSQL = vbNullString
            strSQL = "INSERT INTO tbTEST(product,price)"
            strSQL = strSQL & " SELECT F1, F2"
            strSQL = strSQL & " FROM [Text;HDR=no;FMT=Delimited(;);DATABASE=" & strFolder & "].[Test_temp.csv]"
    
            db.Execute strSQL, dbFailOnError + dbSeeChanges
    
            strFile = Dir
    
        Loop
    
        db.Close
    
    End Sub
    

            

    【讨论】:

    • 有趣的代码sn-p!我有个疑问。使用您的代码 sn -p 并且还可以在将剩余数据导入表之前排除 CSV 文件的前 5 行?
    • 如果是 SQL,我会对源表进行分区,但由于您正在读取输入并使用不同的名称将其重写,因此最简单的方法可能是跳过前 5 行读或写。
    【解决方案3】:

    您只需将双引号括在单引号中:

    Public Function fncReplace(varStr As Variant) As String
        fncReplace = Replace(Trim(Nz(varStr)), Chr(39), Chr(34) & Chr(39))
    End Function
    

    也就是说,我会发现先将文件链接为表格,然后使用链接的表格作为源更容易。

    【讨论】:

    • 使用 Andrea451 代码不需要替换功能。该代码无需使用替换(双引号)即可工作。谢谢!
    猜你喜欢
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 2012-01-23
    • 2015-04-07
    • 2019-05-19
    • 1970-01-01
    • 2023-02-09
    • 2013-06-17
    相关资源
    最近更新 更多