2008-10-09 20 views
13

Ich brauche eine Funktion, die eine INSERT-Anweisung für eine Datenbank ausführt und den Primärschlüssel Auto_Increment zurückgibt. Ich habe den folgenden C# -Code aber, während die INSERT-Anweisung gut funktioniert (ich kann den Datensatz in der Datenbank sehen, der PK wird korrekt generiert und Zeilen == 1), der ID-Wert ist immer 0. Irgendwelche Ideen auf was gehen könnte falsch?@@ IDENTITY nach INSERT-Anweisung gibt immer 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(); 
    } 

Als Reaktion auf Antworten, habe ich versucht:

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; 
    } 

aber ich bin jetzt den Fehler „Zeichen nach dem Ende der SQL-Anweisung gefunden“

Ich verwende eine MS Access bekommen Datenbank mit OleDb Verbindung, Provider = Microsoft.Jet.OLEDB.4.0

+0

Können Sie klären, welche Datenbankserver verwenden und Inline möglicherweise die Verweise auf InitializeAndOpenConnection/connection.CreateCommand wie sie unsere Antworten beeinflussen können Du? :) – Rob

+0

Auch - die ganze "SELECT OID FROM x wo OID = SCOPE_IDENTITY()" Thins ist etwas zu komplex, wie Sie sagen (für einen Datensatz mit einem Identity-Wert von 3 eingefügt): "SELECT 3 FROM table_x WHERE 3 = 3 "- irgendwie redundant – Rob

+0

@Rob: Es mag redundant erscheinen, aber OID ist Int eingegeben, wo scope_identity() nicht ist, so können Sie direkt (Int) Cmd.ExecuteScalar() – devio

Antwort

1

Ich denke, Sie müssen die Identität @@ Identität mit dem ersten erstellen Befehl haben - versuchen, es über "; SELECT @@ Identität" und .ExecuteScalar der Insert-Anweisung

0

Gibt es Trigger in Ihrer Tabelle, die möglicherweise in andere Tabellen eingefügt werden? Im Allgemeinen wird davon abgeraten, @@ Identity zugunsten von IDENT_CURRENT zu verwenden, damit Sie garantieren können, dass die zurückgegebene Identität für die Tabelle gilt, die Sie gerade eingefügt haben.

+1

Ich denke, Sie sollten SCOPE_IDENTITY verwenden, da es den letzten Identitätswert zurückgibt, der für jede Tabelle in der aktuellen Sitzung und dem aktuellen Bereich generiert wurde. Wenn er also direkt nach der Tabelle insert aufgerufen wird, erhalten Sie garantiert den richtigen Wert. – kristof

+0

Mit IDENT_CURRENT, obwohl Sie den Tabellenname angeben, garantieren Sie nicht, dass es innerhalb des Bereichs Ihrer Operation liegt, siehe Definition: "Gibt den letzten Identitätswert zurück, der für eine angegebene Tabelle oder Ansicht generiert wurde. Der letzte erzeugte Identitätswert kann für jede Sitzung sein und jeden Bereich " – kristof

+0

Ich denke, dass beide dann ihre Nachteile hat. Die Chancen, eine "andere" Identität aus der gleichen Tabelle zu bekommen, wären ähnlich, um eine Identität von einer anderen Tabelle im selben Umfang zu erhalten. Entweder ist besser als @@ Identität. –

0

Ich denke, dass @@ Identität nur im Rahmen des Befehls gültig ist - in Ihrem Fall, wenn Sie "Anweisung" ausführen.

Ändern Sie Ihre "Anweisung" so, dass die gespeicherte Prozedur direkt nach der INSERT-Anweisung den @@ IDENTITY-Wert zurückgibt und als Rückgabecode der Ausführung der gespeicherten Prozedur liest.

15

1) verbinden und die INSERT-Anweisung SELECT (verketten Verwendung ";") in 1-dB-Befehl

2) verwenden SCOPE_IDENTITY() anstelle von @@ identity

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

- Update:

wie sich herausstellte, dass die Frage zu MS ACCESS verwandt war, ich this article gefunden, die einfach lässt vermuten, dass der erste Befehl Wiederverwendung gesetzt und sein Command "SELECT @@ IDENTITY" sollte ausreichen.

+0

Der Grund warum ist, weil Sie sonst zwei Befehle ausgeben, und der zweite Befehl weiß zilch über den ersten Befehl und so, während Sie einen IDENTITY-Wert erstellt haben, erhalten Sie es nicht aus dem zweiten (nicht verbundenen) Befehl. –

+0

Dies gibt mir die "Zeichen gefunden nach Ende der SQL-Anweisung" Fehler – Gillian

+0

verwenden Sie einfach "SELECT SCOPE_IDENTITY()" nach der ersten Anweisung. Siehe meinen Link unten für eine gute Erklärung. – evilhomer

-1

Wie Sie Access verwenden, werfen Sie einen Blick auf this article von Aspfaq, scrollen Sie nach unten auf etwa die Hälfte der Seite. Der Code ist im klassischen ASP, aber hoffentlich sollten die Prinzipien noch bestehen.

Die SELECT @@ Identität wird schließlich als separater Ausführungskontext behandelt, glaube ich. Code dass sollte Arbeit sein würde:

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); 
    } 
} 

