@@IDENTITY после оператора INSERT всегда возвращает 0

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


person Gillian    schedule 09.10.2008    source источник
comment
Не могли бы вы уточнить, какой сервер базы данных вы используете, и, возможно, встроить ссылки на InitializeAndOpenConnection/connection.CreateCommand, поскольку они могут повлиять на наши ответы вам? :)   -  person Rob    schedule 09.10.2008
comment
Кроме того, весь SELECT OID FROM x, где OID = SCOPE_IDENTITY(), несколько сложнее, как вы говорите (для записи, вставленной со значением идентификатора 3): SELECT 3 FROM table_x WHERE 3 = 3 - довольно избыточно   -  person Rob    schedule 09.10.2008
comment
@Rob: это может показаться излишним, но OID имеет тип int, а scope_identity() — нет, поэтому вы можете напрямую привести (int) cmd.ExecuteScalar()   -  person devio    schedule 09.10.2008
comment
Вы не используете базу данных Access — вы используете базу данных Jet.   -  person David-W-Fenton    schedule 12.10.2008
comment
Я окончательный ответ на этот вопрос было бы очень приятно. Кажется, что вся эта ветка заполнена дезинформацией и решениями для SQL Server, а не для двигателя Jet.   -  person Matthew Ruston    schedule 25.11.2008


Ответы (12)


1) объединить оператор INSERT и SELECT (объединить с помощью «;») в 1 команду db

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

ВСТАВИТЬ В blabla... ; ВЫБЕРИТЕ OID ИЗ таблицы, ГДЕ OID = SCOPE_IDENTITY()

-- Обновить:

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

person devio    schedule 09.10.2008
comment
Причина в том, что в противном случае вы вводите две команды, а вторая команда знает zilch о первой команде, и поэтому, пока вы создали значение IDENTITY, вы не получаете его из второй (отключенной) команды. - person ; 09.10.2008
comment
Это дает мне символы, найденные после окончания ошибки оператора SQL. - person Gillian; 09.10.2008
comment
просто используйте SELECT SCOPE_IDENTITY() после первого оператора. см. мою ссылку ниже для хорошего объяснения этого. - person evilhomer; 09.10.2008
comment
-1 База данных Jet/ACE, поэтому этот ответ полностью недействителен. SQL является общим тегом. - person Fionnuala; 29.01.2012
comment
@Remou взгляните на историю: теги были добавлены после моего ответа, поэтому я обновил свой ответ. Ваш комментарий может быть не совсем недействительным, но как минимум с опозданием на 3 года. - person devio; 30.01.2012
comment
По моему собственному опыту, Stackoverflow — это живой сайт, и правильный ответ, который впоследствии стал неверным, получает отрицательную оценку. Я рад, что привлек ваше внимание к вашему сообщению, потому что оно получило наибольшее количество голосов и могло вызвать некоторое замешательство у любого, кто ищет ответ на проблему, изложенную и отмеченную в настоящее время. Вы получили 11 голосов за ответ, который стал неправильным. Это довольно хорошо, и не то, что часто случается с правильными ответами в теге доступа :) - person Fionnuala; 30.01.2012

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

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

person Adrian    schedule 09.10.2008
comment
После такой же проблемы я пришел к такому же выводу. Все остальные решения в других ответах просто неприменимы к Access/Jet. - person Daniel Rikowski; 25.11.2010

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

person onedaywhen    schedule 09.10.2008

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

Вы также всегда должны использовать SCOPE_IDENTITY(), а не @@identity. Ссылка здесь

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

SELECT SCOPE_IDENTITY() 

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

person evilhomer    schedule 09.10.2008

Я думаю, вам нужно иметь Select @@identity с первой командой создания - попробуйте добавить его через "; SELECT @@Identity" и .ExecuteScalar оператор вставки

person Richard Harrison    schedule 09.10.2008

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

