Как сделать INSERT INTO Firebird, с автоинкрементом для первичного ключа?

Как сделать INSERT INTO Firebird, с автоинкрементом для первичного ключа?

Для полей таблицы у меня есть:

fstPriority VARCHAR(30), fstInfo VARCHAR(100), fstDateCreated VARCHAR(30), fstDateModified VARCHAR(30), fiKeyID INTEGER PRIMARY KEY

Для INSERT INTO у меня есть:

FbConnection fbConn = new FbConnection(stOpenConn))
fbConn.Open();
...
FbTransaction fbTransaction = fbConn.BeginTransaction();
FbCommand fbCmd = new FbCommand("INSERT INTO " + stTableName + "(" + stFieldNames + ") VALUES ( @p0, @p1, @p2, @p3, @p4 ) RETURNING fiKeyID ", fbConn, fbTransaction);

но я не уверен, что следует использовать для fbCmd.Parameters.AddWithValue

fbCmd.Parameters.AddWithValue("@p0", "1st value");
fbCmd.Parameters.AddWithValue("@p1", "2nd value");
fbCmd.Parameters.AddWithValue("@p2", "3rd value");
fbCmd.Parameters.AddWithValue("@p3", "4th value");

Тогда что? Для fiKeyID, я должен добавить

fbCmd.Parameters.AddWithValue("@p4", "");

Кроме того, я вижу на http://www.firebirdfaq.org/faq29/ создание столбца автоинкремента, но не знаю, как это сделать в C# ... Firebird ADO.NET ... FirebirdClient.5.8.0 ... Visual Студия 2013.

CREATE GENERATOR ...;
SET GENERATOR ...;

set term !! ;
CREATE TRIGGER ...

не распознаются компилятором Visual Studio.

2 ответа

Решение

Важно то, что SET TERM не является частью синтаксиса оператора Firebird, а является клиентской функцией для установки терминатора оператора в инструментах запросов, таких как ISQL. Этот терминатор необходим, чтобы знать, когда оператор завершен, и может быть отправлен на сервер. По умолчанию эти инструменты делают это с помощью точки с запятой (;), но это не работает с PSQL (хранимые процедуры, триггеры), потому что код PSQL также использует точку с запятой. Для решения этой проблемы эти инструменты имеют SET TERM переключить этот терминатор.

Однако, используя поставщика Firebird ADO.net, вы должны выполнять операторы по одному, поэтому терминатор операторов не имеет значения.

Чтобы иметь возможность генерировать первичный ключ, вы можете использовать следующие решения:

  1. В Firebird 3 есть столбец идентификаторов, поэтому вам не нужно создавать последовательность и запускать ее самостоятельно:

    create table withgeneratedid(
        id integer generated by default as identity primary key, 
        column2 varchar(100)
    )
    
  2. Для Firebird 2.5 и более ранних версий вам нужно создать последовательность и триггер:

    create table withgeneratedid(
        id integer primary key,
        column2 varchar(100)
    );
    
    create sequence seq_withgeneratedid;
    
    set term #;
    create trigger withgeneratedid_bi before insert on withgeneratedid
    as
    begin
        if (new.id is null) then new.id = next value for seq_withgeneratedid;
    end#
    set term ;#
    

Когда вы вставляете значения в таблицу и хотите получить сгенерированный ключ, вам не следует включать столбец id в список столбцов. Включение столбца id позволяет переопределить значение ключа, но это может привести к тому, что в будущем при вставке будет создан дубликат ключа!. Если вы включите столбец id, то в примере Firebird 3 не будет сгенерирован ключ, в примере Firebird 2.5 будет сгенерирован ключ, если значение столбца равно nullв противном случае он примет предоставленное значение.

В ADO.net вам обычно нужно выполнять инструкции по отдельности (а не использовать set term). В качестве альтернативы, вы можете использовать FbScript проанализировать сценарий DDL и выполнить операторы синтаксического анализа. Обратите внимание, что FbScript поддерживает (и даже требует) set term,

