использование триггеров для обновления значений - PullRequest
0 голосов
/ 16 мая 2011

Я пытаюсь повысить производительность задания SQL Server 2000.Вот сценарий:
Таблица A имеет макс.300 000 строк.Если я обновляю / удаляю сотую строку (на основе времени вставки), все строки, которые были добавлены после этой строки, должны обновить свои значения.Ряд №101, следует обновить его значение на основе строки №.100 и № строки102 должен обновить свое значение на основе обновленного значения строки № 101.например,

Старая таблица:

ID...........Value  
100..........220  
101..........(220/2) = 110  
102..........(110/2)=55  
......................

Строка № 100 обновлена ​​новым значением: 300.

Новая таблица

ID...........Value  
100..........300  
101..........(300/2) = 150  
102..........(150/2)=75  
......................  

Расчет фактических значений более сложный.формула для простоты.

Прямо сейчас определен триггер для операторов обновления / удаления.Когда строка обновляется или удаляется, триггер добавляет данные строки в таблицу журнала.Кроме того, после обновления / удаления в задании кода создается задание SQL, которое запускает хранимую процедуру, которая, наконец, выполняет итерацию по всем следующим строкам таблицы A и обновляет их значения.Процесс занимает ~ 10 дней для 300 000 строк.

Когда SP запускается, он обновляет значения следующих строк.Я думаю , что заставляет триггер запускаться снова для каждого обновления SP и добавлять эти строки также в таблицу журнала.Кроме того, задача должна быть выполнена на стороне БД по требованию заказчика.

Для решения проблемы:
Измените хранимую процедуру и вызовите ее непосредственно из триггера.Затем хранимая процедура сбрасывает триггер и обновляет значения следующих строк, а затем снова создает триггер.

  • Будет одновременно запущено несколько экземпляров программы.если другой пользователь изменяет строку во время выполнения SP, система не сработает, и у меня будут проблемы!Есть ли обходной путь для этого?
  • Что вы думаете об этом решении?Есть ли лучший способ добиться этого?

Спасибо.

Ответы [ 2 ]

1 голос
/ 17 мая 2011

Сначала о процессе обновления.Я понимаю, ваша процедура просто вызывает себя, когда дело доходит до обновления следующей строки.С 300К строк это, конечно, не будет очень быстрым, даже без регистрации (хотя, скорее всего, это займет гораздо меньше дней).Но что меня совершенно не устраивает, так это то, как можно обновлять более 32 строк таким образом, не достигая максимального уровня вложенности.Возможно, у меня неправильная последовательность действий.

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

UPDATE yourtable
SET @value = Value = CASE ID
                       WHEN @id THEN @value
                       ELSE @value / 2 /* basically, your formula */
                     END
WHERE ID >= @id
OPTION (MAXDOP 1);

Бит OPTION (MAXDOP 1) оператора ограничиваетстепень параллелизма для оператора до 1, таким образом, гарантируя, что строки обновляются последовательно, и каждое значение основывается на предыдущем, то есть на значении в строке с предыдущим значением идентификатора.Кроме того, для столбца ID следует сделать кластеризованный индекс, которым он обычно является по умолчанию, если он стал первичным ключом.

Другие функции процедуры обновления, т. Е. Удаление и повторное создание триггера, должнывероятно, будет заменено отключением и повторным включением it:

ALTER TABLE yourtable DISABLE TRIGGER yourtabletrigger

/* the update part */

ALTER TABLE yourtable ENABLE TRIGGER yourtabletrigger

Но тогда вы говорите, что триггер не должен быть на самом деле удален / отключен, потому что несколько пользователей могут обновить таблицув то же время.

Хорошо, тогда мы не касаемся триггера.

Вместо этого я бы предложил добавить в таблицу специальный столбец, о котором пользователи не должны знатьили, по крайней мере, не должно заботиться о многом и должно быть как-то сделано, чтобы никогда не трогать.Этот столбец должен обновляться только процессом «каскадного обновления».Проверяя, обновлялся ли этот столбец или нет, вы узнаете, следует ли вам вызывать процедуру обновления и ведения журнала.

Итак, в вашем триггере может быть что-то вроде этого:

IF NOT UPDATE(SpecialColumn) BEGIN
  /* assuming that without SpecialColumn only one row can be updated */
  SELECT TOP 1 @id = ID, @value = Value FROM inserted;
  EXEC UpdateProc @id, @value;
  EXEC LogProc ...;
END

В UpdateProc:

UPDATE yourtable
SET @value = Value = @value / 2,
    SpecialColumn = SpecialColumn /* basically, just anything, since it can
                                     only be updated by this procedure */
WHERE ID > @id
OPTION (MAXDOP 1);

Возможно, вы заметили, что оператор UPDATE на этот раз немного отличается.Я понимаю, что ваш триггер FOR UPDATE (= AFTER UPDATE), это означает, что строка @id уже будет обновлена ​​пользователем.Таким образом, процедура должна пропустить ее и начать со следующей строки, а выражение обновления теперь может быть просто формулой.

В заключение я хотел бы сказать, что мое тестовое обновление включало 299 995 из 300 000 строк моей таблицы.и заняло примерно 3 секунды на моей не очень быстрой системе.Без регистрации, конечно, но я думаю, что это должно дать вам основную картину того, как быстро это может быть.

1 голос
/ 16 мая 2011

Большая теоретическая проблема здесь.Это всегда крайне подозрительно при обновлении одной строки ТРЕБУЕТ обновления 299 900 других строк.Это предполагает глубокий недостаток в модели данных.Не то, чтобы это никогда не было уместно, просто это требуется гораздо реже, чем думают люди.Когда подобные вещи абсолютно необходимы, они обычно выполняются как пакетная операция.

Лучшее, на что вы можете надеяться, в какой-то чудесной ситуации - превратить эти 10 дней в 10 минут, но никогда даже 10 секунд,Я бы предложил подробно объяснить, ПОЧЕМУ это кажется необходимым, чтобы можно было изучить другой подход.

...