Это хорошее время, чтобы узнать, что такое xvalues и glvalues .
R-значения могут быть двух типов - , значения и , значения . В соответствии с новым стандартом C ++ 17
prvalue - это выражение, вычисление которого инициализирует объект, битовое поле или операнд оператора, как указано в контексте в котором оно появляется.
, поэтому что-то вроде fun()
в вашем примере оценивается как prvalue (который является r-значением). Это также говорит нам, что fun().v
не является prvalue, так как это не ванильная инициализация.
X-значения , которые также являются r-значениями, определяются так
xvalue (значение «eXpiring») также относится к объекту обычно ближе к концу срока службы (например, ресурсы можно переместить). Определенные виды выражений, включающие ссылки на rvalue (8.3.2), дают значения xvalue. [Пример: Результатом вызова функции, возвращаемый тип которой является rvalue-ссылкой на тип объекта, является xvalue (5.2.2). - конец примера]
В дополнение к rvalues, еще одной зонтичной категорией значений является glvalue , которые бывают двух типов xvalues и традиционные lvalues .
На данный момент мы определили категории основных значений. Это может быть визуализировано следующим образом
Категория glvalue может широко означать, что lvalues должны были означать до того, как семантика перемещения стала вещью - вещью, которая может быть в левой части выражения. glvalue означает обобщенное lvalue.
Если мы посмотрим на определение значения xvalue , то оно говорит, что что-то является значением xvalue , если оно подходит к концу своего времени жизни. В вашем примере, fun().v
подходит к концу своего времени жизни. Таким образом, его ресурсы могут быть перемещены. И поскольку его ресурсы можно перемещать, это не lvalue, поэтому ваше выражение вписывается в единственную оставшуюся категорию значений листьев - xvalue .
Используйте трассировку SQL, чтобы увидеть, каковы планы выполнения в каждом случае. Одна возможность, которая приходит на ум (исходя из опыта): связывает ли пакет с запросом неверный тип значений? Возможно, в SQL Plus вы выполняете:
select * from mytable where id = '1234';
, но в PL / SQL вы выполняете:
select * from mytable where id = p_id;
, где p_id определяется как число. Это приведет к принудительному использованию TO_NUMBER в столбце идентификатора и предотвратит использование индекса Oracle.
Скорее всего, дольше выполняются не запросы, а накладные расходы на их обработку в PL / SQL
.
Когда вы обрабатываете результаты запроса в ] PL / SQL
, происходит переключение контекста. Он требует передачи большого количества данных между процессами Oracle
и выполняется довольно медленно.
Как этот код:
DECLARE
cnt INTEGER := 0;
CURSOR cr_main IS
SELECT 1 AS id
FROM dual
CONNECT BY
level <= 1000000;
BEGIN
FOR res IN cr_main
LOOP
cnt := cnt + res.id;
END LOOP;
DBMS_OUTPUT.put_line(cnt);
END;
выполняется на моей машине более 3
секунд, а этот :
SELECT SUM(1)
FROM dual
CONNECT BY
level <= 1000000
завершается всего за 0,5
секунды.
Переключение контекста также происходит при вызове PL / SQL
из SQL
, например:
SELECT plsql_function(column)
FROM mytable
] или при срабатывании триггера.
Наш администратор баз данных наблюдал, что эти запросы занимают 10 минут через PLSQL и 10 секунд через PL / PSQL .
Я могу понять, если администратор базы данных не захочет решать эту проблему за вас, но если ваш администратор базы данных действительно видел оба случая и еще не предоставил вам планы объяснения для обоих случаев, тогда он действительно не очень хороший администратор базы данных. .
Вероятно, нет неправильной конфигурации, я сам такое случалось - все связываемые переменные, никаких констант, никаких подсказок. Запускать напрямую - производительность хорошая. Поместите его внутрь BEGIN..END - бац, чертовски медленно. Оказалось, что иногда запросы просто используют разные планы выполнения из PL / SQL (это был Oracle 9.2).
Мое решение - использовал подсказки, пока версия PL / SQL не использовала тот же план, что и SQL. так строки, а затем ждет, когда вы попросите больше, но PL / SQL должен обрабатывать их всех, не спрашивая. Тривиальная проблема, но иногда ее забывают.
Если вы работаете в Windows, я рекомендую использовать глобальный уникальный идентификатор (GUID) в качестве идентификатор версии.
Отметка времени (даже если UTC) может вызвать проблемы, если часы установлены пользователем. Увеличивающееся число (если оно хранится в памяти) может вызвать проблемы при перезапуске серверного приложения или его переполнении (если это всего лишь 16-битное или 32-битное целое число).
Он требует передачи большого количества данных между процессами Oracle и выполняется довольно медленно.Как этот код:
DECLARE
cnt INTEGER := 0;
CURSOR cr_main IS
SELECT 1 AS id
FROM dual
CONNECT BY
level <= 1000000;
BEGIN
FOR res IN cr_main
LOOP
cnt := cnt + res.id;
END LOOP;
DBMS_OUTPUT.put_line(cnt);
END;
выполняется более 3 секунд на моей машине, а этот:
SELECT SUM(1)
FROM dual
CONNECT BY
level <= 1000000
завершается всего за 0,5 секунды.
] Переключение контекста также происходит, когда вы вызываете PL / SQL из SQL, например:
SELECT plsql_function(column)
FROM mytable
or when a trigger fires.
Одним из способов решения проблемы переключения контекста является использование BULK COLLECT. Если вы собираете много строк, использование BULK COLLECT INTO для коллекции какого-либо типа может значительно ускорить SQL в операторах PL / SQL.
DML (например, SELECT, UPDATE, DELETE), выданный через SQLPlus, выдается непосредственно в механизм SQL Oracle, тогда как DML в процедуре PLSQL является сначала обрабатывается PL / SQL (например, для привязки переменных), а затем отправляется в механизм SQL.
По большей части один и тот же оператор в PL / SQL будет работать так же, как SQL, и оба способа обычно производят один и тот же план выполнения. По моему опыту (обычно, когда требуется привязка переменных), это может привести к очень разной производительности. Я встречал случаи, когда SELECT, выдаваемый в SQL Plus, занимал доли секунды, в то время как SELECT, выданный через PL / SQL, занимал 1-2 минуты.
Я рекомендую вам настроить свой оператор так, чтобы он работал так же хорошо в PL / SQL как и в SQL. Сосредоточьтесь на правильном связывании переменных (используя FORALL и BULK COLLECT), но также изучите планы выполнения и выполните модульные тесты.
Я встречал случаи, когда SELECT, выдаваемый в SQL Plus, занимал доли секунды, в то время как SELECT, выданный через PL / SQL, занимал 1-2 минуты.Я рекомендую вам настроить свой оператор так, чтобы он работал так же хорошо в PL / SQL как и в SQL. Сосредоточьтесь на правильном связывании переменных (используя FORALL и BULK COLLECT), но также изучите планы выполнения и выполните модульные тесты.
Я встречал случаи, когда SELECT, выдаваемый в SQL Plus, занимал доли секунды, в то время как SELECT, выданный через PL / SQL, занимал 1-2 минуты.Я рекомендую вам настроить свой оператор так, чтобы он работал так же хорошо в PL / SQL как в SQL. Сосредоточьтесь на правильном связывании переменных (используя FORALL и BULK COLLECT), но также изучите планы выполнения и выполните модульные тесты.
Вы действительно сравниваете здесь примерно одинаковые? Выполняете ли вы необработанные операторы SQL в PL / SQL (оптимальный случай) или используете явные или неявные курсоры для возврата значений, а затем их обработки? Есть большая разница.