Чтобы выполнить это с поставщиком Firebird ADO.net, вы можете сделать что-то вроде примера ниже. Я включил три варианта для создания таблицы Firebird3, Firebird2_5, а также FbScriptFB2_5 (что так же, как Firebird2_5 но использует FbScript). Также показано, как получить сгенерированный ключ:

namespace FbGeneratedKeys
{
    class Program
    {
        private static SolutionType solutionType = SolutionType.FbScriptFB2_5;

        static void Main(string[] args)
        {
            var connectionString = new FbConnectionStringBuilder
            {
                Database = @"D:\temp\generatedkey.fdb",
                ServerType = FbServerType.Default,
                UserID = "SYSDBA",
                Password = "masterkey",
            }.ToString();
            FbConnection.CreateDatabase(connectionString, pageSize: 8192, overwrite : true);

            using (FbConnection connection = new FbConnection(connectionString))
            using (FbCommand cmd = new FbCommand())
            {
                connection.Open();

                cmd.Connection = connection;
                switch (solutionType) {
                    case SolutionType.Firebird3:
                        Firebird3Example(cmd);
                        break;
                    case SolutionType.Firebird2_5:
                        Firebird2_5Example(cmd);
                        break;
                    case SolutionType.FbScriptFB2_5:
                        FbScriptFB2_5Example(cmd);
                        break;
                }

                cmd.CommandText = @"insert into withgeneratedid(column2) values (@column2) returning id";
                cmd.Parameters.AddWithValue("@column2", "some value");
                cmd.Parameters.Add(new FbParameter() { Direction = System.Data.ParameterDirection.Output });
                cmd.ExecuteNonQuery();

                Console.WriteLine("Id:" + cmd.Parameters[1].Value);
                Console.ReadLine();
            }
        }

        private static void Firebird3Example(FbCommand cmd)
        {
            // Firebird 3 identity column
            cmd.CommandText = @"create table withgeneratedid(
    id integer generated by default as identity primary key, 
    column2 varchar(100)
)";
            cmd.ExecuteNonQuery();
        }

        private static void Firebird2_5Example(FbCommand cmd)
        {
            // Firebird 2.5 and earlier normal primary key with trigger to generate key
            // Table
            cmd.CommandText = @"create table withgeneratedid(
    id integer primary key,
    column2 varchar(100)
)";
            cmd.ExecuteNonQuery();

            // Sequence
            cmd.CommandText = "create sequence seq_withgeneratedid";
            cmd.ExecuteNonQuery();

            // Trigger
            cmd.CommandText = @"create trigger withgeneratedid_bi before insert on withgeneratedid
as
begin
    if (new.id is null) then new.id = next value for seq_withgeneratedid;
end";
            cmd.ExecuteNonQuery();
        }

        private static void FbScriptFB2_5Example(FbCommand cmd)
        {
            string script = @"
create table withgeneratedid(
    id integer primary key,
    column2 varchar(100)
);

create sequence seq_withgeneratedid;

set term #;
create trigger withgeneratedid_bi before insert on withgeneratedid
as
begin
    if (new.id is null) then new.id = next value for seq_withgeneratedid;
end#
set term ;#
";
            FbScript fbScript = new FbScript(script);
            fbScript.Parse();
            FbBatchExecution exec = new FbBatchExecution(cmd.Connection);
            exec.AppendSqlStatements(fbScript);
            exec.Execute();
        }
    }

    enum SolutionType
    {
        Firebird3,
        Firebird2_5,
        FbScriptFB2_5
    }
}

Определения:

public const string stMAIN_TABLE_NAME = " OrgTable ";
public const string stDELETED_TABLE_NAME = "  BackupTable ";

public const string stFIELD_DEFINITIONS = " fstPriority VARCHAR(30)" + 
                                          ", fstInfo VARCHAR(100)" +
                                          ", fstDateCreated VARCHAR(30)" +
                                          ", fstDateModified VARCHAR(30)" +
                                          ", fiKeyID INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY ";
