2009-02-27 80 views
7

似乎这是一个常见的问题,但大多数解决方案是指连接多个SQL命令,我相信不能用ADO/VBA完成(但我很乐意在这方面显示错误)。如何使用Excel VBA获取新插入记录的ID?

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

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

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

+0

使用`Max`可能会很棘手......如果别人插入记录,它可能会给你另一个记录的ID。如果您是唯一一个插入记录,请继续。 – 2013-05-26 03:48:41

回答

12

关于你的问题:

现在我试图更新表 不HAV在 唯一性的范围内,除了在 人工主键。这意味着 存在新记录 可能不是唯一的风险,并且我不喜欢 添加一个字段只是为了强制唯一性。

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

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

  • 确保您的主键是可排序的,这样你就可以获得使用查询像任一的最后一个:

    SELECT MAX(MyPrimaryField) FROM MyTable; 
    SELECT TOP 1 MyPrimaryField FROM MyTable ORDER BY MyPrimaryField DESC; 
    
  • ,或者,如果排序主场不会给你最后一个,你就需要添加一个DateTime字段(比如InsertedDate)一ND每次创建该表中的新记录的时间保存当前的日期和时间,所以你能得到最后一个是这样的:

    SELECT TOP 1 MyPrimaryField FROM MyTable ORDER BY InsertedDate DESC; 
    

在这两种情况下,我想你会发现添加自动增加的主键是一个更容易对付:

  • 它不会花费你太多

  • 这将保证你的独特性的记录,而不必想一想吧

  • 这会让你更容易选择最近的记录,使用@@Identity或通过主键排序或获得Max()

从Excel中

获取数据到Excel,你有两个选择:

  • 使用查询创建数据链接,这样你就可以使用结果直接在一个单元格或一个范围内。从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

你必须careful of the caveats标准Access数据库使用@@Identity时(*):

  • 它只适用于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的兼容性。

7

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

请注意,对于这两个例子,事务都与其他事件隔离开来,所以返回的标识就是刚刚插入的标识。您可以通过暂停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") 
+0

谢谢,会试试看。 – Lunatik 2009-02-27 16:37:13

+0

有了这个,我得到一个3001错误“参数是错误的类型,超出了可接受的范围,或者彼此冲突”。我也无法在对象浏览器中找到“OpenRecordset”,这可能会有所解释。 我有ADO 2.6库设置为参考,如果这是任何帮助。 – Lunatik 2009-02-27 16:44:39

+0

该代码是针对DAO的,通常适用于MS Access。如果你需要的话,我应该可以在ADO中运行一些东西。 – Fionnuala 2009-02-27 17:03:40

0

尝试下面的宏code.First在代码窗口中添加一个命令按钮,从控制箱上的表并粘贴以下代码

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 
3

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

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

尝试修改此:

myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly 

到:

myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly 
0

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

Enjoy!

0

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

首先,不要关闭原来的连接

'.Close 

与您以前使用的插入

myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly 

连接取代

myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly 

,你会一直可以了,好了。事实上,你甚至不需要指定表格:

SQL2 = "SELECT @@identity AS NewID" 
相关问题