如何使用Excel VBA获取新插入记录的ID?

时间:2009-02-27 15:07:59

标签: sql excel ms-access vba jet

这似乎是一个常见的问题,但是大多数解决方案都是指连接多个SQL命令,我认为这些命令无法用ADO / VBA完成(但我很高兴在这方面显示错误)。< / p>

我当前插入了我的新记录,然后使用(我希望)足够的字段运行一个选择查询,以保证只返回新插入的记录。我的数据库很少被一个人一次访问(在查询之间发生另一次插入的风险可以忽略不计),并且由于表的结构,识别新记录通常很容易。

我现在正在尝试更新一个没有太多唯一性范围的表,而不是人工主键。这意味着新记录可能不是唯一的风险,我不愿意添加字段以强制唯一性。

在这种情况下,将记录插入Access表然后从Excel查询新主键的最佳方法是什么?

感谢您的回复。我试图让@@IDENTITY正常工作,但这总是使用下面的代码返回0。

Private Sub getIdentityTest()
    Dim myRecordset As New ADODB.Recordset
    Dim SQL As String, SQL2 As String

    SQL = "INSERT INTO tblTasks (discipline,task,owner,unit,minutes) VALUES (""testDisc3-3"",""testTask"",""testOwner"",""testUnit"",1);"
    SQL2 = "SELECT @@identity AS NewID FROM tblTasks;"

    If databaseConnection Is Nothing Then
        createDBConnection
    End If

    With databaseConnection
        .Open dbConnectionString
        .Execute (SQL)
        .Close
    End With

    myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly

    Debug.Print myRecordset.Fields("NewID")

    myRecordset.Close

    Set myRecordset = Nothing
End Sub

有什么突出的责任吗?

然而,考虑到Renaud帮助提供的警告(下文),使用@@IDENTITY与使用任何其他方法的风险几乎相同,所以我现在暂时使用SELECT MAX。为了将来参考,虽然我有兴趣看看我上面的尝试有什么问题。

6 个答案:

答案 0 :(得分:13)

关于您的问题:

  

我现在正在尝试更新一个表   没有太大的余地   独特性,除了在   人工主键。这意味着   新记录存在风险   可能不是唯一的,我不喜欢   添加一个字段只是为了强制唯一性。

如果 使用自动增量作为主键,那么您具有唯一性,可以使用SELECT @@Identity;获取上次自动生成ID的值(请参阅下面的警告)。 / p>

如果您使用自动增量,并且您要从Access插入记录但想从Excel中检索最后一个记录:

  • 确保您的主键是可排序的,因此您可以使用以下任一查询获取最后一个:

    SELECT MAX(MyPrimaryField) FROM MyTable;
    SELECT TOP 1 MyPrimaryField FROM MyTable ORDER BY MyPrimaryField DESC;
    
  • 或者,如果对主要字段进行排序不会提供最后一个字段,则需要添加DateTime字段(例如InsertedDate)并在每次创建时保存当前日期和时间该表中的新记录,以便您可以像这样得到最后一个:

    SELECT TOP 1 MyPrimaryField FROM MyTable ORDER BY InsertedDate DESC;
    

在上述任何一种情况下,我认为您会发现添加AutoIncrement主键更容易处理:

  • 这不会花太多钱

  • 这将保证您的记录的独特性,而不必考虑它

  • 您可以更轻松地使用@@Identity或通过主键排序或获取Max()来选择最新记录。

来自Excel

要将数据导入Excel,您有几个选择:

  • 使用查询创建数据链接,因此您可以直接在Cell或范围内使用结果。

  • 来自VBA的查询:

    Sub GetLastPrimaryKey(PrimaryField as string, Table as string) as variant
        Dim con As String
        Dim rs As ADODB.Recordset
        Dim sql As String
        con = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
              "Data Source= ; C:\myDatabase.accdb"
        sql = "SELECT MAX([" & PrimaryField & "]) FROM [" & MyTable & "];"
        Set rs = New ADODB.Recordset
        rs.Open sql, con, adOpenStatic, adLockReadOnly
        GetLastPrimaryKey = rs.Fields(0).Value
        rs.Close
        Set rs = Nothing
    End Sub
    

关于@@Identity

的说明