person Matt Hamilton    schedule 09.10.2008
comment
Я думаю, вам следует использовать SCOPE_IDENTITY, так как он возвращает последнее значение идентификатора, сгенерированное для любой таблицы в текущем сеансе и текущей области, поэтому, если он вызывается сразу после вставки в вашу таблицу, вы гарантированно получите правильное значение. - person kristof; 09.10.2008
comment
С IDENT_CURRENT, хотя вы указываете имя таблицы, вы не гарантируете, что оно находится в рамках вашей операции, см. определение: Возвращает последнее значение идентификатора, сгенерированное для указанной таблицы или представления. Последнее сгенерированное значение идентификатора может относиться к любому сеансу и любой области. - person kristof; 09.10.2008
comment
Я думаю, что у обоих есть свои недостатки. Вероятность получения другого идентификатора из той же таблицы аналогична получению идентификатора из другой таблицы в той же области. Либо лучше, чем @@Identity. - person Matt Hamilton; 09.10.2008
comment
Привет, Мэтт, если вы вызываете вставку myTable, тогда вы знаете, что scope_identity относится к myTable, и вам гарантируется, что она находится в текущей области. При вызове IDENT_CURRENT('myTable') вам не гарантируется область действия, поэтому она находится вне вашего контроля. Было бы неплохо иметь scope_identity('myTable') :-) - person kristof; 09.10.2008
comment
Не используйте Ident_current. Один из наших программистов сделал это и вызвал хаос в базе данных, поскольку параллельные процессы приводили к тому, что связанные таблицы содержали записи, относящиеся к неправильному родителю. Пожалуйста, измените на scope_identity. - person HLGEM; 09.10.2008

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

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

person ila    schedule 09.10.2008

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

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

person Geir-Tore Lindsve    schedule 09.10.2008
comment
Дох, только что прочитал, что вы использовали MS Access, а не SQL Server. Не уверен, что мой ответ относится к MS Access... - person Geir-Tore Lindsve; 09.10.2008

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

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

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

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

person moffdub    schedule 09.10.2008
comment
MS Access 2000 и более поздние версии не поддерживают @@IDENTITY — вы ошиблись. @@IDENTITY поддерживается только начиная с MS Access 2000. ОП сказал, что они используют Jet 4.0, который сначала поставлялся с MS Access 2000. - person onedaywhen; 09.10.2008
comment
Большинство ответивших не забывают об этом - тот факт, что целевым механизмом базы данных был Access, не был указан до тех пор, пока не было сделано несколько ответов :) - person Rob; 09.10.2008
comment
Мораль этой истории: не отвечайте на вопросы в 6 утра. - person moffdub; 10.10.2008
comment
Ядро базы данных — это не Access, поскольку Access — это не ядро ​​базы данных, а платформа разработки приложений для баз данных, которая поставляется с ядром базы данных по умолчанию, т. е. Jet. - person David-W-Fenton; 12.10.2008

Если вы хотите получить значение автоматически выполняющегося номера транзакции, которую вы вставляете, и вашей среды, выполните следующие действия: 1. База данных — MsAccess. 2. Драйвером является Jet4 со строкой подключения, подобной этой "Provider=Microsoft.Jet.OLEDB.4.0;Password={0};Data Source={1};Persist Security 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();
}   
person Fuangwith S.    schedule 22.05.2009

Краткий ответ:
1. Создайте две команды, каждая из которых принимает один запрос.
2. Первый запрос sql — это запись INSERT.
3. Второй запрос sql — «SELECT @@Identity;» который возвращает AutoNumber.
4. Используйте cmd.ExecuteScalar(), который возвращает первый столбец первой строки.
5. Возвращаемый результат — это значение AutoNumber, сгенерированное в текущем запросе на вставку.

На него ссылается эта ссылка. Пример кода приведен ниже. Обратите внимание на разницу между «ТО ЖЕ СОЕДИНЕНИЕ VS НОВОЕ соединение». ЖЕ СОЕДИНЕНИЕ дает желаемый результат.

 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
person user1943915    schedule 02.06.2015

При использовании Access взгляните на эту статью из 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» в конец кода.

person Rob    schedule 09.10.2008