VBA-SQL UPDATE / INSERT / SELECT进出Excel工作表

时间:2022-06-30 09:28:09

In a nutshell: I'm making a scheduler for my client and, due to constraints, it needs to be in a single excel file (as small as possible). So one worksheet works as the UI and any others will be tables or settings.

简而言之:我正在为我的客户端制作一个调度程序,由于受到限制,它需要在一个excel文件中(尽可能小)。因此,一个工作表作为UI工作,其他任何工作表将是表或设置。

I'm trying to use SQL (to which I'm new) to work with the schedule data on a single worksheet (named "TblEmpDays"). So I need to add/update and retrieve records to/from this worksheet. I was able to get a SELECT query to work with some arbitrary data (and paste to a Range). However, I'm not able to get INSERT or UPDATE to work. I've seen it structured as INSERT INTO [<table name>$] (<field names>) VALUES (<data>);. However this gives me a run-time error "'-2147217900 (80040e14)' Syntax error in INSERT INTO statement."

我正在尝试使用SQL(我是新手)来处理单个工作表(名为“TblEmpDays”)上的计划数据。所以我需要在此工作表中添加/更新和检索记录。我能够获得SELECT查询以处理一些任意数据(并粘贴到Range)。但是,我无法使INSERT或UPDATE工作。我已经看到它的结构为INSERT INTO [

)VALUES( );.但是这在INSERT INTO语句中给出了运行时错误“'-2147217900(80040e14)'语法错误。”

