Как получить идентификатор вновь вставленной записи с помощью Excel VBA?

кажется достаточно распространенной проблемой, но большинство решений относятся к объединению нескольких команд SQL, что, по моему мнению, не может быть сделано с ADO/VBA (я буду рад быть показан неправильно в этом отношении, однако).

в настоящее время я вставляю свою новую запись, а затем запускаю запрос select, используя (Я надеюсь) достаточно полей, чтобы гарантировать, что только недавно вставленная запись может быть возвращена. Мои базы данных редко доступны более чем одному человеку за раз (незначительный риск другой вставки происходит между запросами) и из-за структуры таблиц идентификация новой записи обычно довольно проста.

теперь я пытаюсь обновить таблицу, которая не имеет много сфер, где уникальность, кроме искусственного первичного ключа. Это означает, что есть риск, что новая запись может быть не уникальной, и я ненавижу добавлять поле только для того, чтобы заставить уникальность.

каков наилучший способ вставить запись в таблицу доступа, а затем запросить новый первичный ключ из 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

что-нибудь отвечает?

однако, учитывая предостережения, любезно предоставленные Рено (ниже), кажется, почти столько же риска с использованием @@IDENTITY как и любой другой метод, поэтому я прибегла к помощи SELECT MAX сейчас. Для дальнейшего использования, хотя мне было бы интересно узнать, что не так с моим попытка выше.

6 ответов


о вашем вопросе:

теперь я пытаюсь обновить таблицу не имеет особого смысла своеобразие, другие, чем в искусственный первичный ключ. Это средство есть риск, что новый рекорд может быть, не уникальна, и я ненавижу добавьте поле, чтобы заставить уникальность.

если вы are использование автоинкремента для вашего первичного ключа, тогда у вас есть уникальность, и вы можете использовать SELECT @@Identity; получить значение последнего идентификатора автоматически (см. предупреждения ниже).

если вы не С помощью autoincrement, и вы вставляете записи из 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, у вас есть несколько вариантов:

  • создайте ссылку данных с помощью запроса, чтобы вы могли использовать результат непосредственно в ячейке или диапазон.

  • запрос от 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

вы должны быть осторожнее с оговорками при использовании @@Identity в стандартных базах данных Access (*):

  • он работает только с полями идентификации AutoIncrement.

  • он доступен только при использовании ADO и run SELECT @@IDENTITY;

  • он возвращает последний используемый счетчик,но это все таблицы. Вы не можете использовать его для возврата счетчика для определенной таблицы в MS Access (насколько я знаю, если вы укажете таблицу с помощью FROM mytable, оно просто игнорируется).
    Короче говоря, возвращаемое значение может быть совсем не тем, которое вы ожидаете.

  • вы должны запросить его сразу после INSERT чтобы свести к минимуму риск получения неправильного ответа.
    Это означает, что если вы вставляете ваши данные в одно время и должны получить последний идентификатор в другое время (или в другом месте), это не сработает.

  • и последнее, но не менее важное: переменная устанавливается только тогда, когда записи вставляются через программный код.
    Это означает, что запись была добавлена через пользовательский интерфейс,@@IDENTITY не будет.

(*): просто для ясности, @@IDENTITY ведет себя по-другому и более предсказуемо, если вы используете режим ANSI-92 SQL для своего база данных.
Проблема в том, что ANSI 92 имеет немного другой синтаксис, чем ароматизатор ANSI 89 поддерживается Access и предназначен для повышения совместимости с SQL Server при использовании Access в качестве интерфейса.


если искусственный ключ является автономером, вы можете использовать @@identity.

обратите внимание, что в обоих этих примерах транзакция изолирована от других событий, поэтому возвращаемый идентификатор является только что вставленным. Вы можете проверить это, приостановив код в Debug.Печать db.RecordsAffected или Debug.Распечатайте 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")

Re: " Я попытался заставить @ @ IDENTITY работать, но это всегда возвращает 0, используя приведенный ниже код."

ваш код отправляет SQL и SQL2 через различные объекты подключения. Я не думаю @@identity вернет все, кроме нуля, если вы не спросите из того же соединения, где вы выполнили свой INSERT заявление.

попробуйте изменить это:

myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly

в:

myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly

вот мое решение, которое не использует @@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

наслаждайтесь!


попробовать следующий код.Добавить кнопку на лист из блока управления и вставьте следующий код в окно кода

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 для создания новая подключение. @@identity зависит от используемого соединения.

во-первых, не закрывайте исходное соединение

'.Close

заменить

myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly

подключение вы ранее использовали для вставки

myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly

и Вы были бы все готово. Фактически, вам даже не нужно указывать таблицу:

SQL2 = "SELECT @@identity AS NewID"