我是帮助了一些同事的矿SQL问题。主要是他们想要移动的所有行从表A至表B(两个表格的具有相同的列(姓名和类型)).虽然这样做是在Oracle11g我不认为它的真正问题。

他们最初的天真实的东西喜欢

BEGIN
  INSERT INTO B SELECT * FROM A
  DELETE FROM A
  COMMIT;
END

他们的关切是,如果有插入表一期间复制的,从A到B和",删除从"(或截断什么是值得)会造成数据丢失(具有较新插入的行中删除)。

当然,我迅速建议存放身份证的复制的行在临时的表,然后删除的仅仅是行匹配ID在临时表格。

然而,对于好奇心的缘故我们提出了一个小测试通过加入一个等待命令(不记得PL/SQL法)之间插入和删除。然后从一个不同方面,我们将插入行 在等待的.

我们观察到,这是一个数据丢失通过这样做。我再现整个上下文中SQL服务器和包裹它的所有交易但仍然发新的数据丢失了太在SQL服务器。这使我认为是有系统错误/缺陷在最初的方法。

但是我不能告诉我们,如果它是一个事实,即交易是不是(不知?) 隔离新插入或事实,即插入了在等待命令。

在结束,这是实现使用的临时表建议通过我但是我们不能得到的回答"为什么数据的损失"。你知道为什么吗?

有帮助吗?

解决方案

根据你的隔离水平,选择的所有行表不防止新插入,则将只是锁定的行阅读。在SQL服务器上,如果您使用的序列化的隔离水平,那么它将防止新的行如果他们已经包括在你选择的查询。

http://msdn.microsoft.com/en-us/library/ms173763.aspx -

序列化 指定如下:

  • 发言无法读取的数据,已被修改,但尚未致力于通过其他交易。

  • 没有任何其他交易可以修改数据,已经读通过的当前事务,直到当前的事务完成。

  • 其他交易中不能插入新的排与关键的价值观,将属范围内的钥匙读通过的任何陈述的当前事务,直到当前的事务完成。

其他提示

我不能谈论事务稳定性,但另一种方法是从存在的源表中删除第二步(从目标表中选择ID)。

原谅语法,我没有测试过这段代码,但你应该能够理解:

INSERT INTO B SELECT * FROM A;

DELETE FROM A WHERE EXISTS (SELECT B.<primarykey> FROM B WHERE B.<primarykey> = A.<primarykey>);

通过这种方式,您使用关系引擎强制执行不会删除任何新数据,并且您无需在事务中执行这两个步骤。

更新:更正了子查询中的语法

这可以通过以下方式在Oracle中实现:

Alter session set isolation_level=serializable;

可以使用EXECUTE IMMEDIATE:

在PL / SQL中设置
BEGIN
    EXECUTE IMMEDIATE 'Alter session set isolation_level=serializable';
    ...
END;

请参阅询问Tom:关于事务隔离级别

这只是交易的工作方式。您必须为手头的任务选择正确的隔离级别。

您正在同一个事务中执行INSERT和DELETE。您没有提到事务正在使用的隔离模式,但它可能是“已提交读取”。这意味着DELETE命令将查看同时提交的记录。对于这种工作,使用'快照'类型的事务要好得多,因为INSERT和DELETE都会知道同一组记录 - 只有那些而不是其他。

我不知道这是否相关,但在SQL Server中语法是

begin tran
....
commit

不只是'开始'

您需要设置事务隔离级别,以便其他事务的插入不会影响您的事务。我不知道如何在Oracle中这样做。

在Oracle中,默认事务隔离级别是读取提交的。这基本上意味着Oracle会在查询开始时返回SCN(系统更改号)中存在的结果。将事务隔离级别设置为可序列化意味着在事务开始时捕获SCN,以便事务中的所有查询都返回该SCN的数据。无论其他会话和事务正在做什么,这都可确保一致的结果。另一方面,由于其他事务正在执行的活动,Oracle可能会确定它无法序列化您的事务,因此您可能需要处理这类错误。

Tony与AskTom讨论的链接更详细地介绍了所有这些 - 我强烈推荐它。

是米兰,我没有指定事务隔离级别。我想这是默认的隔离级别,我不知道它是什么。无论是在Oracle 11g还是在SQL Server 2005中。

此外,在WAIT命令期间(在第二个连接上)进行的INSERT在事务中是 NOT 。应该是为了防止这种数据丢失吗?

这是默认读取提交模式的标准行为,如上所述。 WAIT命令只会导致处理延迟,没有任何数据库事务处理的链接。

要解决此问题,您可以:

  1. 将隔离级别设置为可序列化,但是您可以获得ORA-错误,您需要重试这些错误!此外,您可能会受到严重影响。
  2. 使用临时表来存储值
  3. 如果数据不是太大而无法容纳到内存中,则可以使用RETURNING子句将BULK COLLECT IN到嵌套表中,并仅在嵌套表中存在该行时才删除。

或者,您可以使用快照隔离来检测丢失的更新:

