2009-02-27 272 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" 
相關問題