PL / SQL使用哪种类型的绑定?

时间:2016-10-29 15:11:11

标签: sql oracle plsql binding

我遇到了以下问题。

PL / SQL使用以下哪个

  • (A)No Binding
  • (B)早期结合
  • (C)晚期绑定
  • (D)延期装订

但找不到任何令人满意的答案。

有人可以对此作出解释吗?

6 个答案:

答案 0 :(得分:15)

  

(E)以上所有

这是一个荒谬的多项选择题。术语早期,晚期和延迟绑定是模糊的。 PL / SQL可以以多种不同的方式运行,包括在SQL中。

以下是我(可以说是不正确的)选择的定义:

  1. 无约束 - 变量没有类型。
  2. 早期绑定 - 变量类型在编译时修复。
  3. 延迟绑定 - 变量类型是灵活的,可以在运行时设置。
  4. 延迟绑定 - 多个变量类型在编译时定义,但在运行时只选择其中一个。
  5. 现在我们必须将这些选择与不同的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_SQLexecute immediate

    面向对象的PL / SQL使用后期绑定。类型在编译时设置,但在运行时可以使用不同的子类型。

    ANYTYPE,ANYDATA和ANYDATASET也使用后期绑定,因为它们可以在运行时创建,也可以在运行时检索和执行。

    远程过程调用REMOTE_DEPENDENCIES_MODE设置为" SIGNATURE"可以说是晚期约束力。在编译时和运行时检查签名,并允许在类型上有一点点灵活性。

    (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中使用早期绑定。

答案 1 :(得分:11)

您可以在与Oracle 8相关的非常旧的文档中找到答案:
https://docs.oracle.com/cd/A58617_01/server.804/a58236/05_ora.htm

  

效率与灵活性

     

在执行PL / SQL程序之前,必须对其进行编译。该   PL / SQL编译器解析对Oracle模式对象的引用   在数据字典中查找他们的定义。然后,   编译器将存储地址分配给将保留的程序变量   Oracle数据使Oracle可以在运行时查找地址。这个   过程称为绑定。

     

数据库语言如何实现绑定会影响运行时效率   和灵活性。在编译时绑定,称为静态或早期   绑定,因为模式的定义提高了效率   然后查找对象,而不是在运行时。另一方面,   在运行时绑定,称为动态或后期绑定,增加   灵活性,因为架构对象的定义可以保留   在那之前不知道。

     

主要设计用于高速事务处理,PL / SQL   通过捆绑SQL语句和避免运行时来提高效率   汇编。与编译和执行的SQL不同   运行时声明语句(后期绑定),处理PL / SQL   在编译时进入机器可读的p代码(早期绑定)。在奔跑   时间,PL / SQL引擎只执行p代码。

但是后续版本中的Oracle从文档中删除了整个章节与Oracle的交互

所以,根据上面的答案,答案是:(B)早期绑定 - 对于Oracle版本8,以及可能在更进一步的版本中肯定。

答案 2 :(得分:3)

编辑:我只是改写了答案,以便更快地达到目的。

简短回答

  • PL使用早期绑定,有时候使用后期绑定(例如,在使用PL / SQL的面向对象功能时会迟到,以及它们所具有的多态性)
  • SQL使用延迟绑定:符号的解析(表名,列等)在运行时发生的延迟编译期间发生:此步骤涉及将sql文本解析并编译为可执行指令( "执行计划")

延迟绑定是SQL引擎的核心,使其非常适应不断变化的数据分布;这是每个人都应该注意的核心功能,所以我最好的猜测是问题要求,除非通过" PL / SQL"他们的意思是" PL" (正如Oracle文档所做的那样)。

以下示例希望能够立即明确这一点。我们有两个用户(" demo"和#34; 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

这是程序,而且我们使用调用者权利这一事实并非根本,只是让事情变得更加明显:

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)。其中,延迟绑定可能是最引人注目的,所以如果他们要求只选择一个答案,我明确地选择&#34; d&#34; (谈到SQL时)(延迟绑定),&#34; b&#34; (早期约束)处理PL时。

我们可能首先应该说术语很重要,krokodilko提供的答案可能是最具权威性的,因为它指的是文档(即使该引用可能已经过时)。

但是我坚信整个问题的关键在于,在Oracle中,PL引擎和SQL引擎几乎完全分开并以完全不同的方式解析名称。虽然前者通常涉及某种形式的早期或晚期绑定(主要是早期绑定),但后者总是暗示延迟绑定:SQL语句是&#34;编译&#34;执行时执行可执行计划,而不是在编译时。符号的语义解析延迟到查询运行时,因此根据当前上下文(主要是当前用户)解析名称。这也是在发出execute immediate语句时使用的绑定类型,dbms_sqldbms_job.submitNO_PARSE = true一起调用。

当Oracle遇到 SQL语句的文本时,即使是已编译的PL单元(过程,函数,触发器......)的一部分,它也会执行语法分析然后是语义分析,最后&#34;编译&#34;执行计划(确定将实际解析查询的算法的程序步骤)。

根据具体情况,它可以跳过部分分析:但是所有这些基本上都是在运行时发生,而不是在编译时。这允许引擎适应,例如它从数据中收集的最新统计数据(这是基于成本的优化器所努力做的),并且与基于规则的优化器在早期版本中所做的基本不同(查询)编译时编译&#34;编译时算法提前知道并修复,直到下次重新编译;)。

PL单元编译的唯一预烘焙产品是直接依赖关系的解析,在编译时发生(存储在DIANA代码中的信息)并启用引擎检测何时是由于外部更改而重新编译PL单元的时间(例如,SQL语句中提到的表结构的更改)。请注意,当使用调用者权限时,这种抢占机制不足,正是因为Oracle在执行期间无法知道名称将在后面引用的内容。

当我们使用调用者权限来运行程序时,所有这一切都非常明显。 考虑上面提到的情况,其中两个用户在他们各自的模式上有一个过程和一个表,命名相等(表格恰好相同,只是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;

并考虑当您编译引用这些名称并使用调用者权限执行的第三个过程(在单个模式上)时会发生什么:

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(它输出&#34; {{ 1}}&#34)
。 所以编译过程中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(存储在库池中的备用计划,在运行时选择);它们涉及略有不同的概念。但是,由于对术语“延迟绑定”缺乏共识,可能所有这些都可以被视为延迟绑定事项的一部分&#34; (我很想知道谁写了原始测验的意见)。

例如,这与我在.NET中看到的不同:延迟绑定似乎是与在运行时期间反复生成和编译的代码松散相关的主题。虽然GWT声称使用&#34;延迟绑定&#34 ;;意思是:编译替代代码段,并且在运行时只选择一个代码段(引用它们&#34;本质上,延迟绑定是Java反射的GWT答案&#34;,参见here

答案 3 :(得分:2)

从发布的查询中,我了解到使用哪种绑定类型PL / SQL是一般的。在这种情况下,选项(B)早期绑定就是oracle的答案。

此外,

  1. PL - 存储过程 - 早期绑定。
  2. PL - 远程程序 - 后期绑定。
  3. 动态SQL - 后期绑定。

答案 4 :(得分:0)

在执行PL / SQL程序之前,必须对其进行编译。 PL / SQL编译器通过在数据字典中查找它们的定义来解析对Oracle对象的引用。然后,编译器将存储地址分配给将保存Oracle数据的程序变量,以便Oracle可以在运行时查找地址。此过程称为绑定

数据库语言如何实现绑定,影响运行时效率和灵活性?

在编译时绑定,称为静态或早期绑定,可以提高效率,因为数据库对象的定义会被查找,而不是在运行时查找。

另一方面,在运行时绑定(称为动态或后期绑定)会增加灵活性,因为在此之前数据库对象的定义可能仍然未知。

答案 5 :(得分:0)

(B)早期绑定,用于编译时(C)后期绑定,用于运行时间