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

似乎这是一个普遍的问题,但大多数解决scheme是指连接多个SQL命令,我相信不能用ADO / VBA来完成(但我很高兴在这方面显示错误)。

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

我现在试图更新一个没有太多的唯一性范围的表,而不是人为的主键。 这意味着新的logging可能并不是独一无二的,我不愿意添加一个字段来强制唯一性。

在这种情况下,将logging插入到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 。 为了将来的参考,虽然我会有兴趣看看我上面的尝试有什么问题。

关于你的问题:

我现在试图更新一个没有太多的唯一性范围的表,而不是人为的主键。 这意味着新的logging可能并不是独一无二的,而且我不愿意增加一个字段来强制唯一性。

如果您使用的是主键的自动增量,那么您具有唯一性,您可以使用SELECT @@Identity; 得到最后自动生成的ID的值(参见下面的注意事项)。

如果您使用自动增量,并且您正在从Access中插入logging,但是您想从Excel中检索最后一个:

  • 确保你的主键是可sorting的,所以你可以使用下面的查询来获得最后一个:

     SELECT MAX(MyPrimaryField) FROM MyTable; SELECT TOP 1 MyPrimaryField FROM MyTable ORDER BY MyPrimaryField DESC; 
  • 或者,如果对主字段进行sorting不会给你最后一个字段,则需要添加一个DateTime字段(比如InsertedDate ),并且每次在该表中创build新logging时保存当前的date和时间,以便获得最后一个像这样的:

     SELECT TOP 1 MyPrimaryField FROM MyTable ORDER BY InsertedDate DESC; 

在这两种情况下,我认为你会发现添加一个AutoIncrement主键更容易处理:

  • 这不会花费太多

  • 这将保证你的logging的独特性,而不必考虑它

  • 这将使您更容易select最新的logging,可以使用@@Identity或通过主键sorting或获取Max()

从Excel

要将数据导入到Excel中,您有几个select:

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

  • 来自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时,您必须小心谨慎 :

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

  • 只有在使用ADO并运行SELECT @@IDENTITY;时才可用SELECT @@IDENTITY;

  • 它返回最新使用的计数器, 但这是所有表格 。 你不能用它来返回MS Access中的特定表的计数器(据我所知,如果你使用FROM mytable指定一个表,它只会被忽略)。
    总而言之,回报的价值可能不是你所期望的。

  • 您必须在INSERT后直接进行查询,以最大限度地减less得到错误答案的风险。
    这意味着,如果你一次插入你的数据,并且需要在另一个时间(或另一个地方)得到最后一个ID,它将不起作用。

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

(*):为了清楚@@IDENTITY@@IDENTITY行为有所不同,如果您对数据库使用ANSI-92 SQL模式,那么预测性更强。
但问题是,ANSI 92与Access支持的ANSI 89风格略有不同,当Access被用作前端时,它的目的是提高与SQL Server的兼容性。

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

请注意,对于这两个示例,事务都与其他事件隔离开来,所以返回的标识就是刚刚插入的标识。 您可以通过暂停Debug.Print db.RecordsAffected或Debug.Print lngRecs中的代码并手动在Table1中插入一条logging来testing此代码,请继续执行代码,并注意返回的标识不是手动插入的logging的标识,而是之前logging由代码插入。

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") 

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

您的代码通过不同的连接对象发送SQLSQL2 。 我不认为@@identity将返回除零之外的任何东西,除非您从相同的连接询问您执行INSERT语句的位置。

尝试改变这一点:

 myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly 

至:

 myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly 

这是我的解决scheme,不使用@@索引或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 

请享用!

尝试下面的macros代码。首先从控制框中添加一个命令button到工作表,并在代码窗口中粘贴以下代码

 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 

晚了8年……你遇到的问题是你正在使用dbConnectionString创build一个新的连接。 @@身份是特定于您正在使用的连接。

首先,不要closures原来的连接

 '.Close 

更换

 myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly 

用你以前用于插入的连接

 myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly 

你一切都已经安定了 事实上,你甚至不需要指定表格:

 SQL2 = "SELECT @@identity AS NewID"