У меня была очень странная проблема, связанная с производительностью, с MERGE
в Oracle 10. В нескольких словах у меня есть хранимая процедура, которая вычисляет и сохраняет рейтинг пользователя на основе ее активности в системе и содержит только один оператор MERGE
:
MERGE INTO user_ranks target USING
([complex query that returns rank_id and user_id])src ON
(src.user_id = target.user_id)
WHEN MATCHED THEN UPDATE SET target.rank_id = src.rank_id
WHEN NOT MATCHED THEN INSERT (target.user_id, target.rank_id)
VALUES (src.user_id, src.rank_id);
// user_ranks table structure:
CREATE TABLE user_ranks (user_id INT NOT NULL
PRIMARY KEY USING INDEX (CREATE UNIQUE INDEX UQ_uid_uranks ON user_ranks(user_id)),
rank_id INT NOT NULL,
CONSTRAINT FK_uid_uranks FOREIGN KEY (user_id) REFERENCES users(id),
CONSTRAINT FK_rid_uranks FOREIGN KEY(rank_id) REFERENCES ranks(id));
// no index on rank_id - intentionally, ranks table is a lookup with
// a very few records and no delete/update allowed
Подзапрос, который используется в качестве источника для MERGE
, возвращает не более 1 записи (user_id передается в качестве параметра в процедуру).Это довольно дорого, но время выполнения приемлемо (1-1,2 сек).Проблема в том, что MERGE
время выполнения увеличивается более чем на 40 секунд, и я понятия не имею, почему.Я попытался использовать подсказку LEADING
безуспешно.Но если я разделю оператор на две части, сначала одну - запустить SELECT
подзапрос и сохранить результат (rank_id) в переменную, а затем объединить - MERGE ... USING (SELECT user_id, rank_id FROM DUAL)src ...
все работает просто отлично.Из того, что я прочитал, есть известные проблемы с Oracle MERGE
, но они в основном связаны с триггерами (в моем случае триггеров нет).Это также говорит о том, что MERGE
работает медленнее, чем комбинация INSERT
и UPDATE
, но я считаю, что "нормальная" разница составляет около 5-10%, а не в 30 раз ...
Япытаясь понять, что я сделал не так ... Спасибо за ваши предложения.
Обновление План выполнения довольно длинный, чтобы опубликовать его здесь, короче: стоимость подзапроса сама по себе составляет 12737, с merge
- 76305. Вывод статистики для merge
:
> Статистика
108 recursive calls
4 db block gets
45630447 consistent gets
24905 physical reads
0 redo size
620 bytes sent via SQL*Net to client
1183 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
только подзапрос:
> Статистика
8 recursive calls
0 db block gets
34 consistent gets
0 physical reads
0 redo size
558 bytes sent via SQL*Net to client
234 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)