【问题标题】:What restricition imposed on cursor variable name in plsql?pl sql中游标变量名有什么限制?
【发布时间】:2017-06-06 21:11:27
【问题描述】:

我在 sql developer 中执行了简单的匿名块,期望从​​“FOO”表中删除 x 行,但是我最终得到了意外的结果,这反过来又删除了整行。

DECLARE
  type pkarray IS VARRAY(3) OF RAW(16);
  ids pkarray;      
BEGIN  
  ids := pkarray('guid_value1','guid_value2','guid_value3');          
  FOR i in 1 .. 3 LOOP  
    FOR foo IN (SELECT FOO_ID FROM FOO WHERE BAR_ID = UPPER(ids(i))) LOOP                  
           DELETE FROM FOO WHERE FOO_ID = foo.FOO_ID;                  
    END LOOP;        
  END LOOP;      
END;

但是,当我将游标变量“foo”更改为“abc”之类的其他值时,程序通过删除 x 行正常工作。我提前知道的数字 x。

【问题讨论】:

  • 这取决于 Oracle 如何评估变量、表名......在您的查询中,foo.id 被视为“表 foo 的列 id”,而不是“变量foo"的字段id,因此出现意外行为。您应该能够通过更改变量名称甚至通过在查询中为表foo 使用别名(不同于foo)来更改此行为。这里有一个很好的答案,只是想找到它
  • 范围和优先级规则适用于所有变量,而不仅仅是您的示例中的游标记录。想想当你 select * from foo where fid = 23fid 既是 foo 中的列名又是局部变量时会发生什么。
  • 感谢您的回复。不幸的是,由于我对这个主题缺乏理解和经验,我仍然不完全理解它为什么会这样。然而,根据执行代码的观察,我使用游标 var 作为小写表名再次向自己确认会产生负面影响。我会继续阅读相关文章。感谢您的解释。

标签: variables plsql cursor naming-conventions shadowing


【解决方案1】:

由于 PL/SQL 对标识符不区分大小写,fooFOO 是等价的。让我们重现您的部分代码,为清楚起见将变量名称设置为小写:

 FOR foo /*1*/ IN (SELECT foo_id FROM foo WHERE bar_id = UPPER(ids(i))) LOOP                  
     DELETE FROM foo /*2*/ WHERE foo_id = foo.foo_id;                  
 END LOOP;

我们在这里处理名称阴影。当编译器计算这个表达式时

DELETE FROM foo WHERE foo_id = foo.foo_id;

它发现所需的标识符foofoo_id 是已知的(在表定义的上下文中)。所以不需要在语法树中寻找更高的定义使用的名称。换句话说,第一个foo(循环变量)被表名遮蔽,并且不用于删除查询的编译,这与

相同
DELETE FROM foo WHERE foo_id = foo_id;

它的过滤条件对于除NULL 之外的所有foo_id 都为真,这会导致删除整行。


幸运的是,这个问题可以通过naming conventions 一劳永逸地解决:在 PL/SQL 块中使用带有特殊前缀等的名称,从而保护您的代码不会与架构对象名称发生意外交叉。

【讨论】:

    猜你喜欢
    • 2016-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多