В принципе, PL/SQL Developer использует тот же DBMS_PROFILER (кстати, есть еще иерархический профайлер ).
Для большей автоматизации иногда удобнее пользоваться своими запросами к этим же таблицам. Вот рыба для них:
select r.runid
,r.related_run
,r.run_owner
,r.run_date
,r.run_comment
,r.run_total_time
,r.run_system_info
,r.run_comment1
,u.unit_number
,u.unit_type
,u.unit_owner
,u.unit_name
,d.line#
,s.TEXT
,u.total_time
,d.total_occur
,d.total_time
,d.min_time
,d.max_time
from plsql_profiler_runs r
join plsql_profiler_data d
on r.runid = d.runid
join plsql_profiler_units u
on d.runid = u.runid
and d.unit_number = u.unit_number
left join all_source s
on u.unit_owner = s.OWNER
and u.unit_name = s.NAME
and u.unit_type = s.TYPE
and d.line# = s.LINE
where 1=1
-- and r.run_owner = user -- запуски от текущей юзера
-- and r.runid = (select max(runid) from plsql_profiler_runs where run_owner=user) -- последний запуск
order by r.runid,r.related_run,u.unit_number,d.line#;
Оба этих «инструмента» не подходят ни для отладки, ни для логгирования.
Это будет дикий overkill — логгировать инструментами, рассчитанными на синхронную/полусинхронную работу. Особенно, для каких-нибудь bulk-операций да еще и параллельных, да с высокой скоростью обработки — тут же упретесь в ограничения. Да еще и не дает возможности «заглянуть в прошлое».
Странный выбор… Это больше похоже на логгирование, а не отладку. Для дебага есть как раз plsql_debug, позволяющий устанавливать breakpoint'ы, получать значения переменных и т.д. Кстати, PL/SQL Developer умеет с ним работать, но только для сессий запущенных в нем самом.
А для логгирования лучше использовать что-то более гибкое и стандартное, например, всяческие аналоги логгеров из Java: github.com/OraOpenSource/Logger log4plsql.sourceforge.net
mkrupenin, подозрительно хорошие у вас результаты для noinmemory. Можно AWR и этого теста? А в идеале ashdump обоих тестов. Я прямо сейчас решил повторить ваш тест с помощью Benchmark factory (15-и дневная trial версия), с вашей же нагрузкой, так у меня noinmemory адско затыкается на direct path reads, и inmemory в несколько раз быстрее.
А отчет AWR есть? У меня сильное подозрение, что у вас слишком много ушло тупо ожидая процессора (CPU queue length смотрели?), т.к. ноут у вас 4-х ядерный, а нагрузку гнали в 8 пользователей, и помимо оракла еще и сам бенчмарк работал, да и еще что-нибудь, наверное. Попробуйте в 2-3 пользователя
1. Как раз описанная вами задача прекрасно решается с fast refresh on commit. Несмотря на всю брутальную кайтовскую категоричность, тут он прав на все 100% — dml триггеры все-таки все-таки это зло, и применяются они только из компромисса, когда уже «слишком дорого и долго» переделывать изначальные архитектурные ошибки.
2. Кроме того, вы же, наверное, понимаете, что таким триггером как у вас описан вы прямо таки нарываетесь на блокировки? Что если несколько сессий попробуют добавлять строки по одному счету?
3. Триггеры реально очень сильно ухудшают производительность и к тому же заставляют сервер генерить больше реду. Сравните свое приведенное решение и нормальное решение где одна процедура будет и обновлять остаток и вставлять записи в таблицу на паре десятков конкурентных процессов в несколько тысяч вставок
4. И, кстати, если триггер написан так что повторный его вызов изменяет его логику, то проблему с рестартами решить в принципе невозможно. Кроме того, не забывайте про нюансы с вызовом тригера при merge.
5. Больше всего пугает ваше доверие к таким данным… Кто и насколько часто потом перепроверяет ваши остатки? А вдруг вы там с рестартами по 3 раза некоторые операции снижения баланса понапроводили?
select
concat(
ifnull(group_concat(if(id-p=1,'',concat(p+1,if(id-p=2,'',concat('-',id-1)),',')) separator ''),'')
,max(id)+1,'...')
s
from (
select @p p
,@p := id id
from (
select 0 id, @rn:=0 rn from dual
union all
select id, @rn:=@rn+1 from test
) v1
) v2
Если так дотошно не показывать-комментировать, то получится очень кратко:
select listagg(id1||decode(id2
,id1 ,null
,null,'...'
,'-'||id2)
,',')
within group(order by id1)s
from (select max(id)+1 id1
,lead(min(id)) over(order by min(id)) id2
from (select 0 id, 0 rn from dual
union all
select id,row_number()over(order by id) rn from test)
group by id - rn)
В Oracle все это можно сделать за один скан.
Объединение диапазонов вообще древняя и широкоизвестная задача и делается кучей разных способов, например так:
select
listagg(decode(id1,id2,to_char(id1),to_char(id1)||'-'||to_char(id2)),',')
within group(order by id1)s -- просто форматирование
from (
select min(id) id1,max(id) id2 -- это у нас и есть схлопнутые диапазоны, т.е. типа (1,3); (4,4); (7,10)
from (select id,row_number()over(order by id) rn
from test)
group by id - rn -- группируем по дельте от ряда
)
Соответственно, чтобы найти пропущенные/недостающие, достаточно внести небольшое изменение — после «схлопывания» диапазонов группировкой, вывести диапазоны между ними, используя lead или lag и +-1:
select
listagg(decode(id2
,id1 ,to_char(id1)
,null,to_char(id1)||'...'
,to_char(id1)||'-'||to_char(id2)
)
,',')
within group(order by id1)s -- просто требуемое форматирование
from (
select
id2+1 id1 -- конец предыдущего + 1
,lead(id1) over(order by id1)-1 id2 --начало следующего - 1
from (
select min(id) id1,max(id) id2 -- это у нас и есть схлопнутые диапазоны, т.е. типа (1,3); (4,4); (7,10)
from (select 0 id, 0 rn from dual -- добавляем "начало"
union all
select id,row_number()over(order by id) rn
from test)
group by id - rn
)
)
Обеспечивать полный ACID в коде промежуточного слоя весьма тяжело и затратно, особенно в больших многопользовательских распределенных системах, так не лучше ли делать то, что умеет RDBMS быстро и эффективно прямо там?
Ну во-первых подход древний(кажется еще Кайт его описывал и уже тогда предлагал вариант получше — с mview log+fast refresh) и с кучей недостатков, например, в сложности зависимостей, громоздкости кода, которая провоцирует на трудноуловимые ошибки, ужасная производительность, но даже в этом древнем подходе такую тяжелую жесть обычно не создают. Учтите, что вы таким подходом фактически превращаете систему в однопользовательскую — два пользователя не смогут одновременно поменять хоть что-то у двух разных сотрудников в разных департаментах, т.к. их коммиты будут ждать друг друга из-за блокировок на MVIEW, да и кол-во генерируемого реду будет просто зашкаливать при мельчайших изменениях… И кстати, подход Кайта в вашем примере будет намного проще — просто fast refresh mview c count(*) по клеркам в разрезе департаментов с check constraint (cnt<=2).
Во-вторых, обычно в таких случаях предпочитают не такую размазанную и тяжелую логику с кучей слабых мест, а уменьшение точек входа — в идеале одной процедуры, которая вносит изменения и вызывает процедуры проверок бизнес-логики. Имхо главные правила — блокируй как можно меньше, не создавай бутылочных горлышек, и конечно принцип KISS/бритва Оккама ...
Для большей автоматизации иногда удобнее пользоваться своими запросами к этим же таблицам. Вот рыба для них:
www.oracle.com/technetwork/database/availability/racdbawareapplications-1933522.pdf
Это будет дикий overkill — логгировать инструментами, рассчитанными на синхронную/полусинхронную работу. Особенно, для каких-нибудь bulk-операций да еще и параллельных, да с высокой скоростью обработки — тут же упретесь в ограничения. Да еще и не дает возможности «заглянуть в прошлое».
Ошибся имел ввиду DBMS_debug
А для логгирования лучше использовать что-то более гибкое и стандартное, например, всяческие аналоги логгеров из Java:
github.com/OraOpenSource/Logger
log4plsql.sourceforge.net
2. Кроме того, вы же, наверное, понимаете, что таким триггером как у вас описан вы прямо таки нарываетесь на блокировки? Что если несколько сессий попробуют добавлять строки по одному счету?
3. Триггеры реально очень сильно ухудшают производительность и к тому же заставляют сервер генерить больше реду. Сравните свое приведенное решение и нормальное решение где одна процедура будет и обновлять остаток и вставлять записи в таблицу на паре десятков конкурентных процессов в несколько тысяч вставок
4. И, кстати, если триггер написан так что повторный его вызов изменяет его логику, то проблему с рестартами решить в принципе невозможно. Кроме того, не забывайте про нюансы с вызовом тригера при merge.
5. Больше всего пугает ваше доверие к таким данным… Кто и насколько часто потом перепроверяет ваши остатки? А вдруг вы там с рестартами по 3 раза некоторые операции снижения баланса понапроводили?
но я не спец по mysql
Объединение диапазонов вообще древняя и широкоизвестная задача и делается кучей разных способов, например так:
Соответственно, чтобы найти пропущенные/недостающие, достаточно внести небольшое изменение — после «схлопывания» диапазонов группировкой, вывести диапазоны между ними, используя lead или lag и +-1:
Во-вторых, обычно в таких случаях предпочитают не такую размазанную и тяжелую логику с кучей слабых мест, а уменьшение точек входа — в идеале одной процедуры, которая вносит изменения и вызывает процедуры проверок бизнес-логики. Имхо главные правила — блокируй как можно меньше, не создавай бутылочных горлышек, и конечно принцип KISS/бритва Оккама ...