我在SQL开发人员中的简单匿名块中执行了执行,期望将x行从" foo"中删除。但是表我最终取得了意外的结果,从而删除了整行。
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。
由于PL/SQL对标识符不敏感,因此foo
和FOO
是等效的。让我们复制代码的一部分,为了清楚起见,在较低的情况下启动变量名:
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;
它看到需要标识符foo
和foo_id
是已知的(在表定义的上下文中(。因此,无需在语法树中寻求更高的语法来定义使用的名称。换句话说,第一个foo
(循环变量(由表名称遮蔽而不用于删除查询的编译,该查询与
DELETE FROM foo WHERE foo_id = foo_id;
及其过滤条件对于除 NULL
以外的所有foo_id
都是正确的,什么导致删除整个行。
幸运的是,可以用命名约定一劳永逸地解决此问题:在PL/SQL块中使用具有特殊前缀等的名称,保护您的代码免受与模式对象名称的意外交集。