Почему ExecuteNonQuery перехватывает исключение {ошибка проверки для столбца \ORGTABLE\.\FIKEYID\, значение \* null *\}

Почему ExecuteNonQuery перехватывает исключение {"ошибка проверки для столбца \"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, так как это ПЕРВИЧНЫЙ КЛЮЧ.


person ttom    schedule 23.03.2017    source источник
comment
Что такое DDL таблицы и какую версию Firebird вы используете. У вас есть триггер для генерации идентификатора для вас? В противном случае отсутствие включения значения для первичного ключа приведет к назначению null, что недопустимо, поэтому в этом случае исключение является правильным и ожидаемым. Кстати: я предполагаю, что вы имеете в виду, почему ExecuteNonQuery выдает исключение.   -  person Mark Rotteveel    schedule 23.03.2017
comment
Можете ли вы сделать вставку только с SQL? Если нет, я подозреваю, что проблема заключается в ключевом поле - на сервере SQL для него обычно устанавливается идентификатор, а начальное значение и значение приращения равны 1.   -  person CooncilWorker    schedule 23.03.2017
comment
Firebird ADO.NET ... FirebirdClient.5.8.0 ----- Вышеизложенное успешно использовалось для записи в Access.accdb и SQLite. Для Access.accdb первичный ключ был определен разработчиком Access.accdb... для SQLite первичный ключ был определен как INTEGER PRIMARY KEY. ----- Что является триггером для генерации идентификатора?   -  person ttom    schedule 23.03.2017
comment
Excel/Access (Microsoft Jet) не является базой данных SQL, хотя есть некоторый мост для использования частей SQL для извлечения данных из нее. SQLite имеет нестандартное поведение: каждая строка имеет выделенный столбец ROWID, независимо от того, хотите вы этого или нет - sqlite .org/autoinc.html – первичный ключ adn автоматически удаляется и сокращается для него. Но мне интересно, будет ли возможность резервного копирования/восстановления базы данных SQLite или одновременного подключения базы данных SQLite для нескольких разных компьютеров, будет ли этот столбец ROWID по-прежнему возможен?   -  person Arioch 'The    schedule 23.03.2017


Ответы (2)


Я не добавлял значение для fiKeyID, так как это ПЕРВИЧНЫЙ КЛЮЧ.

Итак, вы пытаетесь вставить первичный ключ NULL. Это не разрешено.

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

NULL никогда не допускаются в первичных ключах. Столбец может быть (частью) PK только в том случае, если он был определен как NOT NULL либо в определении столбца, либо в определении домена.

Затем вы можете попросить сервер автоматически сгенерировать идентификаторы. Есть несколько способов сделать это.

Например, Firebird 3 поставляется с типом столбца auto-inc. Что является синтаксическим сахаром по сравнению с инструментами, которые ранее явно использовались разработчиком базы данных.

Firebird 2 и предыдущие версии использовали ГЕНЕРАТОРЫ (также известные как SQL SEQUENCE) для достижения этой цели.

Вы должны сделать триггер BEFORE-INSERT (или BEFORE-INSERT-OR-UPDATE) для таблицы, который заполнит поле ID из генератора, если поле ID было NULL. 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. Потому что обычно после того, как вы вставили строку, вы должны знать ее идентификатор.

Если вам не нужен этот идентификатор строки новорожденных, вы можете пропустить остальные.

Но если вы хотите и вставить строку, и узнать ее идентификатор, то это сводится к другому выбору.

Низкотехнологичные библиотеки только для SQL заставят вас совершить двойную поездку:

  • SELECT GEN_ID(GEN_T1_ID, 1) FROM RDB$DATABASE или SELECT 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, и опять же, только вы можете сказать, какую из них вы используете. Несколько примеров из разных библиотек:

и так далее

person Arioch 'The    schedule 23.03.2017
comment
Я вижу на firebirdfaq.org/faq29 --- INSERT INTO t1(field1) VALUES('my материал') ВОЗВРАЩАЕТСЯ id; --- Я могу добавить RETURNING id, --- FbCommand fbCmd = new FbCommand(INSERT ... RETURNING ( + id + ), fbConn, fbTransaction); --- но я не уверен, что следует использовать для fbCmd.Parameters.AddWithValue --- Нужно ли добавлять stPlaceHolder = @p5 ... stValue = . - person ttom; 24.03.2017
comment
@ttom Я почти ничего не знаю о C #, думаю, вам нужно связаться с форумом DotNetProvider или поискать фрагменты кода C #, используя вставку-возврат. Я рассказываю вам, как это обычно делается в Firebird, но я не знаю, насколько хороши или плохи FB-библиотеки C#. - person Arioch 'The; 24.03.2017
comment
также попытайтесь узнать, как вы в C# вызываете такие операторы, как EXECUTE BLOCK или вызов Stored Procedure. Причина на уровне API FB INSERT-RETURNING классифицируется как оператор типа SP-вызова. Возможно, именно так они отображаются и в API DotNetProvider. Может быть нет - person Arioch 'The; 24.03.2017
comment
Также думаю, что я немного ошибся, и RETURNING идет без круглых скобок. firebirdsql.su/doku.php?id=returning firebirdsql.su/doku.php?id=update_or_insert исправляю мой ответ сейчас - person Arioch 'The; 24.03.2017

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

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
//------------------------------  
person ttom    schedule 30.03.2017
comment
Кредит на ответ принадлежит Марку Роттевелу... см. stackoverflow.com/questions/43020306/. - person ttom; 30.03.2017