2008-10-09 28 views
13

Necesito una función que ejecute una instrucción INSERT en una base de datos y devuelva la clave primaria Auto_Increment. Tengo el siguiente código C# pero, aunque la instrucción INSERT funciona bien (puedo ver el registro en la base de datos, la PK se genera correctamente y filas == 1), el valor de identificación es siempre 0. Cualquier idea sobre lo que podría estar pasando ¿incorrecto?@@ IDENTIDAD después de la instrucción INSERT siempre devuelve 0

public int ExecuteInsertStatement(string statement) 
    { 
     InitializeAndOpenConnection(); 
     int id = -1; 


     IDbCommand cmdInsert = connection.CreateCommand(); 
     cmdInsert.CommandText = statement; 
     int rows = cmdInsert.ExecuteNonQuery(); 

     if (rows == 1) 
     { 
      IDbCommand cmdId = connection.CreateCommand(); 
      cmdId.CommandText = "SELECT @@Identity;"; 
      id = (int)cmdId.ExecuteScalar(); 
     } 

     return id; 
    } 
    private void InitializeAndOpenConnection() 
    { 
     if (connection == null) 
      connection = OleDbProviderFactory.Instance.CreateConnection(connectString); 

     if(connection.State != ConnectionState.Open)     
      connection.Open(); 
    } 

En respuesta a las respuestas, he intentado:

public int ExecuteInsertStatement(string statement, string tableName) 
    { 
     InitializeAndOpenConnection(); 
     int id = -1; 
     IDbCommand cmdInsert = connection.CreateCommand(); 
     cmdInsert.CommandText = statement + ";SELECT OID FROM " + tableName + " WHERE OID = SCOPE_IDENTITY();"; 
     id = (int)cmdInsert.ExecuteScalar(); 

     return id; 
    } 

pero ahora estoy consiguiendo el error "Caracteres encontrado después del final de la instrucción SQL"

estoy usando un MS Access base de datos con conexión OleDb, Provider = Microsoft.Jet.OLEDB.4.0

+0

Podría aclarar qué servidor de base de datos que está utilizando y, posiblemente, las referencias a inline InitializeAndOpenConnection/connection.CreateCommand ya que pueden afectar a nuestras respuestas ¿tú? :) – Rob

+0

Además, el todo "SELECCIONAR OID FROM x donde OID = SCOPE_IDENTITY()" adelgaza es un tanto complejo como usted dice (para un registro insertado con un valor de identidad de 3): "SELECT 3 FROM table_x WHERE 3 = 3 "- un poco redundante – Rob

+0

@Rob: puede parecer redundante, pero OID está escrito int, donde scope_identity() no lo es, por lo que puede convertir directamente (int) cmd.ExecuteScalar() – devio

Respuesta

1

Creo que debe tener la identidad Select @@ con el primer comando de creación; intente anexarlo mediante "; SELECT @@ Identity" y .ExecuteScalar the insertar instrucción

0

¿Hay algún disparador en su tabla que pueda estar insertándose en otras tablas? En general, se desaconseja usar @@ Identity a favor del IDENT_CURRENT para que pueda garantizar que la identidad devuelta corresponde a la tabla que acaba de insertar.

+1

Creo que debería usar SCOPE_IDENTITY ya que devuelve el último valor de identidad generado para cualquier tabla en la sesión actual y el alcance actual, de modo que si se llama justo después de la inserción en su tabla, se garantiza que obtendrá el valor adecuado. – kristof

+0

Con IDENT_CURRENT aunque especifique el nombre de la tabla no garantiza que esté dentro del alcance de su operación, consulte la definición: "Devuelve el último valor de identidad generado para una tabla o vista especificada. El último valor de identidad generado puede ser para cualquier sesión y cualquier alcance " – kristof

+0

Creo que tiene sus inconvenientes. Las posibilidades de obtener "otra" identidad de la misma tabla sería similar a obtener una identidad de otra tabla en el mismo ámbito. Cualquiera de las dos es mejor que @@ Identity. –

0

Creo que @@ identity solo es válido en el alcance del comando, en su caso cuando ejecuta "statement".

Modifique su "declaración" para que el procedimiento almacenado devuelva el valor @@ IDENTITY justo después de la instrucción INSERT, y léala como el código de retorno de la ejecución del procedimiento almacenado.

15

1) combinan el INSERT y SELECT (concatenar usando ";") en orden de 1 dB

2) el uso SCOPE_IDENTITY() en lugar de @@ IDENTITY

INSERT INTO blabla ...; OID SELECT FROM tabla WHERE OID = SCOPE_IDENTITY()

- Actualización:

