CLOB
当数据类型被用作传递给MERGE
语句ON
子句的值时,这似乎是 Oracle 中的一个错误。假设这个数据库:
CREATE TABLE t (
v INT,
s VARCHAR2(400 CHAR)
);
使用内联值进行复制
现在,在任何 Oracle 客户端(包括 SQL*Plus、SQL Developer 或 JDBC)中运行以下语句,这有助于非常轻松地重现问题(我使用的是 Oracle 11g XE 11.2.0.2.0):
MERGE INTO t
USING (
SELECT
1 v,
CAST('abc' AS CLOB) s
FROM DUAL
) s
ON (t.s = s.s) -- Using a CLOB here causes the bug.
WHEN MATCHED THEN UPDATE SET
t.v = s.v
WHEN NOT MATCHED THEN INSERT (v, s)
VALUES (s.v, s.s);
这个例子很愚蠢,CLOB
这里被“意外”束缚了。尽管如此,这样的语句不应该在 Oracle 中创建僵尸会话,但它确实存在。我在 SQL*Plus 中运行上述语句 3 次,然后运行这个......
SELECT
s.sid,
s.serial#,
s.sql_id,
s.event,
s.blocking_session,
q.sql_text
FROM v$session s
JOIN v$sql q
ON s.sql_id = q.sql_id
WHERE s.username = 'TEST'
AND UPPER(TRIM(q.sql_text)) LIKE 'MERGE%';
...我得到:
sid serial# sql_id event blocking_session
9 3 82a2k4sqzy1jq cursor: pin S wait on X 92
49 89 82a2k4sqzy1jq cursor: pin S wait on X 92
92 13 82a2k4sqzy1jq db file sequential read
请注意报告的事件(“db 文件顺序读取” )与使用绑定变量的原始事件(“来自客户端的 SQL*Net 消息” )有何不同
使用绑定值进行复制
var v_s varchar2(50)
exec :v_s := 'abc'
MERGE INTO t
USING (
SELECT
1 v,
CAST(:v_s AS CLOB) s
FROM DUAL
) s
ON (t.s = s.s) -- Using a CLOB here causes the bug.
WHEN MATCHED THEN UPDATE SET
t.v = s.v
WHEN NOT MATCHED THEN INSERT (v, s)
VALUES (s.v, s.s);
上面在 SQL*Plus 中运行的语句也会产生错误:
sid serial# sql_id event blocking_session
8 1 4w9zuxrumumgj SQL*Net message from client
90 7 4w9zuxrumumgj cursor: pin S wait on X 8
94 21 4w9zuxrumumgj cursor: pin S wait on X 8
在 PL/SQL 中没有复制
有趣的是,在以下 PL/SQL 语句中避免了该错误:
DECLARE
v_s CLOB := 'abc';
BEGIN
MERGE INTO t
USING (
SELECT
1 v,
CAST(v_s AS CLOB) s
FROM DUAL
) s
ON (t.s = s.s) -- Using a CLOB here causes the bug.
WHEN MATCHED THEN UPDATE SET
t.v = s.v
WHEN NOT MATCHED THEN INSERT (v, s)
VALUES (s.v, s.s);
END;
/
我越来越:
CAST(v_s AS CLOB) s
*
ERROR at line 8:
ORA-06550: line 8, column 11:
PL/SQL: ORA-00932: inconsistent datatypes: expected - got CLOB
ORA-06550: line 4, column 7:
PL/SQL: SQL Statement ignored
看起来 PL/SQL 引擎从这个 SQL 引擎错误中保护了客户端。