最近我遇到了一个奇怪的问题,在Oracle数据库中编程:在可序列化的事务中,我做了一个批量插入(INSERT ... SELECT),之后立即在改变的表格上用SELECT键打开游标。我认为这个游标会包含新插入的行,但令我惊讶的是,它的内容不稳定,有时包括所有新插入的行,有时只包含一个子集。Oracle:在可序列化事务中插入后立即选择
我已经通过在打开游标之前提交来解决了这个问题,但行为让我感到困惑。在同一个事务中插入一个insert后,如果没有间接提交,实际上可以被信任吗?或者,这种行为是否与可序列化的事务相关?我试图创建一个可重复的测试用例时,只有在添加索引(本例中为主键索引,在实际代码中为普通索引)时才能够获得此行为。也许问题在于构建索引所花费的时间,以便SELECT实际上使用不完整的索引来检索结果?无论如何,这里有一个可重复的测试用例:
-- Create empty source table
CREATE TABLE TEST_CASE_1 AS
(SELECT 'CONTENT' AS CONTENT
FROM DUAL
WHERE 1 = 2)
-- Add primary key
ALTER TABLE TEST_CASE_1
ADD CONSTRAINT TEST_CASE_1_PK PRIMARY KEY (CONTENT);
-- Create empty destination table
CREATE TABLE TEST_CASE_2 AS
(SELECT 'CONTENT' AS CONTENT
FROM DUAL
WHERE 1 = 2)
-- Example of faulty code
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- Populate with 100.000 rows (I used ALL_OBJECTS but any source of 100.000 rows is good)
INSERT INTO TEST_CASE_1
(SELECT ROWNUM
FROM ALL_OBJECTS
WHERE ROWNUM <= 100000);
INSERT INTO TEST_CASE_2
(SELECT *
FROM TEST_CASE_1
WHERE CONTENT > 0);
COMMIT;
END;
在这个例子中,我希望TEST_CASE_2也有100.000行。重新生成这个测试用例(在一个无负载的数据库中),我获得了约400-500行插入。除去将事务设置为可序列化的语句,我获得了正确的100.000行计数。
您是否在插入操作时在相同的数据库会话中打开游标?你在哪里做这项工作 - 实际上是在数据库还是从客户端应用程序 - 如果后者你有连接池,并且你(有时)为这两个操作获得不同的连接? – 2012-08-06 11:17:16
游标正在同一会话中打开。最初游标在数据库中打开,然后在.NET可执行文件中迭代,但为了隔离问题,我制作了一个在数据库中迭代游标的过程版本,问题依然存在 - 事实上,在执行此操作之前,不要说服自己,问题出现在插入后的选择中。 – user1578874 2012-08-06 11:37:41
这不应该发生;来自[文档](http://docs.oracle.com/cd/E11882_01/server.112/e25789/consist.htm#sthref1189)'在序列化隔离级别,事务仅查看在事务时提交的更改 - 不是查询 - 由交易本身开始并进行更改“,因此您应该看到自己的更改。我猜可能是你的特定版本中的一个错误。承诺使得改变隔离级别有点毫无意义。你能发布一个可重复的测试用例吗? – 2012-08-06 17:20:04