为什么 ExecuteNonQuery 捕获异常 {"validation error for column \"ORGTABLE\".\"FIKEYID\", value \"* null *\""}

Why the ExecuteNonQuery catch exception {"validation error for column \"ORGTABLE\".\"FIKEYID\", value \"* null *\""}

为什么 ExecuteNonQuery 捕获异常 {"validation error for column \"ORGTABLE\".\"FIKEYID\",值 \"* null *\""}

string stValuesPlaceHolder = "@p0";
for (int iii = 1; iii < liststFieldValuesNoKeyId.Count; iii++)
    stValuesPlaceHolder += ", @p" + (iii).ToString();

FbTransaction fbTransaction = fbConn.BeginTransaction();
FbCommand fbCmd = new FbCommand("INSERT INTO " + stTableName + "(" + stFieldNamesNoKeyId + ") VALUES ( " + stValuesPlaceHolder + " )", 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.ExecuteNonQuery();
fbTransaction.Commit();

stTableName 是 OrgTable。

字段名称是:

fstPriority, fstInfo, fstDateCreated, fstDateModified, fiKeyID.

字段定义为:

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

这段代码中:

stFieldNamesNoKeyId = "fstPriority, fstInfo, fstDateCreated, fstDateModified".

stValuesPlaceHolder = "@p0, @p1, @p2, @p3"

四个 fbCmd.Parameters.AddWithValue:

stPlaceHolder = "@p0" ... stValue = "1st value";

stPlaceHolder = "@p1" ... stValue = "2nd value";

stPlaceHolder = "@p2" ... stValue = "3rd value";

stPlaceHolder = "@p3" ... stValue = "4th value";

我没有为 fiKeyID 添加值,因为它是主键。

I did not add a value for fiKeyID as it as the PRIMARY KEY.

因此您尝试插入一个 NULL 主键。这是不允许的。

http://www.firebirdsql.org/manual/nullguide-keys.html

NULLs are never allowed in primary keys. A column can only be (part of) a PK if it has been defined as NOT NULL, either in the column definition or in a domain definition.

然后,您可能需要请求服务器自动生成 ID。有几种方法可以做到这一点。

例如,Firebird 3 带有 auto-inc 列类型。 这是数据库开发人员以前明确使用的工具的语法糖。

Firebird 2 和之前的版本使用 GENERATORS(又名 SQL SEQUENCE)来实现它。

您必须在 table 上创建一个 BEFORE-INSERT(或 BEFORE-INSERT-OR-UPDATE)触发器,如果​​ ID 字段为 NULL,它将填充来自生成器的 ID 字段。 http://www.firebirdfaq.org/faq29/

CREATE GENERATOR gen_t1_id;
SET GENERATOR gen_t1_id TO 0;
set term !! ;
 CREATE TRIGGER T1_BI FOR T1
 ACTIVE BEFORE INSERT POSITION 0
 AS
 BEGIN
 if (NEW.ID is NULL) then NEW.ID = GEN_ID(GEN_T1_ID, 1);
 END!!
set term ; !!

归结为您的 SQL 访问库。 因为通常在您插入该行之后 - 您必须知道它的 ID。

如果你不关心新生行的那个ID,你可以跳过剩下的。

但是如果您既想插入该行又想知道它的 ID,那么它归结为另一种选择。

低技术含量的 SQL- 只有图书馆会迫使您进行双程旅行:

  • SELECT GEN_ID(GEN_T1_ID, 1) FROM RDB$DATABASESELECT NEXT VALUE FOR GEN_T1_ID FROM RDB$DATABASE 会为您保留一个免费令牌,然后您会明确地将您的 ID PK 列分配给该值并将其与数据列一起插入,绕过触发器。

  • 或者使用高级 SQL 库,您可以要求 Firebird 自动计算值并将其报告给您:INSERT INTO tablename(data1,data2,dataq3) VALUES (1,2,3) RETURNING id。参见 https://en.wikipedia.org/wiki/Insert_(SQL)#Retrieving_the_key

是否需要学习插入的 ID,以及您的 SQL 库是否支持 INSERT-RETURNING 命令 - 由您决定。

然而,当我进行 Google 搜索(它是 www.google.com )时,它带有许多关于 C# Firebird Insert Returniung 的链接,用于许多不同的 C# SQL 库,同样只有你可以告诉你使用哪一个。来自不同库的几个例子:

等等

定义:

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_NO_KEY_ID = " fstPriority" + 
                                              ", fstInfo" + 
                                              ", fstDateCreated" + 
                                              ", fstDateModified ";

public const string stFIELD_NAMES_KEY_ID = " fiKeyID ";

public const string stFIELD_NAMES = stFIELD_NAMES_NO_KEY_ID + ", " + stFIELD_NAMES_KEY_ID;

代码:

//------------------------------
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,
                            string stFieldNamesKeyId,
                            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  " + stFieldNamesKeyId;
      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
//------------------------------