Sie würden wahrscheinlich wollen/müssen die Verkettung aufzuräumen, dass, obwohl die ‚SELECT @@ Identität‘ auf das Ende des Codes hinzufügt.

4

Sie müssen die Identität gleichzeitig mit dem Öffnen der ursprünglichen Verbindung zurückgeben. Geben Sie eine Ergebnismenge aus Ihrer Einfügung oder einer Ausgabevariablen zurück.

Sie sollten auch immer SCOPE_IDENTITY() nicht @@ Identität verwenden.Reference here

sollten Sie

SELECT SCOPE_IDENTITY() 

Nach dem Einsatz hinzufügen.

+2

-1 Nicht in einer Access-Datenbank. – Fionnuala

0

Überprüfen Sie Ihre Datenbankeinstellungen. Ich hatte vor einiger Zeit ein ähnliches Problem und stellte fest, dass die SQL Server-Verbindungseinstellung 'no count' aktiviert war.

In SQL Server Management Studio finden Sie dies, indem Sie im Objekt-Explorer mit der rechten Maustaste auf den Server klicken, Eigenschaften auswählen und dann zur Seite Verbindungen navigieren. Sehen Sie sich die Einstellungen für "Standard-Verbindungsoptionen" an

+0

Doh, lesen Sie, dass Sie MS Access und nicht SQL Server verwendet haben. Ich bin mir nicht sicher, ob meine Antwort für MS Access gilt ... –

+0

Kein Zugriff, aber Jet. –

4

Sie verwenden Jet (nicht SQL Server) und Jet kann nur eine SQL-Anweisung pro Befehl behandeln, daher müssen Sie SELECT @@IDENTITY in einem separaten Befehl ausführen, offensichtlich die Verwendung der gleiche Verbindung wie die INSERT.

0

Verpassen die meisten Antwortenden nicht, dass der Fragesteller SQL Server nicht verwendet?

Anscheinend, MS Access 2000 und später doesn't support @@IDENTITY. Die Alternative ist "Mit dem RowUpdated-Ereignis können Sie feststellen, ob ein INSERT aufgetreten ist, den letzten @@ IDENTITY-Wert abrufen und diesen in die Identitätsspalte der lokalen Tabelle im DataSet einfügen."

Und ja, das ist für eingebettete VBA in der Access DB. Dies ist immer noch außerhalb von Access über die Access Object Library aufrufbar.

Edit: ok, es wird unterstützt, Entschuldigung für die groggy frühmorgens Antwort. Aber der Rest dieser Antwort könnte helfen.

+0

"MS Access 2000 und höher unterstützt nicht @@ IDENTITY" - Sie haben das falsch verstanden. IDENTITY wird erst ab MS Access 2000 unterstützt. Das OP sagte, dass sie Jet 4.0 verwenden, das zuerst mit MS Access 2000 geliefert wurde. – onedaywhen

+0

Die meisten Beantworter vergessen das nicht - die Tatsache, dass die Zieldatenbank-Engine Access war, wurde erst nach ein paar Antworten angegeben :) – Rob

+0

Die Moral der Geschichte: Fragen nicht um 6 Uhr morgens beantworten – moffdub

7

Microsoft.Jet.OLEDB.4.0-Anbieter unterstützt Jet v3- und Jet v4-Datenbank-Engines jedoch SELECT @ IDENTITY wird für Jet v3 nicht unterstützt.

MSAccess 97 ist Jet v3 und unterstützt nicht SELECT @@ IDENTITY; Es wird auf MSAccess 2000 und höher unterstützt.

+0

Nach dem gleichen Problem bin ich zu dem gleichen Schluss gekommen. Alle anderen "Lösungen" in den anderen Antworten sind für Access/Jet einfach nicht anwendbar. –

0

Wenn Sie den Wert der automatisch ablaufenden Nummer der Transaktion, die Sie einfügen möchten, und Ihrer Umgebung abrufen möchten, gehen Sie folgendermaßen vor: 1. Die Datenbank ist MsAccess. 2. Treiber ist Jet4 mit Verbindungszeichenfolge wie dieser "Provider = Microsoft.Jet.OLEDB.4.0; Password = {0}; Data Source = {1}; Persist Security Info = True" 3. Verwendung Oledb


1. erstellen Sie zwei Befehle jeweils die Annahme einer einzigen Abfrage: Sie können mein Beispiel, um Ihren Code

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 Die Datenbank ist Jet/ACE. – Fionnuala

0

Die kurze Antwort gelten.
2. Erste SQL-Abfrage ist der INSERT-Datensatz.
3. Zweite SQL-Abfrage ist "SELECT @@ Identität;" was die AutoNummer zurückgibt.
4. Verwenden Sie cmd.ExecuteScalar(), das eine erste Spalte der ersten Zeile zurückgibt.
5. Die zurückgegebene Ergebnisausgabe ist der AutoNumber-Wert, der in der aktuellen Einfügeabfrage generiert wurde.

It is referenced from this link. Der Beispielcode ist wie unten. Beachten Sie den Unterschied für "SAME Verbindung VS NEUE Verbindung". Die SAME-Verbindung liefert die gewünschte Ausgabe.

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(); 
     } 
    } 
} 

Dies sollte die selbsterklärend Ausgabe erzeugen:

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

New connection, AutoNumber: 0