我正在尝试在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}}是另一种可能的解决方法。
子>
问题不在于如何解决这个问题,更多的是在这种情况下理解这个错误的原因。
答案 0 :(得分:4)
您似乎误解了文档:
如果动态SQL语句表示匿名PL / SQL块或 CALL语句,重复占位符名称很重要。
这表明如果正在动态执行的语句是PL / SQL块,则不必重复绑定变量。您的动态语句是SQL语句,不符合条件。您可以通过将begin
和end;
添加到动态语句来解决此问题:
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子句中的绑定变量按位置关联,而不是按名称关联。
如果动态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)
- 这只是避免了问题......