Я наткнулся на следующий вопрос.
Но не удалось найти удовлетворительных ответов.
Ответ 1
(E) Все выше
Это смешной вопрос с множественным выбором. Сроки раннего, позднего и отсроченного связывания неоднозначны. И PL/SQL можно запускать разными способами, в том числе в SQL.
Вот мои (возможно неправильные) определения вариантов:
- Нет привязки - переменные не имеют типов.
- Раннее связывание - переменные типы фиксируются во время компиляции.
- Late Binding. Переменные типы являются гибкими и могут быть установлены во время выполнения.
- Отложенное связывание. Во время компиляции определяются несколько типов переменных, но во время выполнения выбирается только один из них.
Теперь мы должны сопоставлять эти варианты с различными контекстами PL/SQL: статические SQL и PL/SQL, пустые анонимные блоки, удаленные процедуры, динамический SQL и PL/SQL, совместное использование адаптивных курсоров, операции FILTER
, объектно-ориентированные PL/SQL, ЛЮБЫЕ * типы, и я, вероятно, пропустил еще немного.
(A) Нет привязки
У пустого анонимного блока нет переменных, поэтому ничего не связано. Я не уверен, что это действительно соответствует определению без привязки, похоже, похоже на краевой случай. На некоторых языках всегда есть объект, и что-то всегда должно быть связано, но не в PL/SQL.
(B) Ранняя привязка
Обычный SQL и PL/SQL используют раннее связывание - переменным присваивается тип, и они должны придерживаться его. Несоответствия типов либо выдают ошибку компилятора, либо требуют неявного преобразования.
Удаленные вызовы процедур с REMOTE_DEPENDENCIES_MODE
, установленными на "TIMESTAMP", возможно, раннее связывание. Временная метка устанавливается во время компиляции, когда все проверяется. Он по-прежнему проверяется во время выполнения, но это простая и быстрая проверка.
(C) Поздняя привязка
Динамический SQL и PL/SQL используют позднюю привязку, потому что код даже не скомпилирован до времени выполнения. Это относится как к DBMS_SQL
, так и к execute immediate
.
Объектно-ориентированный PL/SQL использует позднюю привязку. Тип устанавливается во время компиляции, но во время выполнения может использоваться другой подтип.
ANYTYPE, ANYDATA и ANYDATASET также используют последнее связывание, так как они могут быть созданы во время выполнения или получены и выполнены во время выполнения.
Удаленные вызовы процедур с REMOTE_DEPENDENCIES_MODE
, установленными на "ПОДПИСЬ", возможно, связаны с поздним связыванием. Подпись проверяется как во время компиляции, так и во время выполнения, и допускает крошечную гибкость в типах.
(D) Отложенная привязка
Некоторые функции Oracle SQL создают несколько путей кода, но выполняют только один из них. Адаптивные функции обмена курсором и FILTER
будут создавать несколько способов запуска одного и того же оператора SQL, а соответствующая версия будет выбрана во время выполнения.
Права и права защитника
Права и права детектора также усложняют этот вопрос. Но я думаю, что в конечном счете они не имеют никакого значения, и что оба они все еще ранние. Компилятор все еще решает тип во время компиляции. Хотя вы можете использовать права invoker для скрытного изменения типа во время выполнения, он будет генерировать ошибку только потому, что он не соответствует ожидаемому типу.
Например, скажем, есть две схемы, которые имеют одинаковые имена таблиц и столбцов, но разные типы:
create table user1.test_table(a number);
insert into suer1.test_table values(1);
create table user2.test_table(a date);
insert into user2.test_table values(sysdate);
Если вы создаете эту функцию в USER1, это выглядит так: тип V_VALUE
является динамическим и может меняться с пользователем.
create or replace function user1.test_function return varchar2 authid current_user is
v_value test_table.a%type;
begin
select a into v_value from test_table;
return to_char(v_value);
end;
/
Код компилируется с использованием типов из USER1
и отлично работает при запуске USER1. Однако, когда USER2 запускает его, генерируется эта ошибка: ORA-00932: inconsistent datatypes: expected NUMBER got DATE
.
Это заставляет меня думать, что права на вызовы и определители не влияют на привязку. Они оба используют раннее связывание в статических SQL и PL/SQL.
Ответ 3
edit. Я просто перефразировал ответ, чтобы немного ускорить процесс.
короткий ответ
- PL использует раннее связывание, а иногда и последнее связывание (например, опаздывает при использовании объектно-ориентированных возможностей PL/SQL и полиморфизма, которые они используют)
- SQL использует отложенную привязку: разрешение символов (имена таблиц, столбцов и т.д.) происходит во время отложенной компиляции, которая встречается во время выполнения: этот шаг включает в себя синтаксический анализ и компиляцию SQL-текста в исполняемые инструкции "план выполнения" )
Отложенное связывание лежит в основе механизма SQL и делает его чрезвычайно удобным для изменения распределения данных; это основная функция, о которой все должны знать, поэтому я думаю, что вопрос был задан, если только "PL/SQL" не означает "PL" (как часто это делают документы Oracle).
В следующем примере, надеюсь, сразу становится ясно. У нас есть два пользователя ( "demo" и "scott" ), которым принадлежит таблица и каждая процедура с одинаковыми именами.
demo
invoked_proc
x_table
scott
invoked_proc
x_table
Если мы создадим процедуру, приведенную ниже в схеме "demo", так Oracle может разрешить символы во время компиляции и во время выполнения
compilation
"invoked_proc": demo.invoked_proc
"x_table": demo.x_table
execution when logged as "demo": the same
execution when logged as "scott"
"invoked_proc": demo.invoked_proc <- resolves as per compilation
"x_table": scott.x_table <- resolved differently
Вот процедура, и тот факт, что мы используем права invoker, не является основополагающим, это просто делает этот вопрос более очевидным:
CREATE or replace PROCEDURE demo.invoker_proc
AUTHID CURRENT_USER
IS
n number := 1;
BEGIN
invoked_proc();
select id into n
from X_TABLE where id = n;
END;
/
Длинный ответ
Применяются все типы привязки (b, c, d). Среди них отсроченная привязка, вероятно, самая замечательная, поэтому, если они попросили выбрать только один ответ, я бы окончательно пошел на "d" (отложенное обязательство), когда говорил о SQL, и "b" (раннее связывание) при работе с PL.
Мы, вероятно, должны начать с того, чтобы сказать, что терминология важна, и что, возможно, ответ krokodilko является наиболее авторитетным с учетом документов, на которые он указывает ( даже если эта ссылка потенциально устарела).
Однако я твердо верю, что дело в том, что в Oracle, движок PL и движок SQL почти полностью расставлены и разрешают имена совершенно по-разному. В то время как первое обычно связано с некоторой формой раннего или позднего связывания (в основном, раннего связывания), последнее всегда подразумевает отсроченное связывание: операторы SQL "компилируются" в исполняемые планы при выполнении, а не во время компиляции. Семантическое разрешение символов откладывается до тех пор, пока запрос не будет выполнен, поэтому имена будут разрешены в соответствии с текущим контекстом (в первую очередь, с текущим пользователем). Это также тип привязки, используемый при выпуске оператора execute immediate
, dbms_sql
или dbms_job.submit
, вызванного с помощью NO_PARSE = true
.
Когда Oracle встречает текст оператора SQL, даже если он является частью скомпилированного блока PL (процедура, функция, триггер...), он выполняет синтаксический анализ своих символов, , затем семантический анализ и, наконец, "компилирует" план выполнения (определяет процедурные шаги алгоритма, который фактически разрешит запрос).
В зависимости от ситуации он может пропустить часть этого анализа: однако все это принципиально происходит во время выполнения, а не во время компиляции. Это позволяет двигателю адаптироваться, например, к последней статистике, полученной от данных (это то, что стремится Оптимизатор на основе затрат) и принципиально отличается от того, что использовал оптимизатор, основанный на правилах, в более ранних версиях (запросы где "скомпилировано" во время компиляции, и алгоритм знал заранее и фиксировался до следующей повторной компиляции;).
Единственным предварительно испеченным продуктом компиляции модуля PL является разрешение его прямых зависимостей, которое происходит во время компиляции (информация, которая хранится в коде DIANA) и позволяет движку чтобы обнаружить, когда пришло время повторно скомпилировать блок PL из-за внешних изменений (например, изменения в структуре таблицы, упомянутой в SQL-заявлении). Обратите внимание, что этот превентивный механизм не подходит при правильном использовании invoker, потому что Oracle не может знать, что имя будет ссылаться позже, во время выполнения.
Все это очень заметно, когда мы используем права invoker для запуска процедуры.
Рассмотрим вышеупомянутый случай, в котором у двух пользователей есть процедура и таблица по их соответствующим схемам, именованные равными (таблицы являются идентичными, за исключением того, что таблица scott имеет индекс на нем)
CONNECT demo/demo
CREATE PROCEDURE invoked_proc IS
BEGIN
DBMS_OUTPUT.put_line ('invoked_proc owned by demo');
END;
/
GRANT execute on invoked_proc to public;
create table X_TABLE (id number);
insert into x_table values(1);
commit;
CONNECT scott/tiger
CREATE PROCEDURE invoked_proc IS
BEGIN
DBMS_OUTPUT.put_line ('invoked_proc owned by scott');
END;
/
create table X_TABLE (id number);
create index X_INDEX ON X_TABLE(id);
insert into x_table values(1);
commit;
и подумайте о том, что происходит, когда вы компилируете третью процедуру (по одной схеме), которая ссылается на эти имена и которая выполняется с правами invoker:
CREATE or replace PROCEDURE invoker_proc
AUTHID CURRENT_USER
IS
n number := 1;
BEGIN
invoked_proc();
select id into n
from X_TABLE where id = n;
END;
/
Что мы можем сказать о символах, которые были разрешены компилятором?
Мы можем быстро вывести их, посмотрев зависимости INVOKER_PROC
:
select owner, name,
referenced_owner, referenced_name
from dba_dependencies
where name = 'INVOKER_PROC';
OWNER NAME REFERENCED_OWNER REFERENCED_NAME
------- ------------- ------------------ -----------------
DEMO INVOKER_PROC DEMO X_TABLE
DEMO INVOKER_PROC DEMO INVOKED_PROC
Таким образом, как процедура, так и таблица, идентифицированные компилятором, принадлежат DEMO, пользователю, которому принадлежат процедуры.
Что касается плана, лежащего в основе запроса sql, пока ничего не определено:
Select substr(sql_text,1,40),
sql_id,
plan_hash_value,
parsing_schema_name,
child_number
from v$sql
where sql_text like '%X_TABLE%'
;
no relevant records found...
Тем не менее, все начинает меняться при запуске SQL-движка, чего не происходит до выполнения.
connect demo/demo
EXEC demo.invoker_proc
> invoked_proc owned by demo
и глядя на планы, мы видим, что новый план вокруг
Select substr(sql_text,1,40),
sql_id,
plan_hash_value,
parsing_schema_name,
child_number
from v$sql
where sql_text like '%X_TABLE%'
;
SUBSTR(SQL_TEXT,1,40) SQL_ID PLAN_HASH_VALUE PARSING_SCHEMA_NAME CHILD_NUMBER
-------------------------------------- ------------- --------------- ------------------- ------------
SELECT ID FROM X_TABLE WHERE ID = :B1 01a77qj300y18 1220025608 DEMO 0
Если мы запустим ту же процедуру из перспективы scott и посмотрим на планы, мы увидим, что был вызван тот же invoked_proc
, который принадлежит DEMO (он выводит "invoked_proc owned by demo
" ).
Таким образом, разрешение имени части PL, переносимое во время компиляции, по-прежнему остается верным (раннее связывание). То же самое не совсем верно для части SQL:
connect scott/tiger
EXEC demo.invoker_proc;
> invoked_proc owned by demo
SUBSTR(SQL_TEXT,1,40) SQL_ID PLAN_HASH_VALUE PARSING_SCHEMA_NAME CHILD_NUMBER
-------------------------------------- ------------- --------------- ------------------- ------------
SELECT ID FROM X_TABLE WHERE ID = :B1 01a77qj300y18 1220025608 DEMO 0
SELECT ID FROM X_TABLE WHERE ID = :B1 01a77qj300y18 3707869577 SCOTT 1
Мы видим, что он создал другой план для нового запроса с другим child_number
. Второй план ссылается на другую таблицу, отличную от той, которая была определена во время компиляции (отложенная привязка). Это подтверждается, если мы посмотрим на содержание планов; первый план включает в себя простой стол, на котором может выполняться только полное сканирование:
select plan_table_output
from table(dbms_xplan.display_cursor(
SQL_ID => '01a77qj300y18'
,CURSOR_CHILD_NO => 0
));
SQL_ID 01a77qj300y18, child number 0
-------------------------------------
SELECT ID FROM X_TABLE WHERE ID = :B1
Plan hash value: 1220025608
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 4 (100)| |
|* 1 | TABLE ACCESS FULL| X_TABLE | 1 | 13 | 4 (0)| 00:00:01 |
-----------------------------------------------------------------------------
а вторая таблица, которая предлагает индекс, позволяет Oracle получить лучший путь кода (в этом случае сканирование диапазона индекса)
select plan_table_output
from table(dbms_xplan.display_cursor(
SQL_ID => '01a77qj300y18'
,CURSOR_CHILD_NO => 1
));
SQL_ID 01a77qj300y18, child number 1
-------------------------------------
SELECT ID FROM X_TABLE WHERE ID = :B1
Plan hash value: 3707869577
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1 (100)| |
|* 1 | INDEX RANGE SCAN| X_INDEX | 1 | 13 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------
Вывод заключается в том, что PL обычно включает раннее или позднее связывание, тогда как SQL всегда откладывается.
Есть дополнительные соображения, которые могут вступить в игру, например, использование BASELINES
(предварительно скомпилированные планы, которые постоянно сохраняются и могут быть выбраны или нет в зависимости от того, что оптимизатор считает более дешевым) или ADAPTIVE CURSORS
(альтернативные планы, хранящиеся в пул библиотек, выбранный во время выполнения); и они включают несколько иные понятия. Однако, учитывая отсутствие консенсуса в отношении термина "отсроченное обязательство", возможно, все они могут считаться частью "отложенной обязательной вещи" (мне любопытно узнать мнение о том, кто написал оригинальную викторину).
Например, это не противоречит тому, что я вижу в .NET: отложенное связывание, по-видимому, является темой, слабо связанной с кодом, который рефлексивно сгенерирован и скомпилирован во время выполнения. Хотя GWT утверждает, что использует "отложенное обязательство"; что: альтернативные сегменты кода скомпилированы и только один выбран во время выполнения (для их цитирования "По сути, отложенное связывание является ответом GWT на отражение Java", см. здесь)