public const string stFIELD_NAMES = " fstPriority" + 
                                    ", fstInfo" + 
                                    ", fstDateCreated" + 
                                    ", fstDateModified" + 
                                    ", fiKeyID ";
public const string stFIELD_NAMES_NO_KEY_ID = " fstPriority" + 
                                              ", fstInfo" + 
                                              ", fstDateCreated" + 
                                              ", fstDateModified ";

Код:

//------------------------------
static private bool boCreateDatabaseTables(string stPathFilename, 
                                           string stUserID, 
                                           string stPassword, 
                                           List<string> liststTableNames, 
                                           List<string> liststFieldDefinitions) 
{
    bool boErrorFlag = false;
    int iTablesCount = liststTableNames.Count();
    string stOpenConn = new FbConnectionStringBuilder {
        Database = stPathFilename,
        UserID = stUserID,
        Password = stPassword,
        ServerType = FbServerType.Embedded,
        ClientLibrary = stCLIENT_LIBRARY
    }.ToString();
    using (FbConnection fbConn = new FbConnection(stOpenConn)) {
        try {
            fbConn.Open();

            FbTransaction fbTransaction = fbConn.BeginTransaction();
            for (int ii = 0; ii < iTablesCount; ii++) {
                string stSql = "CREATE TABLE " + liststTableNames[ii] + "( " + liststFieldDefinitions[ii] + ")";
                FbCommand fbCmd = new FbCommand(stSql, fbConn, fbTransaction);
                fbCmd.ExecuteNonQuery();
            }
            fbTransaction.Commit();
        }
        catch (Exception ex) {
            boErrorFlag = true;
            MessageBox.Show("catch ... GlobalsFirebird ... boCreateDatabaseTables ... " + ex.Message);
        }
    }
    return boErrorFlag;
}//boCreateDatabaseTables
//------------------------------
//------------------------------
static public bool boAddRow(string stPathFilename,
                            string stUserID,
                            string stPassword,
                            string stTableName,
                            string stFieldNamesNoKeyId,
                            List<string> liststFieldValuesNoKeyId) 
{
    bool boErrorFlag = false;
    string stOpenConn = new FbConnectionStringBuilder {
        Database = stPathFilename,
        UserID = stUserID,
        Password = stPassword,
        ServerType = FbServerType.Embedded,
        ClientLibrary = stCLIENT_LIBRARY
    }.ToString();

    using(FbConnection fbConn = new FbConnection(stOpenConn)) {
        fbConn.Open();
        try {
            string stValuesPlaceHolder = "@p0";
            for (int iii = 1; iii < liststFieldValuesNoKeyId.Count; iii++)
                stValuesPlaceHolder += ", @p" + (iii).ToString();
            FbTransaction fbTransaction = fbConn.BeginTransaction();
            string stCmd = "INSERT INTO " + stTableName + "(" + stFieldNamesNoKeyId + ") VALUES ( " + stValuesPlaceHolder + " ) RETURNING  fiKeyID ";
            FbCommand fbCmd = new FbCommand(stCmd, fbConn, fbTransaction);

            for (int iii = 0; iii < liststFieldValuesNoKeyId.Count; iii++) {
                string stPlaceHolder = "@p" + (iii).ToString();
                string stValue = liststFieldValuesNoKeyId[iii];
                fbCmd.Parameters.AddWithValue(stPlaceHolder, stValue);
            }
            fbCmd.Parameters.Add(new FbParameter() { Direction = System.Data.ParameterDirection.Output });
            fbCmd.ExecuteNonQuery();
            fbTransaction.Commit();
        }
        catch (Exception ex) {
            boErrorFlag = true;
            MessageBox.Show("catch ... GlobalsFirebird ... boAddRow ... " + ex.Message);
        }
    }
    return boErrorFlag;
}//boAddRow
//------------------------------        
Другие вопросы по тегам