在标准Access数据库(*)中使用@@Identity时,您必须是careful of the caveats

  • 它仅适用于AutoIncrement标识字段。

  • 仅在您使用ADO并运行SELECT @@IDENTITY;

  • 时才可用
  • 返回最新使用的计数器but that's for all tables。你不能用它来返回MS Access中特定表的计数器(据我所知,如果你使用FROM mytable指定一个表,它就会被忽略)。
    简而言之,返回的值可能与您期望的完全不同。

  • 您必须在INSERT后直接查询,以尽量减少错误答案的风险 这意味着,如果您一次插入数据并需要在其他时间(或其他地方)获取最后一个ID,则无法使用。

  • 最后但并非最不重要的是,只有在通过编程代码插入记录时才设置变量 这意味着通过用户界面添加了记录,@@IDENTITY将不会被设置。

(*):为了清楚起见,@@IDENTITY表现不同,并且以更具预测性的方式,如果您对数据库使用ANSI-92 SQL模式。
但问题是ANSI 92的语法略有不同 Access支持的ANSI 89 flavor,用于在Access用作前端时提高与SQL Server的兼容性。

答案 1 :(得分:7)

如果人工密钥是自动编号,则可以使用@@ identity。

请注意,对于这两个示例,事务与其他事件隔离,因此返回的标识是刚刚插入的标识。您可以通过暂停Debug.Print db.RecordsAffected或Debug.Print lngRecs中的代码并手动将记录插入Table1来测试这一点,继续代码并注意返回的标识不是手动插入的记录,而是之前的记录由代码插入。

DAO示例

'Reference: Microsoft DAO 3.6 Object Library '
Dim db As DAO.Database
Dim rs As DAO.Recordset

Set db = CurrentDb

db.Execute ("INSERT INTO table1 (field1, Crdate ) " _
            & "VALUES ( 46, #" & Format(Date, "yyyy/mm/dd") & "#)")
Debug.Print db.RecordsAffected
Set rs = db.OpenRecordset("SELECT @@identity AS NewID FROM table1")
Debug.Print rs.Fields("NewID")

ADO示例

Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset

Set cn = CurrentProject.Connection

cn.Execute ("INSERT INTO table1 (field1, Crdate ) " _
            & "VALUES ( 46, #" & Format(Date, "yyyy/mm/dd") & "#)"), lngRecs
Debug.Print lngRecs
rs.Open "SELECT @@identity AS NewID FROM table1", cn
Debug.Print rs.Fields("NewID")

答案 2 :(得分:3)

Re:“我试图让@@ IDENTITY工作,但这总是使用下面的代码返回0。”

您的代码通过不同的连接对象发送SQLSQL2。我不认为@@identity将返回除零以外的任何内容,除非您从执行INSERT语句的同一连接询问。

尝试更改此内容:

myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly

为:

myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly

答案 3 :(得分:0)

尝试以下宏代码。首先从控制框向工作表添加一个命令按钮,然后在代码窗口中粘贴以下代码

Private Sub CommandButton1_Click()
    MsgBox GetLastPrimaryKey
End Sub

Private Function GetLastPrimaryKey() As String
Dim con As String
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sql As String
con = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\myaccess.mdb;Persist Security Info=False"
sql = "SELECT MAX(id) FROM  tblMyTable"

Set cn = New ADODB.Connection
Set rs = New ADODB.Recordset
cn.Open con
rs.Open sql, cn, 3, 3, 1
If rs.RecordCount <> 0 Then
   GetLastPrimaryKey = rs.Fields(0).Value
End If
rs.Close
cn.Close
Set rs = Nothing
Set cn = Nothing
End Function

答案 4 :(得分:0)

这是我的解决方案,不使用@@ index或MAX。

Const connectionString = "Provider=SQLOLEDB; Data Source=SomeSource; Initial Catalog=SomeDB; User Id=YouIDHere; Password=YourPassword"
Const RecordsSQL = "SELECT * FROM ThatOneTable"

Private Sub InsertRecordAndGetID()
    Set connection = New ADODB.connection
    connection.connectionString = connectionString
    connection.Open
    Set recordset = New ADODB.recordset
    recordset.Open SQL, connection, adOpenKeyset, adLockOptimistic

    With recordset
        .AddNew
        !Field1 = Value1
        !Field2 = Value2
    End With

    recordset.MoveLast
    ID = recordset.Fields("id")

End Sub

享受!

答案 5 :(得分:0)

晚会结束8年......您遇到的问题是您正在使用dbConnectionString创建连接。 @@ identity特定于您正在使用的连接。

首先,不要关闭原始连接

'.Close

替换

myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly

使用之前用于插入的连接

myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly

你已经全部准备好了。事实上,您甚至不需要指定表格:

SQL2 = "SELECT @@identity AS NewID"