$](

I'm using VBA to write all of this and I made an SQL helper class to make the query execution easier.

我正在使用VBA编写所有这些,并且我创建了一个SQL帮助程序类,以使查询执行更容易。

To clarify, my question is: How do I need to construct the INSERT and UPDATE queries? What am I missing? I'm trying to post as much related info as possible, so let me know if I missed anything.

为了澄清,我的问题是:我如何构建INSERT和UPDATE查询?我错过了什么?我试图发布尽可能多的相关信息,所以如果我错过任何内容,请告诉我。

Class SQL:

类SQL:

Private pCn ' As Database
Private pResult 'As Recordset
Private pSqlStr As String

Public Property Get Result()
    Result = pResult
End Property

Public Function Init()
    Set pCn = CreateObject("ADODB.Connection")

    With pCn
        .ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
                            "Data Source=" & ThisWorkbook.FullName & ";" & _
                            "Extended Properties=""Excel 12.0 Macro;HDR=YES;ReadOnly=False"";"
        .Open
    End With

End Function

Public Function Cleanup()
    If Not (pCn Is Nothing) Then
        pCn.Close
        Set pCn = Nothing
    End If

    If Not pResult Is Nothing Then
        Set pResult = Nothing
    End If

End Function

Public Function CopyResultToRange(rg As Range)
    If Not rg Is Nothing And Not pResult Is Nothing Then
        rg.CopyFromRecordset pResult
    End If
End Function

Public Property Get query() As String
    query = pSqlStr
End Property
Public Property Let query(value As String)
    pSqlStr = value
End Property

Public Function Execute(Optional sqlQuery As String)
    If sqlQuery = "" Then
        sqlQuery = query
    End If

    If Not pCn Is Nothing Then
        Set pResult = pCn.Execute(sqlQuery, , CommandTypeEnum.adCmdText Or ExecuteOptionEnum.adExecuteNoRecords)
    Else
        MsgBox "SQL connection not established"
    End If

End Function

Executing function:

执行功能:

Dim s As SQL ' this is the SQL class '
Dim tbl As String
' rcDay=date string, rcIn & rcOut = time strings, rcVac=boolean string, rcSls=number string'
Dim rcName As String, rcDay As String, rcIn As String, rcOut As String, rcVac As String, rcSls As String
Dim qry As String

tbl = "[TblEmpDays$]"
qry = "INSERT INTO <tbl> (name, date, in, out, vac, sales)" & vbNewLine & _
        "VALUES ('<name>', '<date>', '<in>', '<out>', '<vac>', <sales>);"

' Set rc* vars '

s.Init
s.query = Replace(Replace(Replace(Replace(Replace(Replace(Replace(qry, _
                                    "<tbl>", tbl), _
                                    "<sales>", rcSls), _
                                    "<vac>", rcVac), _
                                    "<out>", rcOut), _
                                    "<in>", rcIn), _
                                    "<date>", rcDay), _
                                    "<name>", rcName)
MsgBox s.query
s.Execute
s.Cleanup

I've looked all over an can't find a solution. I'm sure I just haven't searched the right phrase or something simple.

我看了一遍都找不到解决办法。我确定我没有搜索过正确的短语或简单的东西。

2 个解决方案

#1


2  

I'm posting the solution here since I can't mark his comment as the answer.

我在这里发布解决方案,因为我无法将他的评论标记为答案。


Thanks to @Jeeped in the comments, I now feel like an idiot. It turns out three of my field names were using reserved words ("name", "date", and "in"). It always seems to be a subtle detail that does me in...

感谢@Jeeped在评论中,我现在觉得自己像个白痴。事实证明我的三个字段名称都使用了保留字(“name”,“date”和“in”)。它总是似乎是一个微妙的细节,让我...

I renamed these fields in my worksheet (table) and altered the appropriate code. I also had to Cast the input strings into the proper data types. I'm still working the rest of the details out, but here's the new query:

我在工作表(表)中重命名了这些字段并更改了相应的代码。我还必须将输入字符串转换为正确的数据类型。我还在处理剩下的细节,但这是新查询:

qry = "INSERT INTO <tbl> (empName, empDay, inTime, outTime, vac, sales)" & vbNewLine & _
                  "VALUES (CStr('<name>'), CDate('<date>'), CDate('<in>'), CDate('<out>'), " & _
                      "CBool('<vac>'), CDbl(<sales>));"

I needed the CDate() (instead of the #*#) so I could pass in a string. So CDate('<date>') instead of #<date>#

我需要CDate()(而不是#*#)所以我可以传入一个字符串。所以CDate(' ')而不是#

#2


0  

Consider using a relational database as backend instead of a worksheet for your project. You can continue to use the UI spreadsheet as a frontend. As a Windows product, the Jet/ACE SQL Engine can be a working solution plus it allows multiple user with simultaneous access (with record-level locking). Additionally, Jet/ACE comes equipped with its own SQL dialect for Database Definition Language (DDL) and Database Maniupulation Language (DML) procedures. And Excel can connect to Jet/ACE via ADO/DAO objects. The only difference of Jet/ACE compared to other RDMS is that it is a file level database (not server) and you cannot create a database using SQL. You must first create the database file using VBA or other COM defined language.

考虑使用关系数据库作为后端而不是项目的工作表。您可以继续使用UI电子表格作为前端。作为Windows产品,Jet / ACE SQL引擎可以是一个可行的解决方案,它允许多个用户同时访问(具有记录级锁定)。此外,Jet / ACE还配备了自己的SQL方言,用于数据库定义语言(DDL)和数据库管理语言(DML)程序。 Excel可以通过ADO / DAO对象连接到Jet / ACE。与其他RDMS相比,Jet / ACE的唯一区别在于它是文件级数据库(而不是服务器),您无法使用SQL创建数据库。您必须首先使用VBA或其他COM定义的语言创建数据库文件。

Below are working examples of VBA scripts (Clients and Orders tables) in creating a database with DAO, creating tables with ADO, executing action queries, and copying a recordset to worksheet. Integrate these macros into your project. Use error handling and debug.Print to help develop your app. If you do not have MS Access installed, the .accdb file will show in directory but with blank icon. There will be no user interface to manage the file except via code.

下面是使用DAO创建数据库,使用ADO创建表,执行操作查询以及将记录集复制到工作表时VBA脚本(客户端和订单表)的工作示例。将这些宏集成到您的项目中。使用错误处理和debug.Print来帮助开发您的应用程序。如果未安装MS Access,则.accdb文件将显示在目录中,但带有空白图标。除了通过代码之外,没有用户界面来管理文件。

Sub CreateDatabase()
On Error GoTo ErrHandle
    Dim fso As Object
    Dim olDb As Object, db As Object
    Dim strpath As String
    Const dbLangGeneral = ";LANGID=0x0409;CP=1252;COUNTRY=0"

    strpath = "C:\Path\To\Database\File.accdb"

    ' CREATE DATABASE '
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set olDb = CreateObject("DAO.DBEngine.120")

    If Not fso.FileExists(strpath) Then
        Set db = olDb.CreateDatabase(strpath, dbLangGeneral)
    End If

    Set db = Nothing
    Set olDb = Nothing
    Set fso = Nothing

    MsgBox "Successfully created database!", vbInformation
    Exit Sub

ErrHandle:
    MsgBox Err.Number & " - " & Err.Description, vbCritical
    Exit Sub
End Sub

Sub CreateTables()
On Error GoTo ErrHandle
    Dim strpath As String, constr As String
    Dim objAccess As Object
    Dim conn As Object

    strpath = "C:\Path\To\Database\File.accdb"

    ' CONNECT TO DATABASE '
    constr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & strpath & ";"
    Set conn = CreateObject("ADODB.Connection")
    conn.Open constr

    ' CREATE TABLES (RUN ONLY ONCE) '
    conn.Execute "CREATE TABLE Clients (" _
                    & " ClientID AUTOINCREMENT," _
                    & " ClientName TEXT(255)," _
                    & " Address TEXT(255)," _
                    & " Notes TEXT(255)," _
                    & " DateCreated DATETIME" _
                    & ");"

    conn.Execute "CREATE TABLE Orders (" _
                    & " OrderID AUTOINCREMENT," _
                    & " ClientID INTEGER," _
                    & " Item TEXT(255)," _
                    & " Price DOUBLE," _
                    & " OrderDate DATETIME," _
                    & " Notes TEXT(255)" _
                    & ");"

    ' CLOSE CONNECTION '
    conn.Close
    Set conn = Nothing

    MsgBox "Successfully created Clients and Orders tables!", vbInformation
    Exit Sub

ErrHandle:
    MsgBox Err.Number & " - " & Err.Description, vbCritical
    Exit Sub

End Sub

Sub RetrieveDataToWorksheet()
On Error GoTo ErrHandle
    Dim strpath As String, constr As String
    Dim conn As Object, rs As Object
    Dim fld As Variant

    strpath = "C:\Path\To\Database\File.accdb"

    ' OPEN CONNECTION '
    constr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & strpath & ";"
    Set conn = CreateObject("ADODB.Connection")
    Set rs = CreateObject("ADODB.Recordset")

    conn.Open constr
    rs.Open "SELECT * FROM Clients" _
             & " INNER JOIN Orders ON Clients.ClientID = Orders.ClientID;", conn

    ' COPY FROM RECORDSET TO WORKSHEET '
    Worksheets(1).Activate
    Worksheets(1).Range("A4").Select

    ' COLUMN NAMES '
    For Each fld In rs.Fields
        ActiveCell = fld.Name
        ActiveCell.Offset(0, 1).Select
    Next

    ' ROW VALUES '
    Worksheets(1).Range("A5").CopyFromRecordset rs

    ' CLOSE RECORDSET AND CONNECTION '
    rs.Close
    conn.Close

    Set conn = Nothing
    Set rs = Nothing
    Exit Sub

ErrHandle:
    MsgBox Err.Number & " - " & Err.Description, vbCritical
    Exit Sub
End Sub

Sub ActionQueries()
On Error GoTo ErrHandle
    Dim strpath As String, constr As String
    Dim conn As Object

    strpath = "C:\Path\To\Database\File.accdb"

    ' OPEN CONNECTION '
    constr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & strpath & ";"
    Set conn = CreateObject("ADODB.Connection")
    conn.Open constr

    ' APPEND QUERY '
    conn.Execute "INSERT INTO Clients (ClientID, ClientName)" _
                    & " VALUES (" & Worksheets(1).Range("A2") & ", '" & Worksheets(1).Range("B2") & "');"

    conn.Execute "INSERT INTO Orders (ClientID, Item, Price)" _
                    & " VALUES (" & Worksheets(1).Range("A2") & ", " _
                    & "'" & Worksheets(1).Range("C2") & "', " _
                    & Worksheets(1).Range("D2") & ");"

    ' UPDATE QUERY '
    conn.Execute "UPDATE Clients " _
                    & " SET Address = '" & Worksheets(1).Range("E2") & "'" _
                    & " WHERE ClientID = " & Worksheets(1).Range("A2") & ";"

    ' DELETE QUERY '
    conn.Execute "DELETE FROM Orders " _
                    & " WHERE ClientID = " & Worksheets(1).Range("A2") & ";"

    ' CLOSE CONNECTION '
    conn.Close
    Set conn = Nothing

    MsgBox "Successfully updated database!", vbInformation
    Exit Sub

ErrHandle:
    MsgBox Err.Number & " - " & Err.Description, vbCritical
    Exit Sub
End Sub

#1


2  

I'm posting the solution here since I can't mark his comment as the answer.

我在这里发布解决方案,因为我无法将他的评论标记为答案。


Thanks to @Jeeped in the comments, I now feel like an idiot. It turns out three of my field names were using reserved words ("name", "date", and "in"). It always seems to be a subtle detail that does me in...

感谢@Jeeped在评论中,我现在觉得自己像个白痴。事实证明我的三个字段名称都使用了保留字(“name”,“date”和“in”)。它总是似乎是一个微妙的细节,让我...

I renamed these fields in my worksheet (table) and altered the appropriate code. I also had to Cast the input strings into the proper data types. I'm still working the rest of the details out, but here's the new query:

我在工作表(表)中重命名了这些字段并更改了相应的代码。我还必须将输入字符串转换为正确的数据类型。我还在处理剩下的细节,但这是新查询:

qry = "INSERT INTO <tbl> (empName, empDay, inTime, outTime, vac, sales)" & vbNewLine & _
                  "VALUES (CStr('<name>'), CDate('<date>'), CDate('<in>'), CDate('<out>'), " & _
                      "CBool('<vac>'), CDbl(<sales>));"

I needed the CDate() (instead of the #*#) so I could pass in a string. So CDate('<date>') instead of #<date>#

我需要CDate()(而不是#*#)所以我可以传入一个字符串。所以CDate(' ')而不是#

#2


0  

Consider using a relational database as backend instead of a worksheet for your project. You can continue to use the UI spreadsheet as a frontend. As a Windows product, the Jet/ACE SQL Engine can be a working solution plus it allows multiple user with simultaneous access (with record-level locking). Additionally, Jet/ACE comes equipped with its own SQL dialect for Database Definition Language (DDL) and Database Maniupulation Language (DML) procedures. And Excel can connect to Jet/ACE via ADO/DAO objects. The only difference of Jet/ACE compared to other RDMS is that it is a file level database (not server) and you cannot create a database using SQL. You must first create the database file using VBA or other COM defined language.

考虑使用关系数据库作为后端而不是项目的工作表。您可以继续使用UI电子表格作为前端。作为Windows产品,Jet / ACE SQL引擎可以是一个可行的解决方案,它允许多个用户同时访问(具有记录级锁定)。此外,Jet / ACE还配备了自己的SQL方言,用于数据库定义语言(DDL)和数据库管理语言(DML)程序。 Excel可以通过ADO / DAO对象连接到Jet / ACE。与其他RDMS相比,Jet / ACE的唯一区别在于它是文件级数据库(而不是服务器),您无法使用SQL创建数据库。您必须首先使用VBA或其他COM定义的语言创建数据库文件。

Below are working examples of VBA scripts (Clients and Orders tables) in creating a database with DAO, creating tables with ADO, executing action queries, and copying a recordset to worksheet. Integrate these macros into your project. Use error handling and debug.Print to help develop your app. If you do not have MS Access installed, the .accdb file will show in directory but with blank icon. There will be no user interface to manage the file except via code.

下面是使用DAO创建数据库,使用ADO创建表,执行操作查询以及将记录集复制到工作表时VBA脚本(客户端和订单表)的工作示例。将这些宏集成到您的项目中。使用错误处理和debug.Print来帮助开发您的应用程序。如果未安装MS Access,则.accdb文件将显示在目录中,但带有空白图标。除了通过代码之外,没有用户界面来管理文件。

Sub CreateDatabase()
On Error GoTo ErrHandle
    Dim fso As Object
    Dim olDb As Object, db As Object
    Dim strpath As String
    Const dbLangGeneral = ";LANGID=0x0409;CP=1252;COUNTRY=0"

    strpath = "C:\Path\To\Database\File.accdb"

    ' CREATE DATABASE '
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set olDb = CreateObject("DAO.DBEngine.120")

    If Not fso.FileExists(strpath) Then
        Set db = olDb.CreateDatabase(strpath, dbLangGeneral)
    End If

    Set db = Nothing
    Set olDb = Nothing
    Set fso = Nothing

    MsgBox "Successfully created database!", vbInformation
    Exit Sub

ErrHandle:
    MsgBox Err.Number & " - " & Err.Description, vbCritical
    Exit Sub
End Sub

Sub CreateTables()
On Error GoTo ErrHandle
    Dim strpath As String, constr As String
    Dim objAccess As Object
    Dim conn As Object

    strpath = "C:\Path\To\Database\File.accdb"

    ' CONNECT TO DATABASE '
    constr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & strpath & ";"
    Set conn = CreateObject("ADODB.Connection")
    conn.Open constr

    ' CREATE TABLES (RUN ONLY ONCE) '
    conn.Execute "CREATE TABLE Clients (" _
                    & " ClientID AUTOINCREMENT," _
                    & " ClientName TEXT(255)," _
                    & " Address TEXT(255)," _
                    & " Notes TEXT(255)," _
                    & " DateCreated DATETIME" _
                    & ");"

    conn.Execute "CREATE TABLE Orders (" _
                    & " OrderID AUTOINCREMENT," _
                    & " ClientID INTEGER," _
                    & " Item TEXT(255)," _
                    & " Price DOUBLE," _
                    & " OrderDate DATETIME," _
                    & " Notes TEXT(255)" _
                    & ");"

    ' CLOSE CONNECTION '
    conn.Close
    Set conn = Nothing

    MsgBox "Successfully created Clients and Orders tables!", vbInformation
    Exit Sub

ErrHandle:
    MsgBox Err.Number & " - " & Err.Description, vbCritical
    Exit Sub

End Sub

Sub RetrieveDataToWorksheet()
On Error GoTo ErrHandle
    Dim strpath As String, constr As String
    Dim conn As Object, rs As Object
    Dim fld As Variant

    strpath = "C:\Path\To\Database\File.accdb"

    ' OPEN CONNECTION '
    constr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & strpath & ";"
    Set conn = CreateObject("ADODB.Connection")
    Set rs = CreateObject("ADODB.Recordset")

    conn.Open constr
    rs.Open "SELECT * FROM Clients" _
             & " INNER JOIN Orders ON Clients.ClientID = Orders.ClientID;", conn

    ' COPY FROM RECORDSET TO WORKSHEET '
    Worksheets(1).Activate
    Worksheets(1).Range("A4").Select

    ' COLUMN NAMES '
    For Each fld In rs.Fields
        ActiveCell = fld.Name
        ActiveCell.Offset(0, 1).Select
    Next

    ' ROW VALUES '
    Worksheets(1).Range("A5").CopyFromRecordset rs

    ' CLOSE RECORDSET AND CONNECTION '
    rs.Close
    conn.Close

    Set conn = Nothing
    Set rs = Nothing
    Exit Sub

ErrHandle:
    MsgBox Err.Number & " - " & Err.Description, vbCritical
    Exit Sub
End Sub

Sub ActionQueries()
On Error GoTo ErrHandle
    Dim strpath As String, constr As String
    Dim conn As Object

    strpath = "C:\Path\To\Database\File.accdb"

    ' OPEN CONNECTION '
    constr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & strpath & ";"
    Set conn = CreateObject("ADODB.Connection")
    conn.Open constr

    ' APPEND QUERY '
    conn.Execute "INSERT INTO Clients (ClientID, ClientName)" _
                    & " VALUES (" & Worksheets(1).Range("A2") & ", '" & Worksheets(1).Range("B2") & "');"

    conn.Execute "INSERT INTO Orders (ClientID, Item, Price)" _
                    & " VALUES (" & Worksheets(1).Range("A2") & ", " _
                    & "'" & Worksheets(1).Range("C2") & "', " _
                    & Worksheets(1).Range("D2") & ");"

    ' UPDATE QUERY '
    conn.Execute "UPDATE Clients " _
                    & " SET Address = '" & Worksheets(1).Range("E2") & "'" _
                    & " WHERE ClientID = " & Worksheets(1).Range("A2") & ";"

    ' DELETE QUERY '
    conn.Execute "DELETE FROM Orders " _
                    & " WHERE ClientID = " & Worksheets(1).Range("A2") & ";"

    ' CLOSE CONNECTION '
    conn.Close
    Set conn = Nothing

    MsgBox "Successfully updated database!", vbInformation
    Exit Sub

ErrHandle:
    MsgBox Err.Number & " - " & Err.Description, vbCritical
    Exit Sub
End Sub