快照隔离帮助及何时受伤

    I have written a sample code:-

    First run this on Oracle DB:-


     Create table AccountBalance
        (
              id integer Primary Key,
              acctName varchar2(255) not null,
              acctBalance integer not null,
              bankName varchar2(255) not null
        );

        insert into AccountBalance values (1,'Test',50000,'Bank-a');

    Now run the below code 





 package com.java.transaction.dirtyread;
        import java.sql.Connection;
        import java.sql.DriverManager;
        import java.sql.SQLException;

        public class DirtyReadExample {

         /**
          * @param args
         * @throws ClassNotFoundException 
          * @throws SQLException 
          * @throws InterruptedException 
          */
         public static void main(String[] args) throws ClassNotFoundException, SQLException, InterruptedException {

             Class.forName("oracle.jdbc.driver.OracleDriver");
             Connection connectionPayment = DriverManager.getConnection(
                        "jdbc:oracle:thin:@localhost:1521:xe", "hr",
                        "hr");
             Connection connectionReader = DriverManager.getConnection(
                        "jdbc:oracle:thin:@localhost:1521:xe", "hr",
                        "hr");

          try {
              connectionPayment.setAutoCommit(false);
              connectionPayment.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);


          } catch (SQLException e) {
           e.printStackTrace();
          }


          Thread pymtThread=new Thread(new PaymentRunImpl(connectionPayment));
          Thread readerThread=new Thread(new ReaderRunImpl(connectionReader));

          pymtThread.start();
          Thread.sleep(2000);
          readerThread.start();

         }

        }



        package com.java.transaction.dirtyread;

        import java.sql.Connection;
        import java.sql.PreparedStatement;
        import java.sql.ResultSet;
        import java.sql.SQLException;

        public class ReaderRunImpl  implements Runnable{

         private Connection conn;

         private static final String QUERY="Select acctBalance from AccountBalance where id=1";

         public ReaderRunImpl(Connection conn){
          this.conn=conn;
         }

         @Override
         public void run() {
          PreparedStatement stmt =null; 
          ResultSet rs =null;

          try {
           stmt = conn.prepareStatement(QUERY);
           System.out.println("In Reader thread --->Statement Prepared");
           rs = stmt.executeQuery();
           System.out.println("In Reader thread --->executing");
           while (rs.next()){

            System.out.println("Balance is:" + rs.getDouble(1));

           }
           System.out.println("In Reader thread --->Statement Prepared");
           Thread.sleep(5000);
           stmt.close();
           rs.close();
           stmt = conn.prepareStatement(QUERY);
           rs = stmt.executeQuery();
           System.out.println("In Reader thread --->executing");
           while (rs.next()){

            System.out.println("Balance is:" + rs.getDouble(1));

           }
           stmt.close();
           rs.close();
           stmt = conn.prepareStatement(QUERY);
           rs = stmt.executeQuery();
           System.out.println("In Reader thread --->executing");
           while (rs.next()){

            System.out.println("Balance is:" + rs.getDouble(1));

           }
          } catch (SQLException | InterruptedException e) {
           e.printStackTrace();
          }finally{
           try {
            stmt.close();
            rs.close();
           } catch (SQLException e) {
            e.printStackTrace();
           }   
          }
         }

        }

        package com.java.transaction.dirtyread;
        import java.sql.Connection;
        import java.sql.PreparedStatement;
        import java.sql.SQLException;

        public class PaymentRunImpl implements Runnable{

         private Connection conn;

         private static final String QUERY1="Update AccountBalance set acctBalance=40000 where id=1";
         private static final String QUERY2="Update AccountBalance set acctBalance=30000 where id=1";
         private static final String QUERY3="Update AccountBalance set acctBalance=20000 where id=1";
         private static final String QUERY4="Update AccountBalance set acctBalance=10000 where id=1";

         public PaymentRunImpl(Connection conn){
          this.conn=conn;
         }

         @Override
         public void run() {
          PreparedStatement stmt = null;

          try {   
           stmt = conn.prepareStatement(QUERY1);
           stmt.execute();
           System.out.println("In Payment thread --> executed");
           Thread.sleep(3000);
           stmt = conn.prepareStatement(QUERY2);
           stmt.execute();
           System.out.println("In Payment thread --> executed");
           Thread.sleep(3000);
           stmt = conn.prepareStatement(QUERY3);
           stmt.execute();
           System.out.println("In Payment thread --> executed");
           stmt = conn.prepareStatement(QUERY4);
           stmt.execute();
           System.out.println("In Payment thread --> executed");

           Thread.sleep(5000);
            //case 1
           conn.rollback();
           System.out.println("In Payment thread --> rollback");
          //case 2
           //conn.commit();
          // System.out.println("In Payment thread --> commit");
          } catch (SQLException e) {
           e.printStackTrace();
          } catch (InterruptedException e) {    
           e.printStackTrace();
          }finally{
           try {
            stmt.close();
           } catch (SQLException e) {
            e.printStackTrace();
           }
          }
         }

        }

    Output:-
    In Payment thread --> executed
    In Reader thread --->Statement Prepared
    In Reader thread --->executing
    Balance is:50000.0
    In Reader thread --->Statement Prepared
    In Payment thread --> executed
    In Payment thread --> executed
    In Payment thread --> executed
    In Reader thread --->executing
    Balance is:50000.0
    In Reader thread --->executing
    Balance is:50000.0
    In Payment thread --> rollback

你可以通过插入oracle定义的新行来测试它: -  当事务A检索满足给定条件的一组行时,发生虚拟读取,事务B随后插入或更新行,使得该行现在满足事务A中的条件,并且事务A稍后重复条件检索。事务A现在看到一个额外的行。这一行被称为幻像。它将避免上述场景以及我使用TRANSACTION_SERIALIZABLE。它将对Oracle设置最严格的锁定。 Oracle仅支持2种类型的事务隔离级别: - TRANSACTION_READ_COMMITTED和TRANSACTION_SERIALIZABLE。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top