2008-10-09 7 views
13

Мне нужна функция, которая выполняет инструкцию INSERT в базе данных и возвращает первичный ключ Auto_Increment. У меня есть следующий код C#, но, хотя оператор INSERT работает нормально (я вижу запись в базе данных, PK генерируется правильно, а строки == 1), значение id всегда равно 0. Любые идеи о том, что может произойти неправильно?@@ IDENTITY после инструкции INSERT всегда возвращает 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(); 
    } 

В ответ на ответы, я попробовал:

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

, но сейчас я получаю сообщение об ошибке «символов, обнаруженных после окончания SQL заявления»

Я использую MS Access база данных с соединением OleDb, Provider = Microsoft.Jet.OLEDB.4.0

+0

Не могли бы вы уточнить, что сервер базы данных вы используете и, возможно, INLINE ссылки на InitializeAndOpenConnection/connection.CreateCommand, как они могут повлиять на наши ответы ты? :) – Rob 2008-10-09 10:00:54

+0

Также - весь «SELECT OID FROM x, где OID = SCOPE_IDENTITY()« thins несколько более сложный, как вы говорите (для записи, вставленной с идентификационным значением 3): «SELECT 3 FROM table_x WHERE 3 = 3 "- kinda redundant – Rob 2008-10-09 10:02:21

+0

@Rob: может показаться избыточным, но OID набирается int, где scope_identity() нет, поэтому вы можете напрямую использовать (int) cmd.ExecuteScalar() – devio 2008-10-09 13:03:50

ответ

1

Я думаю, что вам нужно иметь идентификатор Select @@ с первой командой create - попробуйте добавить его через:; SELECT @@ Identity и .Execute Скаляр вставить заявление

0

Есть ли триггеры на вашей таблице, которые могут вставляться в другие таблицы? Как правило, нам не рекомендуется использовать @@ Identity в пользу IDENT_CURRENT, чтобы вы могли гарантировать, что возвращаемая идентификация предназначена для таблицы, в которую вы только что вставили.

+1

Я думаю, вы должны использовать SCOPE_IDENTITY, поскольку он возвращает последнее значение идентификации, сгенерированное для любой таблицы текущего сеанса и текущей области, поэтому, если она вызывается сразу после вставки в вашу таблицу, вы гарантированно получите правильное значение. – kristof 2008-10-09 09:57:07

+0

С IDENT_CURRENT, хотя вы указываете имя таблицы, вы не гарантируете, что оно находится в пределах вашей операции, см. Определение: «Возвращает последнее значение идентификации, сгенерированное для указанной таблицы или представления. Последнее генерируемое значение идентификации может быть для любого сеанса и любой области " – kristof 2008-10-09 09:58:37

+0

Я думаю, что у него есть свои недостатки. Шансы получить «другую» личность из той же таблицы будут похожи на получение идентичности из другой таблицы в той же области. Либо лучше, чем @@ Identity. – 2008-10-09 10:35:07

0

Я думаю, что идентификация @@ действительна только в рамках команды - в вашем случае, когда вы выполняете «инструкцию».

Измените свое «заявление», чтобы сама хранимая процедура вернула значение @@ IDENTITY сразу после инструкции INSERT и прочитала ее как код возврата выполнения хранимой процедуры.

15

1) объединить вкладышем и ЗЕЬЕСТ (сцепить с помощью ";") в команде 1 дб

2) использовать SCOPE_IDENTITY() вместо @@ IDENTITY

INSERT INTO BlaBla ...; ВЫБРАТЬ OID из таблицы, где OID = SCOPE_IDENTITY()

- обновление:

, как выяснилось, что этот вопрос связан с MS ACCESS, я нашел this article, который предполагает, что просто повторное использование первой команды и установки его CommandText на «SELECT @@ IDENTITY» должно быть достаточно.

-1

Как вы используете Access, взгляните на this article из aspfaq, прокрутите страницу вниз примерно до половины. Код в классическом ASP, но, надеюсь, принципы должны по-прежнему стоять.


SELECT @@ Identity в конечном итоге рассматривается как отдельный контекст исполнения, я считаю. Код, который должен работа будет:

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

Вы, вероятно, хотите/должны привести в порядок конкатенации, который добавляет «SELECT @@ Identity» на конце кода, хотя.

4

вам необходимо вернуть идентификатор одновременно с открытием начального соединения. Возвратите набор результатов из вашей вставки или выходной переменной.

Вы также должны всегда использовать идентификатор SCOPE_IDENTITY() not @@.Reference here

Вы должны добавить

SELECT SCOPE_IDENTITY() 

После вставки.

0

Проверьте настройки своей базы данных. Некоторое время назад у меня была аналогичная проблема, и я обнаружил, что параметр подключения к серверу SQL Server не включен.

В SQL Server Management Studio вы можете найти это, щелкнув правой кнопкой мыши на сервере в Обозревателе объектов, выберите «Свойства», а затем перейдите на страницу «Соединения». Посмотрите настройки «Параметры подключения по умолчанию».

4

Вы используете Jet (не SQL Server), а Jet может обрабатывать только один оператор SQL для каждой команды, поэтому вам нужно выполнить команду SELECT @@IDENTITY в отдельной команде, очевидно, что она использует то же соединение, что и у INSERT.

0

Разве большинство респондентов не забывают, что обманщик не использует SQL Server?

По-видимому, MS Access 2000 и последующие doesn't support @@IDENTITY. Альтернативой является «Использование события RowUpdated, вы можете определить, произошел ли INSERT, получить последнее значение @@ IDENTITY и поместить его в столбец идентификатора локальной таблицы в DataSet».

И да, это для встроенного VBA в Access DB. Это все еще можно вызывать вне доступа через библиотеку объектов доступа.

Редактировать: хорошо, это поддерживается, извините за неуклюжий ответ раннего утра. Но остальная часть этого ответа может помочь.

7

Поставщик Microsoft.Jet.OLEDB.4.0 поддерживает двигатели базы данных Jet v3 и Jet v4, однако SELECT @@ IDENTITY не поддерживается для Jet v3.

MSAccess 97 - это Jet v3 и не поддерживает SELECT @@ IDENTITY; Он поддерживался MSAccess 2000 и выше.

0

Если вы хотите получить значение автоматического запуска числа транзакций, которые вы вставляете, и вашей среды после 1. База данных - это MsAccess. 2. Драйвер Jet4 с строки соединения, как этот "Provider = Microsoft.Jet.OLEDB.4.0; Password = {0}; Data Source = {1}; Упорство безопасности Info = True" 3. Использование OLEDB

Вы можете применить мой пример кода

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. Создайте две команды каждый прием одного запроса.
2. Первый запрос sql - это запись INSERT.
3. Второй запрос sql: «SELECT @@ Identity;» который возвращает AutoNumber.
4. Используйте cmd.ExecuteScalar(), который возвращает первый столбец первой строки.
5. Возвращенный результат - это значение AutoNumber, сгенерированное в текущем запросе вставки.

It is referenced from this link. Пример кода указан ниже. Обратите внимание на разницу в «SAME Connection VS NEW Connection». SAME Connection дает желаемый результат.

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

Это должно производить самоочевидная вывод:

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

New connection, AutoNumber: 0