|
plpgsql и триггеры
|
|||
---|---|---|---|
#18+
Есть таблицы: CREATE TABLE tr_email ( id serial, category_id int not null, address varchar(255) not null, is_active bool not null default true, primary key(id) ); CREATE TABLE tr_category_stats ( id serial, category_id int not null, total int4 not null default 0, removed int4 not null default 0, updated timestamp not null default now(), primary key(id) ); и триггер: CREATE TRIGGER tr_update_stats AFTER INSERT OR UPDATE OR DELETE ON tr_email FOR EACH STATEMENT EXECUTE PROCEDURE update_stats(); В функции update_stats() нужно обновить информацию в таблице tr_category_stats. Проблема в том что триггер вызывается FOR EACH STATEMENT и доступа к данным (NEW и OLD) НЕТ. И сделать такое: SELECT INTO total_cnt count(*) FROM tr_email WHERE category_id = NEW.category_id; SELECT INTO removed_cnt count(*) FROM tr_email WHERE category_id = NEW.category_id AND is_active = ''false''; UPDATE tr_email_category_stats SET total = total_cnt, removed = removed_cnt, updated = now() WHERE category_id = NEW.category_id; RETURN NEW; НЕЛЬЗЯ. category_id один для всего обновляемого набора. Нельзя ли его как-нибудь получить в триггере? Возможно есть другие варианты решения проблемы? ... |
|||
:
Нравится:
Не нравится:
|
|||
21.04.2004, 07:05 |
|
plpgsql и триггеры
|
|||
---|---|---|---|
#18+
про category_id Afaik нельзя. А что мешает вызывать for each row - актуализация второй таблицы будет "мгновенной" и точной. к тому же оцените как часто будет этот триггер срабатывать и на сколько записей сразу и прикиньте -а стоит ли each statement неудобств отсутствия кортежей NEW OLD немного оффтопика А с какой версии работает конструкция выделенная болдом, может я что-то пропустил ? CREATE TRIGGER tr_update_stats AFTER INSERT OR UPDATE OR DELETE ON tr_email FOR EACH STATEMENT EXECUTE PROCEDURE update_stats(); ... |
|||
:
Нравится:
Не нравится:
|
|||
22.04.2004, 08:15 |
|
plpgsql и триггеры
|
|||
---|---|---|---|
#18+
Дело в том что функция, которая висит на триггере (update_stats()), выполняет 2 раза count() для таблицы, объём которой будет порядка 100 млн. записей. Так вот мне кажется что если триггер повесить на FOR EACH ROW, то время операции значительно увеличится. (Насколько я знаю count() при таких объёмах работает не очень быстро). А на счёт конструкции: это версия 7.4.2, просто я раньше с PostgreSQL не работал - поэтому не совсем понял ваше сомнение. ... |
|||
:
Нравится:
Не нравится:
|
|||
22.04.2004, 09:07 |
|
plpgsql и триггеры
|
|||
---|---|---|---|
#18+
наверное можно так: на ROW закопляйте NOW.id куда-нить (подошли бы временные таблички, но тут с ними некий гемор), а на STATEMENT вшурупливайте их (id) по месту. Т.е. в порядной обработке не вызывайте COUNT() и т.п. тормоза, но закопляйте необходимые вам в последующем данные. ( Temporary tables are automatically dropped at the end of a session, or optionally at the end of the current transaction (see ON COMMIT)). Надеюсь, доны застарелые слонолюбы дадут вам более полезные советы. /я как-то подумывал создавать свои #inserted и #deleted таким макаром, но тогда все, что касается этих "внутрисеансных табличек" должно выглядеть как динамический сыккуль - "EXECUTE \'...\'", не уверен, что не будет межсеансных проблем, хотя хелп по TEMPORARY/TEMP позволяет на это надеяться - но пока обошелся тем, что попроще (порядной обработкой) - ибо данных пока немного/)/ PS. А зачем вы 2 раза считаете один и тот же count()? может таки достаточно посчитать 1 раз :). ... |
|||
:
Нравится:
Не нравится:
|
|||
22.04.2004, 11:54 |
|
plpgsql и триггеры
|
|||
---|---|---|---|
#18+
assaPS. А зачем вы 2 раза считаете один и тот же count()? может таки достаточно посчитать 1 раз :). 1-ый раз: количество всех записей 2-ой раз: количество неактивных А нельзя ли как-нибудь в триггере на ROW запоминать category_id не во временную табличку, а в какую-нибудь переменную. А потом её использовать в триггере на STATMENT? Этот id`ик ведь будет один для всего набора записей. Если можно, то подскажите как это сделать. Я чего-то ума не приложу. ... |
|||
:
Нравится:
Не нравится:
|
|||
23.04.2004, 06:44 |
|
plpgsql и триггеры
|
|||
---|---|---|---|
#18+
ok, заметил AND is_active = ''false''; (хотя не понял, почему "false" а не false, ну да ладно). по поводу переменной ничего не скажу. Если имеются в виду переменные pgsql, то их область видимости - процедура, в которой они описаны. Т.ч. они вам не подходят (у вас одна процедура - ф-я триггера, вызываемая к примеру before ... для каждого ряда, а вторая - ф-я триггера, вызываемая after ... - для стейтмента). Т.е. вам придется передаваться через переменные описанные в каких-то других языках (и возвращаемые функциями написанными на этих других языках). Если же вы хотите остаться в пределах SQL/pgsql, придется передаваться через объекты б.д., т.е., насколько я понимаю, через таблицы или счетчики. При этом, поскольку вряд ли вам нужны проблемы с сетевой или многосеансной работой, вам нужны объекты, существующие в отдельном сеансе и невидимые из других (могущие иметь одноименные независимые "копии" в других сеансах). А это Temporary таблицы/счетчики. Возможно я не вполне прав, и слонолюбы меня поправят. Жаль, что они не спешат поделиться опробованными рецептами. И вопросы по реализации триггеров на стейтмент как правило остаются ими не замеченными. (Видимо они не пишут частично денормализованных хранилищ). ЗЫ: кстати, что такое NEW в AFTER ... DELETE ? (там ведь будет OLD) ... |
|||
:
Нравится:
Не нравится:
|
|||
23.04.2004, 11:04 |
|
plpgsql и триггеры
|
|||
---|---|---|---|
#18+
assaкстати, что такое NEW в AFTER ... DELETE? (там ведь будет OLD) Да, там действительно будет OLD. Просто в этой процедуре обрабатывались все 3 случая (INSERT, UPDATE, DELETE), а я вставил сюда лишь для INSERT. Спасибо за совет!!! хоть кто-то что-то сказал по существу................ ... |
|||
:
Нравится:
Не нравится:
|
|||
23.04.2004, 13:42 |
|
plpgsql и триггеры
|
|||
---|---|---|---|
#18+
По-моему, здесь очень хорошо подойдут триггеры FOR EACH ROW. aev (Насколько я знаю count() при таких объёмах работает не очень быстро). Никаких каунтов делать не нужно. В триггере FOR EACH ROW: Если запись вставляется, то update tr_category_stats set total = total +1; (или вставка новой записи с total=1) Если запись удаляется, то смотрим на is_active и уменьшаем на единицу соотв. количество. Если запись обновляется, то смотрим на изменение флага is_active и выполняем уменьшение/увеличение total/removed. Если в таблице tr_email предполагаются массовые удаления/обновления, то можно (как пример) добавить дополнительную таблицу, в которой будет изменяться флаг перед такими массовыми операциями. А в триггере проверять этот флаг и если установлен, то ничего не делать. И массовые удаления/обновления выполнять отдельной процедурой, без всяких триггеров. То есть, схема процедуры примерно следующая: 1. Блокируем таблицу tr_email 2. Выставляем флаг, что начали массовую обработку. 3. Копируем во временную таблицу рабочую выборку из category_id и и количества. (То есть, если операция update tr_email set is_active=false where category_id in (2, 3, 6) and is_active=true, то выбираем предикат и вставляем во временную таблицу такое: select category_id, count(*) from tr_email where category_id in (2, 3, 6) and is_active=true group by category_id. Если некоторые записи из одной категории могут быть is_active=true, а другие is_active=false, то все усложняется...) 4. Делаем чего хотели (удаляем или обновляем). 5. Для каждой строки из временной таблицы делаем правильный update tr_category_stats. 6. Сбрасываем флаг. Теперь со спокойной душой делаем коммит, и все продолжается как обычно. Жаль, триггер нельзя задизэйблить, или я еще не знаю чего-то... В общем-то, можно и на Statement-триггерах делать, наверно, но надо все еще раз хорошо оценить. Для обеспечения согласованности в этом случае, наверно, придется использовать уровень изоляции serializable. ... |
|||
:
Нравится:
Не нравится:
|
|||
30.04.2004, 12:18 |
|
plpgsql и триггеры
|
|||
---|---|---|---|
#18+
2 Заглянул а теперь докажите, что 100 апдейтов (пусть без подсчета каунта), лучше 1 апдейта с подсчетом :0). Или что изгиляться с недотыкомками полезней, чем пользоваться готовыми продуманными решениями ... |
|||
:
Нравится:
Не нравится:
|
|||
30.04.2004, 14:48 |
|
plpgsql и триггеры
|
|||
---|---|---|---|
#18+
немного оффтопа - задисэйблить триггера можно но либо все (reltriggers =0 , умрут также все констрейнты и ключи, у нас таким образом накосячили в базе и нарушили целостность, когда записей нету а foreign key с рестрикт делитом лежит и не чешется... =( ) либо по имени таблицы находить все триггера в системных и перевешивать на другой оид\таблицу.. ... |
|||
:
Нравится:
Не нравится:
|
|||
05.05.2004, 23:13 |
|
plpgsql и триггеры
|
|||
---|---|---|---|
#18+
ок. А во время подъема дампа чо происходит? (я сам не поднимаю, но видел пару раз, что триггера не отрабатывают (в процессе заливки). оно может и к лучшему - а то неподьемно ж на каждую запись пересчитывать итоги, но пришлось писать процедурку для заполнения промежуточных итогов и прочей лабуды после таких операций. Такоже апдейтить счетчики под максимум после заливки). Или это от параметров pg_restore зависит? ... |
|||
:
Нравится:
Не нравится:
|
|||
06.05.2004, 12:01 |
|
plpgsql и триггеры
|
|||
---|---|---|---|
#18+
2 centur: Это путь для смелых и продвинутых людей. ;-) Я в таблицах словаря предпочитаю не ковыряться, боюсь. ;) 2 аха: Так кто ж спорит, можит и не лучше, а может даже намного хуже. Может, каунт у вас будет почти моментальным. ;) Просто надо протестировать на конкурирующих транзакциях, чтобы убедиться, что в результате итоговые значения будут корректными. А можно проверить разные подходы и сравнить. З.Ы. аха Или что изгиляться с недотыкомками полезней, чем пользоваться готовыми продуманными решениями Эту фразу я понять не могу. ;-) О чем это вы? ... |
|||
:
Нравится:
Не нравится:
|
|||
06.05.2004, 12:16 |
|
plpgsql и триггеры
|
|||
---|---|---|---|
#18+
ну у меня ручки шаловливые - лазим всюду 2офтоп - ну правильно, это и делается, триггера и ключи отключаются для увеличения скорости ... |
|||
:
Нравится:
Не нравится:
|
|||
06.05.2004, 22:37 |
|
plpgsql и триггеры
|
|||
---|---|---|---|
#18+
2 aev & аха: (вдогонку) Любые предложенные варианты нужно тестировать и сравнивать, только в сравнении (на реальных данных и в реальных условиях) можно определить, какой подход может оказаться лучше. Доказывать ничего не собираюсь, зачем оно мне? Удачи. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.05.2004, 19:06 |
|
|
start [/forum/topic.php?fid=53&msg=32493038&tid=2007875]: |
0ms |
get settings: |
12ms |
get forum list: |
13ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
46ms |
get topic data: |
12ms |
get forum data: |
3ms |
get page messages: |
55ms |
get tp. blocked users: |
1ms |
others: | 332ms |
total: | 480ms |
0 / 0 |