2009-02-27 161 views
7

Parece un problema bastante común esto, pero la mayoría de las soluciones se refieren a concatenar múltiples comandos SQL, algo que creo que no se puede hacer con ADO/VBA (sin embargo, me complacerá que se muestre mal al respecto).¿Cómo obtener la identificación del nuevo registro insertado usando Excel VBA?

Actualmente inserto mi nuevo registro y luego ejecuto una consulta de selección utilizando (espero) suficientes campos para garantizar que solo se pueda devolver el registro recién insertado. Raramente se accede a mis bases de datos por más de una persona a la vez (riesgo insignificante de que se realice otra inserción entre consultas) y debido a la estructura de las tablas, identificar el nuevo registro es normalmente bastante fácil.

Estoy tratando de actualizar una tabla que no tiene mucho alcance para la unicidad, que no sea la clave primaria artificial. Esto significa que existe el riesgo de que el nuevo registro no sea único y me da pena agregar un campo solo para forzar la exclusividad.

¿Cuál es la mejor manera de insertar un registro en una tabla de Access y luego consultar la nueva clave primaria de Excel en esta situación?

Gracias por las respuestas. Intenté que @@IDENTITY funcionara, pero esto siempre devuelve 0 utilizando el siguiente código.

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 

Cualquier cosa se destacan ser responsable?

Sin embargo, dadas las advertencias proporcionadas amablemente por Renaud (abajo) parece casi tanto riesgo con el uso de @@IDENTITY al igual que con cualquier otro método, por lo que he recurrido a la utilización de SELECT MAX por ahora. Sin embargo, para referencia futura, me interesaría ver qué está mal con mi intento anterior.

+0

usando 'Max' puede ser complicado ... si otra persona es la inserción de registros, puede darle Identificación de otro registro. Si usted es el único que está insertando registros, continúe. –

Respuesta

12

Sobre su pregunta:

Ahora estoy tratando de actualizar una tabla que no lo hace hav e gran alcance para la singularidad , excepto en la clave primaria artificial . Esto significa existe el riesgo de que el nuevo registro no sea único, y detestamos agregar un campo solo para forzar la exclusividad.

Si son utilizando un AutoIncrement de su clave primaria, entonces usted tiene la singularidad y se podía utilizar SELECT @@Identity; para obtener el valor de la última ID autogenerado (ver advertencias a continuación).

Si usted es no utilizando incremento automático, y va a insertar los registros de acceso, pero que desea recuperar el último de Excel:

  • asegurarse de que su clave primaria se puede ordenar, por lo que puede obtener la última mediante una consulta al igual que cualquiera de éstos:

    SELECT MAX(MyPrimaryField) FROM MyTable; 
    SELECT TOP 1 MyPrimaryField FROM MyTable ORDER BY MyPrimaryField DESC; 
    
  • o, si su campo de clasificación primario no le daría la última, que tendría que añadir un campo DateTime (digamos InsertedDate) una nd guardar la fecha y la hora actuales cada vez que se crea un nuevo registro de esa tabla por lo que podría obtener la última como esta:

    SELECT TOP 1 MyPrimaryField FROM MyTable ORDER BY InsertedDate DESC; 
    

En cualquiera de estos casos, creo que puedes encontrar la adición de una AutoIncrement clave primaria por ser mucho más fácil de tratar:

  • no va a costar mucho

  • se va a garantizar que la singularidad de sus registros sin tener para pensarlo

  • Le facilitará la tarea de seleccionar el registro más reciente, ya sea usando @@Identity o clasificándolo por la clave principal o recibiendo el Max().

De Excel

para obtener los datos en Excel, tiene un par de opciones:

  • crear un enlace de datos mediante una consulta, por lo que puede utilizar el resultado directamente en una celda o un rango.

  • consulta de 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 
    

Nota sobre @@Identity

