Мне нужно оптимизировать процедуру в Oracle SQL, в основном с помощью индексов. Это заявление:
CREATE OR REPLACE PROCEDURE DEL_OBS(cuantos number) IS
begin
FOR I IN (SELECT * FROM (SELECT * FROM observations ORDER BY DBMS_RANDOM.VALUE)WHERE ROWNUM<=cuantos)
LOOP
DELETE FROM OBSERVATIONS WHERE nplate=i.nplate AND odatetime=i.odatetime;
END LOOP;
end del_obs;
Мой план состоял в том, чтобы создать индекс, связанный с Rownum, поскольку он, по-видимому, используется для удаления. Но не знаю, будет ли оно достойным. Проблема с этой процедурой в том, что ее случайность вызывает много последовательный получает. Кто-нибудь может мне с этим помочь?? Спасибо :)
Примечание: я не могу изменить код, только потом внесу улучшения.
@APC это не мой код, я просто хочу улучшить его выполнение
Оптимизация означает более эффективное получение того же результата. IP трудно сделать это, когда мы не понимаем, что должен делать исходный код.
@APC У меня есть таблица, и я удаляю случайные N строк. Вот что делает код. Как сделать удаление более эффективным?
Если вы не знаете, чего пытаетесь достичь, мы не сможем прокомментировать, как сделать реализацию более эффективной.
Код по своей сути медленный. Если вы не можете его переписать, нет никакого способа сделать это быстрее (учитывая, что путь доступа является первичным ключом). Итак, вам нужно вернуться к человеку, который поставил вам эту задачу, и объяснить, что невозможно улучшить производительность кода, не переписав его. Базы данных - это двигатели, больше похожие на механические устройства, а не на мешок с волшебными зернами. Вот что делает их красивыми (а иногда и разочаровывающими).
@APC спасибо за совет
@symcbean Я знаю, что мне нужно удалить из таблицы N разных строк. И, учитывая приведенный выше код, попробуйте улучшить его производительность с помощью внешних инструментов.
Насколько мне известно, этот запрос ничем не поможет, если вы не можете изменить код. При выполнении ORDER BY DBMS_RANDOM.VALUE
выполняется сканирование таблицы, и никакой индекс не может улучшить производительность. Я бы отказался от этого задания, это невозможно. Это что-то вроде «переделать мою кухню, но вы не можете ничего убрать или добавить, или отключить воду, или сделать пыль». :-)
Единственные другие варианты: (1) УДАЛИТЬ сначала большую часть данных, чтобы оставшийся набор данных был меньше и случайная сортировка будет быстрее, (2) добавить намного больше ОЗУ на сервер и увеличить пул буферов, чтобы удерживать всю таблицу, независимо от ее размера, (3) перейти на более мощный сервер с большей производительностью ЦП и ввода-вывода.
Вы можете сделать это с помощью одного оператора delete
:
delete from observations o
where (o.nplate, o.odatetime) in (select nplace, odatetime
from (select o2.nplate, o2.odatetime
from observations o2
order by DBMS_RANDOM.VALUE
) o2
where rownum <= v_cuantos
);
Часто это быстрее, чем выполнение нескольких запросов для каждой удаляемой строки.
Спасибо за подсказку, но я не могу изменить запрос, это заданный код, который нам не разрешено трогать. Мы можем только добавить внешние улучшения, с индексами или изменить PCTFREE и PCTUSED ...
Что вам определенно нужно, так это индекс на OBSERVATIONS
, чтобы позволить DELETE
с доступом по индексу.
CREATE INDEX cuantos ON OBSERVATIONS(nplate, odatetime);
Выполнение процедуры приведет к одному FULL TABLE SCAN
в таблице OBSERVATIONS
и одному INDEX ACCESS
для каждой удаленной записи.
Для ограниченного числа удаленных повторных стержней он будет вести себя так же, как набор DELETE
, предложенный в другом ответе; для большего количества удаленных записей прошедшее время будет линейно масштабироваться с количеством удалений.
Для нетривиального количества удаленных записей вы должны предположить, что индекс не полностью находится в пуле буферов и потребуется много доступа к диску. Таким образом, вы закончите примерно с 100 удаленных строк в секунду.
Другими словами для удаления 100 тыс. строк потребуется ок. 1/4 часа.
Чтобы удалить На 1 миллион строк вам понадобится 2 3/4 часа.
Вы видите, удаляя в этой шкале первую часть задачи - FULL SCAN
вашей таблицы пренебрежимо, это займет всего несколько минут. Единственная возможность получить приемлемое время отклика в этом случае - это переключить логику на одно выражение DELETE
, как предлагается в других ответах.
Это поведение также называется правилом: «Строка за строкой медленно за медленностью» (т.е. обработка в цикле работает нормально, но только с ограниченным количеством записей).
Я попробую, но сейчас я не понимаю, как этот индекс поможет, если этот индекс создается СУБД (nplate и odatetime являются PK НАБЛЮДЕНИЙ, извините, я этого не сказал)
Чем индекс уже существует. Вы можете легко убедиться в этом, показав сценарий DDL таблицы.
Попробуй это. test на MSSQL надеется, что он будет работать и на Oracle. пожалуйста, отметьте статус.
CREATE OR REPLACE PROCEDURE DEL_OBS(cuantos number) IS
begin
DELETE OBSERVATIONS FROM OBSERVATIONS
join (select * from OBSERVATIONS ORDER BY VALUE ) as i on
nplate=i.nplate AND
odatetime=i.odatetime AND
i.ROWNUM<=cuantos;
End DEL_OBS;
Спасибо за помощь, но я не могу изменить код, только потом внесу улучшения: S
Поскольку вы говорите, что nplate
и odatetime
являются первичным ключом observations
, я предполагаю, что проблема здесь:
SELECT * FROM (
SELECT *
FROM observations
ORDER BY DBMS_RANDOM.VALUE)
WHERE ROWNUM<=cuantos;
Невозможно предотвратить полное сканирование observations
, а также большую сортировку, если это большая таблица.
Вам нужно изменить выполняемый код. По далеко, самый простой способ изменить код - это изменить исходный код и перекомпилировать его.
Однако существуют способы находятся для изменения кода, который выполняется без изменения исходного кода. Вот два:
(1) Используйте DBMS_FGAC
, чтобы добавить политику, которая определяет, участвуете ли вы в этой процедуре, и, если да, добавьте предикат в таблицу observations
следующим образом:
AND rowid IN
( SELECT obs_sample.rowid
FROM observations sample (0.05) obs_sample)
(2) Используйте DBMS_ADVANCED_REWRITE
, чтобы переписать ваш запрос, изменяя:
FROM observations
.. к ..
FROM observations SAMPLE (0.05)
Использование текста вашего запроса в политике перезаписи должно предотвратить его влияние на другие запросы к таблице observations
.
Ни то, ни другое не является легким (совсем), но стоит попробовать, если вы действительно застряли.
Используйте псевдоколонку ROWID
для фильтрации столбцов:
CREATE OR REPLACE PROCEDURE DEL_OBS(
cuantos number
)
IS
BEGIN
DELETE FROM OBSERVATIONS
WHERE ROWID IN (
SELECT rid
FROM (
SELECT ROWID AS rid
FROM observations
ORDER BY DBMS_RANDOM.VALUE
)
WHERE ROWNUM < cuantos
);
END del_obs;
Если у вас есть индекс в таблице, он может использовать быстрое полное сканирование индекса:
Настройка схемы Oracle 11g R2:
CREATE TABLE table_name ( id ) AS
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 50000;
Запрос 1: нет индекса:
DELETE FROM table_name
WHERE ROWID IN (
SELECT rid
FROM (
SELECT ROWID AS rid
FROM table_name
ORDER BY DBMS_RANDOM.VALUE
)
WHERE ROWNUM <= 10000
)
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
----------------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | 1 | 24 | 123 | 00:00:02 |
| 1 | DELETE | TABLE_NAME | | | | |
| 2 | NESTED LOOPS | | 1 | 24 | 123 | 00:00:02 |
| 3 | VIEW | VW_NSO_1 | 10000 | 120000 | 121 | 00:00:02 |
| 4 | SORT UNIQUE | | 1 | 120000 | | |
| * 5 | COUNT STOPKEY | | | | | |
| 6 | VIEW | | 19974 | 239688 | 121 | 00:00:02 |
| * 7 | SORT ORDER BY STOPKEY | | 19974 | 239688 | 121 | 00:00:02 |
| 8 | TABLE ACCESS FULL | TABLE_NAME | 19974 | 239688 | 25 | 00:00:01 |
| 9 | TABLE ACCESS BY USER ROWID | TABLE_NAME | 1 | 12 | 1 | 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 5 - filter(ROWNUM<=10000)
* 7 - filter(ROWNUM<=10000)
Запрос 2 Добавить индекс:
ALTER TABLE table_name ADD CONSTRAINT tn__id__pk PRIMARY KEY ( id )
Запрос 3 с индексом:
DELETE FROM table_name
WHERE ROWID IN (
SELECT rid
FROM (
SELECT ROWID AS rid
FROM table_name
ORDER BY DBMS_RANDOM.VALUE
)
WHERE ROWNUM <= 10000
)
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
---------------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | 1 | 37 | 13 | 00:00:01 |
| 1 | DELETE | TABLE_NAME | | | | |
| 2 | NESTED LOOPS | | 1 | 37 | 13 | 00:00:01 |
| 3 | VIEW | VW_NSO_1 | 9968 | 119616 | 11 | 00:00:01 |
| 4 | SORT UNIQUE | | 1 | 119616 | | |
| * 5 | COUNT STOPKEY | | | | | |
| 6 | VIEW | | 9968 | 119616 | 11 | 00:00:01 |
| * 7 | SORT ORDER BY STOPKEY | | 9968 | 119616 | 11 | 00:00:01 |
| 8 | INDEX FAST FULL SCAN | TN__ID__PK | 9968 | 119616 | 9 | 00:00:01 |
| 9 | TABLE ACCESS BY USER ROWID | TABLE_NAME | 1 | 25 | 1 | 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 5 - filter(ROWNUM<=10000)
* 7 - filter(ROWNUM<=10000)
Если вы не можете сделать это в одном операторе SQL с использованием ROWID
, вы можете переписать существующую процедуру, чтобы использовать точно такие же запросы, но использовать оператор FORALL
:
CREATE OR REPLACE PROCEDURE DEL_OBS(cuantos number)
IS
TYPE obs_tab IS TABLE OF observations%ROWTYPE;
begin
SELECT *
BULK COLLECT INTO obs_tab
FROM (
SELECT * FROM observations ORDER BY DBMS_RANDOM.VALUE
)
WHERE ROWNUM<=cuantos;
FORALL i IN 1 .. obs_tab.COUNT
DELETE FROM OBSERVATIONS
WHERE nplate = obs_tab(i).nplate
AND odatetime = obs_tab(i).odatetime;
END del_obs;
Вопрос в том, что вы пытаетесь сделать? Почему вы хотите удалить случайное подмножество своих записей?