如何在匿名PL / SQL块中使用重复占位符名称?

时间:2014-05-12 14:29:50

标签: sql oracle plsql

我正在尝试在Person这样的表BEGIN EXECUTE IMMEDIATE q'[ MERGE INTO Person p USING (SELECT :x id, :y first_name, :z last_name FROM Dual) data ON (p.id = :x) -- note the repeated placeholder :x here WHEN MATCHED THEN UPDATE SET p.first_name = data.first_name, p.last_name = data.last_name WHEN NOT MATCHED THEN INSERT (id, first_name, last_name) VALUES (data.id, data.first_name, data.last_name) ]' USING 123, 'Foo', 'Bar'; END; / 上运行UPSERT

ORA-01008: not all variables bound ORA-06512: at line 2

如果我运行上面给出的代码,Oracle会抛出… USING 123, 'Foo', 'Bar', 123;

根据docs,这不应该发生(在匿名块内)。我做错了什么?

<子> 我知道如果我提供四个参数ON (p.id = data.id),我可以解决这个问题,这很好用,但显然我不想重复自己。编辑:正如答案中所指出的,{{1}}是另一种可能的解决方法。

问题不在于如何解决这个问题,更多的是在这种情况下理解这个错误的原因。

2 个答案:

答案 0 :(得分:4)

您似乎误解了文档:

  

如果动态SQL语句表示匿名PL / SQL块或   CALL语句,重复占位符名称很重要。

这表明如果正在动态执行的语句是PL / SQL块,则不必重复绑定变量。您的动态语句是SQL语句,不符合条件。您可以通过将beginend;添加到动态语句来解决此问题:

BEGIN
    EXECUTE IMMEDIATE q'[
        BEGIN
           MERGE INTO Person p
           USING (SELECT :x id, :y first_name, :z last_name FROM Dual) data    
           ON (p.id = :x) -- note the repeated placeholder :x here    
           WHEN MATCHED THEN
              UPDATE SET p.first_name = data.first_name, p.last_name = data.last_name
           WHEN NOT MATCHED THEN
              INSERT (id, first_name, last_name) VALUES (data.id, data.first_name, data.last_name)
        END;
    ]' USING 123, 'Foo', 'Bar';
END;
/

在一个案例中,修复此语句而不重复绑定变量很简单:

BEGIN
    EXECUTE IMMEDIATE q'[
        MERGE INTO Person p
        USING (SELECT :x id, :y first_name, :z last_name FROM Dual) data
        ON (p.id = data.id) 
        WHEN MATCHED THEN
              UPDATE SET p.first_name = data.first_name, p.last_name = data.last_name
        WHEN NOT MATCHED THEN
              INSERT (id, first_name, last_name) VALUES (data.id, data.first_name, data.last_name)
    ]' USING 123, 'Foo', 'Bar';
END;
/

最后,我觉得我必须指出在这种情况下使用动态SQL没有什么理由。除非有更多的事情影响正在运行的SQL,否则这应该只是一个静态SQL语句。

答案 1 :(得分:3)

documentation you linked to说问题是;或者至少上一节确实:

  

如果动态SQL语句不表示匿名PL / SQL块或CALL语句,则重复占位符名称无关紧要。占位符与USING子句中的绑定变量按位置关联,而不是按名称关联。

referred to the part说:

  

如果动态SQL语句表示匿名PL / SQL块或CALL语句,则重复占位符名称非常重要。每个唯一占位符名称必须在USING子句中具有相应的绑定变量。如果重复占位符名称,则无需重复其对应的绑定变量。对该占位符名称的所有引用都对应于USING子句中的一个绑定变量。

您的动态SQL语句不是匿名块。您在匿名块中执行动态SQL ,但这无关紧要;它是execute immediate正在做的事情。这是一个简单的SQL merge语句。

所以你必须重复这些值,你不能避免这种情况 - 除非你创建一个执行merge的过程并将三个绑定变量作为参数传递给它,每次一个。或者在你的语句周围创建一个匿名的块包装器,这似乎有点无意义,但也许可以说明行为上的差异;这将有效:

BEGIN
    EXECUTE IMMEDIATE q'[
      BEGIN
        MERGE INTO Person p
        USING (SELECT :x id, :y first_name, :z last_name FROM Dual) data

        ON (p.id = :x) 

        WHEN MATCHED THEN
              UPDATE SET p.first_name = data.first_name, p.last_name = data.last_name
        WHEN NOT MATCHED THEN
              INSERT (id, first_name, last_name) VALUES (data.id, data.first_name, data.last_name);
      END;
    ]' USING 123, 'Foo', 'Bar';
END;
/

请注意动态声明中的BEGIN / END

或者在这种情况下,正如评论和其他答案中所述,将代码更改为ON (p.id = data.id) - 这只是避免了问题......