Tienes que ser careful of the caveats cuando se utiliza @@Identity en bases de datos de acceso estándar (*):

  • Solo funciona con los campos de AutoIncrement Identity.

  • sólo está disponible si se utiliza ADO y ejecutar SELECT @@IDENTITY;

  • Devuelve el último contador utilizado, but that's for all tables. No puede usarlo para devolver el contador de una tabla específica en MS Access (hasta donde yo sé, si especifica una tabla usando FROM mytable, simplemente se ignora).
    En resumen, el valor devuelto puede no ser el esperado.

  • Debe consultarlo directamente después de INSERT para minimizar el riesgo de obtener una respuesta incorrecta.
    Eso significa que si está insertando sus datos a la vez y necesita obtener la última ID en otro momento (u otro lugar), no funcionará.

  • Por último, la variable se establece solo cuando los registros se insertan a través del código de programación.
    Esto significa que el registro se agregó a través de la interfaz de usuario, @@IDENTITY no se establecerá.

(*): sólo para ser claros, @@IDENTITY comporta de manera diferente, y de una manera más predictiva, si se utiliza el modo ANSI SQL-92 para su base de datos.
El problema es que ANSI 92 tiene una sintaxis ligeramente diferente que , el sabor ANSI 89 admitido por Access y está destinado a aumentar la compatibilidad con SQL Server cuando Access se utiliza como interfaz.

+1

"SELECT MAX" huele muy mal. Si bien funcionará en las circunstancias descritas, como en todos los casos, las cosas cambian con el tiempo y, antes de que te des cuenta, ¡este código fallará! No hagas mal el código, piensa en el próximo chico – TFD

+1

@TFD: de acuerdo en principio, pero no hay muchas maneras de lograr lo que el afiche quiere, al menos en MS Access. Siempre va a ser una compensación. Max() funcionará bien para la mayoría de los tipos de datos, incluidas las cadenas, aunque estoy de acuerdo en que probablemente haya casos en los que no obtenga lo que desea. –

+0

Gracias por la respuesta detallada y por las advertencias útiles sobre @@ IDENTIDAD: votaría si pudiera. – Lunatik

7

Si la clave artificial es un autonumber, puede usar @@ identity.

Tenga en cuenta que con ambos ejemplos, la transacción está aislada de otros eventos, por lo que la identidad devuelta es la que acaba de insertar. Puede probar esto pausando el código en Debug.Print db.RecordsAffected o Debug.Print lngRecs e insertando un registro manualmente en Table1, continúe el código y tenga en cuenta que la identidad devuelta no es la del registro insertado manualmente, sino de la anterior registro insertado por código.

DAO Ejemplo

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

Ejemplo 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

Gracias, intentaremos esto. – Lunatik

+0

Con esto obtengo un error 3001 "Los argumentos son del tipo incorrecto, están fuera del rango aceptable o están en conflicto uno con el otro". Tampoco puedo encontrar "OpenRecordset" en el navegador de objetos, lo que podría explicar algo. Tengo la biblioteca de ADO 2.6 establecida como referencia, si eso sirve de ayuda. – Lunatik

+0

El código es para DAO, que generalmente es mejor para MS Access. Debería poder ejecutar algo en ADO, si lo necesitas. – Fionnuala

0

Try siguiente macro code.First añadir un botón de comando a la hoja de la caja de control y pegar siguientes códigos en la ventana de código

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

Re: "Me han tratado de conseguir @@ IDENTITY de trabajo, pero esto siempre devuelve 0 usando el siguiente código ".

Su código envía SQL y SQL2 a través de diferentes objetos de conexión. No creo que @@identity devuelva nada que no sea cero a menos que pregunte desde la misma conexión donde ejecutó su declaración INSERT.

trate de cambiar esto:

myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly 

a:

myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly 
+0

¡Oh, eso es lo que obtengo por reutilizar trozos de código de diferentes proyectos! Estoy en otro proyecto por el momento, pero tu sugerencia tiene mucho sentido y la intentaré cuando tenga oportunidad. – Lunatik

0

aquí está mi solución que no utiliza @@ índice o 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 años tarde en la fiesta ... El problema que está teniendo es que está utilizando dbConnectionString para crear una nueva conexión . @@ identity es específico de la conexión que está utilizando.

En primer lugar, no cierre la conexión original

'.Close 

reemplazar

myRecordset.Open SQL2, dbConnectionString, adOpenStatic, adLockReadOnly 

con la conexión que utilizó previamente para la inserción

myRecordset.Open SQL2, databaseConnection, adOpenStatic, adLockReadOnly 

y que le ha sido todo listo.De hecho, ni siquiera es necesario especificar la tabla:

SQL2 = "SELECT @@identity AS NewID" 
Cuestiones relacionadas