como se vio después de que la cuestión se relaciona con MS ACCESS, encontré this article lo que sugiere que simplemente reutilizando el primer comando y el establecimiento de su CommandText "SELECCIONAR @@ IDENTIDAD" debería ser suficiente.

+0

La razón es porque de lo contrario estás emitiendo dos comandos, y el segundo comando no sabe nada sobre el primer comando y, por lo tanto, mientras has creado un valor de IDENTIDAD no lo estás obteniendo del segundo comando (desconectado). –

+0

Esto me da el error "Caracteres encontrados después de la finalización de la instrucción SQL" – Gillian

+0

simplemente use "SELECT SCOPE_IDENTITY()" después de la primera instrucción. mira mi enlace a continuación para una buena explicación de esto. – evilhomer

-1

Como está utilizando Access, eche un vistazo a this article desde aspfaq, baje hasta aproximadamente la mitad de la página. El código está en ASP clásico, pero ojalá los principios aún se mantengan.


El SELECT @@ Identity termina siendo tratado como un contexto de ejecución independiente, creo. El código que debe habría trabajo:

public int ExecuteInsertStatement(string statement) 
{ 
    InitializeAndOpenConnection(); 

    IDbCommand cmdInsert = connection.CreateCommand(); 
    cmdInsert.CommandText = statement + "; SELECT @@Identity"; 
    object result = cmdInsert.ExecuteScalar(); 

    if (object == DBNull.Value) 
    { 
     return -1; 
    } 
    else 
    { 
     return Convert.ToInt32(result); 
    } 
} 

lo que probablemente quiere/necesita para poner en orden la concatenación que se suma el 'SELECT @@ Identidad' en el extremo del código sin embargo.

4

debe devolver la identidad al mismo tiempo que abre la conexión inicial. Devuelve un conjunto de resultados de su inserción o una variable de salida.

También debe usar siempre SCOPE_IDENTITY() no @@ identity.Reference here

Debe añadir

SELECT SCOPE_IDENTITY() 

Después de la inserción.

+2

-1 No está en una base de datos de Access. – Fionnuala

0

Compruebe la configuración de su base de datos. Hace un tiempo tuve un problema similar y descubrí que la configuración de conexión de SQL Server 'no count' estaba habilitada.

En SQL Server Management Studio, puede encontrarlo haciendo clic con el botón derecho en el servidor en el Explorador de objetos, seleccione Propiedades y luego vaya a la página Conexiones. Consulte las configuraciones para "Opciones de conexión predeterminadas"

+0

Doh, acaba de leer que estaba usando MS Access y no SQL Server. No estoy seguro si mi respuesta se aplica a MS Access ... –

+0

No se utiliza Access, pero Jet. –

4

Está utilizando Jet (no SQL Server) y Jet solo puede manejar una instrucción SQL por comando, por lo tanto, necesita ejecutar SELECT @@IDENTITY en un comando separado, obviamente asegurando que usa el misma conexión que el INSERT.

0

¿No está la mayoría de los que responden olvidando que el asker no está usando SQL Server?

Aparentemente, MS Access 2000 y posterior doesn't support @@IDENTITY. La alternativa es "Al usar el evento RowUpdated, puede determinar si se ha producido un INSERT, recuperar el último valor @@ IDENTITY y colocarlo en la columna de identidad de la tabla local en el DataSet".

Y sí, esto es para VBA incorporado en Access DB. Eso todavía se puede llamar fuera de Access a través de la Biblioteca de objetos de acceso.

Editar: ok, es compatible, lo siento por la aturdida respuesta de madrugada. Pero el resto de esta respuesta podría ayudar.

+0

"MS Access 2000 y posterior no es compatible con @@ IDENTITY": lo has hecho al revés. @@ IDENTITY solo es compatible con MS Access 2000 en adelante. El OP dijo que están usando Jet 4.0, que se despachó por primera vez con MS Access 2000. – onedaywhen

+0

La mayoría de los que responden no olvidan eso: el hecho de que el motor de la base de datos de destino era Access no se estableció hasta después de unas pocas respuestas correctas :) – Rob

+0

La moraleja de la historia: no responda preguntas a las 6 a.m. – moffdub

7

El proveedor Microsoft.Jet.OLEDB.4.0 admite motores de base de datos Jet v3 y Jet v4, sin embargo, SELECCIONAR @@ IDENTIDAD no es compatible con Jet v3.

MSAccess 97 es Jet v3 y no admite SELECT @@ IDENTITY; Es compatible con MSAccess 2000 y superior.

+0

Después de tener el mismo problema, he llegado a la misma conclusión. Todas las demás "soluciones" en las otras respuestas simplemente no son aplicables a Access/Jet. –

