2014-05-12 19 views
2

我試圖運行表Person一個UPSERT這樣的:如何在匿名PL/SQL塊中使用重複佔位符名稱?

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; 
/

如果我運行代碼上面給出,甲骨文拋出一個ORA-01008: not all variables bound ORA-06512: at line 2

按照docs,這是不應該發生在這一點(在匿名塊內)。我究竟做錯了什麼?

我知道我可以解決這個問題,如果我提供四個參數… USING 123, 'Foo', 'Bar', 123;,這工作得很好,但顯然我不想重複自己。編輯:正如答案中指出的那樣,ON (p.id = data.id)是另一種可能的解決方法。

問題不在於如何解決這個問題,更多的是在這種情況下了解造成這種錯誤的原因。

+0

您的評論是指'佔位符:1';你真的使用位置或命名綁定變量? (這個例子有多麼人爲 - 你爲什麼不做'ON(p.id = data.id)'?) –

+0

@AlexPoole我修復了評論。我試過':1,:2,:3'和':x,:y,:z',都會產生相同的錯誤。 – user123444555621

回答

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語句。

+0

非常好的答案。 – Hogan

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) - 這只是避免問題...

+0

創建過程的更簡單的替代方法是將「begin」和「end」添加到動態SQL中,這將使其成爲匿名塊。 – Allan

+0

@Allan - 是的,我剛剛添加了這個選項* 8-)呃,抱歉,由於某種原因我沒有看到您的答案......至少在編輯完成後。 –

相關問題