INSERT INTO Firebird 如何使用主键的自动增量?

How do INSERT INTO Firebird, with autoincrement for the primary key?

如何 INSERT INTO Firebird,主键自增?

对于 table 个字段,我有:

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 Studio 2013.

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

set term !! ;
CREATE TRIGGER ...

Visual Studio 编译器无法识别。

重要的是 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 ;#
    

当您将值插入 table 并希望生成一个键时,您应该在列列表中包含 id 列。包括 id 列允许您覆盖键值,但这可能会导致将来的插入生成重复的键!。如果确实包含 id 列,则在 Firebird 3 示例中不会生成任何键,在 Firebird 2.5 示例中,如果该列的值为 null,则会生成一个键,否则它将采用提供的值.

在 ADO.net 中,您通常需要单独执行语句(而不是使用 set term)。或者,您可以使用 FbScript 来解析 DDL 脚本并执行解析语句。请注意 FbScript 确实支持(甚至需要)set term.

要使用 Firebird ADO.net 提供程序执行此操作,您可以执行类似于下面示例的操作。我已经包含了三个用于创建 table、Firebird3Firebird2_5FbScriptFB2_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
//------------------------------