Как получить идентификатор вновь вставленной записи с помощью Excel VBA? - PullRequest
8 голосов
/ 27 февраля 2009

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

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

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

Каков наилучший способ вставить запись в таблицу 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

Что-нибудь выделяется, будучи ответственным?

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

Ответы [ 6 ]

13 голосов
/ 28 февраля 2009

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

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

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

Если вы не используете автоинкремент, и вы вставляете записи из 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 Identity.

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

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

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

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

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

7 голосов
/ 27 февраля 2009

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

Обратите внимание, что в обоих этих примерах транзакция изолирована от других событий, поэтому возвращаемое удостоверение является только что вставленным. Вы можете проверить это, приостановив код в Debug.Print db.RecordsActed или 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")
3 голосов
/ 12 марта 2009

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

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

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

myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly

до:

myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly
0 голосов
/ 10 мая 2017

8 лет с опозданием на вечеринку ... Проблема в том, что вы используете dbConnectionString для создания нового соединения. @@ identity зависит от того, какое соединение вы используете.

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

'.Close

заменить

myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly

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

myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly

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

SQL2 = "SELECT @@identity AS NewID"
0 голосов
/ 28 января 2012

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

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

0 голосов
/ 01 марта 2009

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

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
...