0

Si desea recuperar el valor del número de ejecución automática de la transacción que está insertando y su entorno después de 1. La base de datos es MsAccess. 2. conductor es Jet4 con cadena de conexión como este "Provider = Microsoft.Jet.OleDb.4.0; contraseña = {0}; Data Source = {1}; Persistir seguridad Info = True" 3. Uso Oledb

puede aplicar mi ejemplo a su código

OleDbConnection connection = String.Format("Provider=Microsoft.Jet.OLEDB.4.0;Password={0};Data Source={1};Persist Security Info=True",dbinfo.Password,dbinfo.MsAccessDBFile); 
connection.Open(); 
OleDbTransaction transaction = null; 
try{ 
    connection.BeginTransaction(); 
    String commandInsert = "INSERT INTO TB_SAMPLE ([NAME]) VALUES ('MR. DUKE')"; 
    OleDbCommand cmd = new OleDbCommand(commandInsert , connection, transaction); 
    cmd.ExecuteNonQuery(); 
    String commandIndentity = "SELECT @@IDENTITY"; 
    cmd = new OleDbCommandcommandIndentity, connection, transaction); 
    Console.WriteLine("New Running No = {0}", (int)cmd.ExecuteScalar()); 
    connection.Commit(); 
}catch(Exception ex){ 
    connection.Rollback(); 
}finally{ 
    connection.Close(); 
} 
-2
CREATE procedure dbo.sp_whlogin 
(
@id nvarchar(20), 
@ps nvarchar(20), 
@curdate datetime, 
@expdate datetime 
) 

AS 
BEGIN 
DECLARE @role nvarchar(20) 
DECLARE @menu varchar(255) 
DECLARE @loginid int 

SELECT  @role = RoleID 
FROM   dbo.TblUsers 
WHERE UserID = @id AND UserPass = @ps 

if @role is not null 
BEGIN 
    INSERT INTO TblLoginLog (UserID, LoginAt, ExpireAt, IsLogin) VALUES (@id, @curdate, @expdate, 1); 
    SELECT @loginid = @@IDENTITY; 
    SELECT @loginid as loginid, RoleName as role, RoleMenu as menu FROM TblUserRoles WHERE RoleName = @role 
END 
else 
BEGIN 
    SELECT '' as role, '' as menu 
END 
END 
GO 
+0

-1 La base de datos es Jet/ACE. – Fionnuala

0

La respuesta corta:
1. Cree dos órdenes cada uno aceptar una sola consulta.
2. La primera consulta sql es el registro INSERTAR.
3. La segunda consulta sql es "SELECT @@ Identity;" que devuelve el Autonumérico.
4. Use cmd.ExecuteScalar() que devuelve una primera columna de la primera fila.
5. La salida del resultado devuelto es el valor de Autonumérico generado en la consulta de inserción actual.

It is referenced from this link. El código de ejemplo es como debajo. Tenga en cuenta la diferencia para "MISMA conexión VS NUEVA conexión". La MISMA conexión da la salida deseada.

class Program 
{ 
    static string path = @"<your path>"; 
    static string db = @"Test.mdb"; 
    static void Main(string[] args) 
    { 
     string cs = String.Format(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0}\{1}", path, db); 
     // Using the same connection for the insert and the SELECT @@IDENTITY 
     using (OleDbConnection con = new OleDbConnection(cs)) 
     { 
      con.Open(); 
      OleDbCommand cmd = con.CreateCommand(); 
      for (int i = 0; i < 3; i++) 
      { 
       cmd.CommandText = "INSERT INTO TestTable(OurTxt) VALUES ('" + i.ToString() + "')"; 
       cmd.ExecuteNonQuery(); 

       cmd.CommandText = "SELECT @@IDENTITY"; 
       Console.WriteLine("AutoNumber: {0}", (int)cmd.ExecuteScalar()); 
      } 
      con.Close(); 
     } 
     // Using a new connection and then SELECT @@IDENTITY 
     using (OleDbConnection con = new OleDbConnection(cs)) 
     { 
      con.Open(); 
      OleDbCommand cmd = con.CreateCommand(); 
      cmd.CommandText = "SELECT @@IDENTITY"; 
      Console.WriteLine("\nNew connection, AutoNumber: {0}", (int)cmd.ExecuteScalar()); 
      con.Close(); 
     } 
    } 
} 

Esto debería producir la auto-explicativo de salida:

AutoNumber: 1 <br> 
AutoNumber: 2 <br> 
AutoNumber: 3 <br> 

New connection, AutoNumber: 0 
Cuestiones relacionadas