РТЫ 04091: таблица [вздор] видоизменяется, триггер/функция не может видеть его

Если вы не возражаете против отключения режима автоматической сортировки заголовков столбцов DataGridView, вы можете отключить Column.SortMode , установив для него значение DataGridViewColumnSortMode.NotSortable .
Это будет препятствовать тому, чтобы Заголовок столбца был выделен, и сортировочный глиф не показывается.

Щелчок мыши не даст заметного эффекта; событие CellClick вызывается (с e.RowIndex = -1) как обычно.

foreach (DataGridViewColumn col in dataGridView1.Columns) {
    col.SortMode = DataGridViewColumnSortMode.NotSortable;
}

Если DataGridView связан с источником данных и для него установлено значение AutoGenerateColumns, повторите процедуру, если источник данных изменился. Вы также можете установить это свойство в интерфейсе конструктора DataGridView, если столбцы предварительно определены.

Для RowHeader, если необходимо, вы можете изменить DataGridView.SelectionMode на DataGridViewSelectionMode.CellSelect .

6
задан OMG Ponies 6 December 2010 в 16:44
поделиться

3 ответа

Я думаю, что не соглашаюсь с Вашим описанием того, что триггер пытается сделать. Это смотрит на меня как он, предназначен для осуществления этого бизнес-правила: Для данного значения t1_appnt_event только одна строка может иметь ненулевое значение t1_prnt_t1_pk за один раз. (Не имеет значения, если у них есть то же значение во втором столбце или нет.)

Интересно, это определяется для ОБНОВЛЕНИЯ t1_appnt_event, но не для другого столбца, таким образом, я думаю, что кто-то мог нарушить правило путем обновления второго столбца, если нет отдельный триггер для того столбца.

Мог бы быть способ, которым Вы могли создать функциональный индекс, который осуществляет это правило, таким образом, можно избавиться от триггера полностью. Я придумал один путь, но требуются некоторые предположения:

  • Таблица имеет числовой первичный ключ
  • Первичный ключ и t1_prnt_t1_pk являются оба всегда положительными числами

Если эти предположения верны, Вы могли бы создать функцию как это:

dev> create or replace function f( a number, b number ) return number deterministic as
  2  begin
  3    if a is null then return 0-b; else return a; end if;
  4  end;

и индекс как это:

CREATE UNIQUE INDEX my_index ON my_table
  ( t1_appnt_event, f( t1_prnt_t1_pk, primary_key_column) );

Таким образом, строки, где столбец PMNT является ПУСТЫМ, появились бы в индексе с инверсией первичного ключа как второе значение, таким образом, они никогда не будут конфликтовать друг с другом. Строки, где это не является ПУСТЫМ, использовали бы фактическое (положительное) значение столбца. Единственным путем Вы могли добраться, ограничительное нарушение состояло бы в том, если бы две строки имели те же ненулевые значения в обоих столбцах.

Это, возможно, чрезмерно "умно", но это могло бы помочь Вам обойти свою проблему.

Обновление от Paul Tomblin: Я пошел с обновлением исходной идеи, что igor вставляют комментарии:

 CREATE UNIQUE INDEX cappec_ccip_uniq_idx 
 ON tbl1 (t1_appnt_event, 
    CASE WHEN t1_prnt_t1_pk IS NOT NULL THEN 1 ELSE t1_pk END);
7
ответ дан 17 December 2019 в 00:16
поделиться

Я соглашаюсь с Dave, что желаемый результат, вероятно, может и должен быть достигнут с помощью встроенных ограничений, таких как уникальные индексы (или ограничения на уникальность данных).

Если действительно необходимо обойти видоизменяющуюся ошибку таблицы, обычный способ сделать это состоит в том, чтобы создать пакет, который содержит ограниченную по объему пакетом переменную, которая является таблицей чего-то, что может использоваться для идентификации измененных строк (я думаю, что ROWID возможен, иначе необходимо использовать PK, я в настоящее время не использую Oracle, таким образом, я не могу протестировать его). ДЛЯ КАЖДОГО триггера СТРОКИ затем заполняет эту переменную со всеми строками, которые изменяются оператором, и затем существует ПОСЛЕ каждого триггера оператора, который читает строки, и проверьте их.

Что-то как (синтаксис является, вероятно, неправильным, я не работал с Oracle в течение нескольких лет),

CREATE OR REPLACE PACKAGE trigger_pkg;
   PROCEDURE before_stmt_trigger;
   PROCEDURE for_each_row_trigger(row IN ROWID);
   PROCEDURE after_stmt_trigger;
END trigger_pkg;

CREATE OR REPLACE PACKAGE BODY trigger_pkg AS
   TYPE rowid_tbl IS TABLE OF(ROWID);
   modified_rows rowid_tbl;

   PROCEDURE before_stmt_trigger IS
   BEGIN
      modified_rows := rowid_tbl();
   END before_each_stmt_trigger;

   PROCEDURE for_each_row_trigger(row IN ROWID) IS
   BEGIN
      modified_rows(modified_rows.COUNT) = row;
   END for_each_row_trigger;

   PROCEDURE after_stmt_trigger IS
   BEGIN
      FOR i IN 1 .. modified_rows.COUNT LOOP
         SELECT ... INTO ... FROM the_table WHERE rowid = modified_rows(i);
         -- do whatever you want to
      END LOOP;
   END after_each_stmt_trigger;
END trigger_pkg;

CREATE OR REPLACE TRIGGER before_stmt_trigger BEFORE INSERT OR UPDATE ON mytable AS
BEGIN
   trigger_pkg.before_stmt_trigger;
END;

CREATE OR REPLACE TRIGGER after_stmt_trigger AFTER INSERT OR UPDATE ON mytable AS
BEGIN
   trigger_pkg.after_stmt_trigger;
END;

CREATE OR REPLACE TRIGGER for_each_row_trigger
BEFORE INSERT OR UPDATE ON mytable
WHEN (new.mycolumn IS NOT NULL) AS
BEGIN
   trigger_pkg.for_each_row_trigger(:new.rowid);
END;
0
ответ дан 17 December 2019 в 00:16
поделиться

С любым основанным на триггере (или основанный на коде приложения) решение необходимо вставить блокировку для предотвращения повреждения данных в пользовательской среде. Даже если бы Ваш триггер работал или был переписан для предотвращения видоизменяющейся проблемы таблицы, то он не предотвратил бы 2 пользователей от одновременного обновления t1_appnt_evnt_id к тому же значению на строках, где t1_appnt_evnt_id не является пустым: предположите, что нет currenly никаких строк, где t1_appnt_evnt_id=123 и t1_prnt_t1_pk не являются пустыми:

Session 1> update tbl1 
           set t1_appnt_evnt_id=123 
           where t1_prnt_t1_pk =456;
           /* OK, trigger sees count of 0 */

Session 2> update tbl1
           set t1_appnt_evnt_id=123
           where t1_prnt_t1_pk =789;
           /* OK, trigger sees count of 0 because 
              session 1 hasn't committed yet */

Session 1> commit;

Session 2> commit;

У Вас теперь есть поврежденная база данных!

Способ избежать этого (в триггере или коде приложения) состоял бы в том, чтобы заблокировать родительскую строку в таблице, на которую ссылается t1_appnt_evnt_id=123 прежде, чем выполнить проверку:

select appe_id 
into   v_app_id
from parent_table
where appe_id = :new.t1_appnt_evnt_id
for update;    

Теперь сессия 2 триггер должен ожидать сессии 1, чтобы фиксировать или откатывать, прежде чем это выполнит проверку.

Это было бы намного более простым и более безопасным реализовать индекс Dave Costa!

Наконец, я рад, что никто не предложил добавить ПРАГМУ AUTONOMOUS_TRANSACTION к Вашему триггеру: это часто предлагается на форумах и работах в так, как видоизменяющаяся проблема таблицы уходит - но она делает проблему целостности данных еще хуже! Поэтому просто не делайте...

0
ответ дан 17 December 2019 в 00:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: