首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 数据库 > 其他数据库 >

(转)在Oracle 数据库中的临时表用法集锦

2013-03-14 
(转)在Oracle 数据库中的临时表用法汇总?????????????????? System.out.println(-----Room Address: sho

(转)在Oracle 数据库中的临时表用法汇总

?????????????????? System.out.println("-----Room Address:" showRoom.getAddress());?

?????????????????? System.out.println("-----Room Number:" showRoom.getNumber());?

???????? }?

}?

在这段代码中,M实现了三个主要的DAO方法:?

(1)createTable(),此方法用以创建临时表temproom?

(2)insertRoom(),此方法用以为临时表temproom插入一条记录?

(3)findRoom(),此方法用以取得insertRoom()方法所插入的一条,将以Room实体返回。?

此外,为了简单起见,M给出了getConnection()方法,三个DAO的主要方法都将调用getConnection()来取得数据库的JDBC连接。?

很快代码实现了,于是M又构造了main()方法来对实现结果做测试。当M满心欢喜的以为结果将如他所料时,一个意想不到的情况发生了。这段看起来完全正确的代码居然抛出了异常:?

java.sql.SQLException: ERROR: relation "temproom" does not exist?

这是怎么一回事呢??

5.4.2 理解临时表M的代码如果用在与非临时表的表交互时自然没有错,但是用在临时表上显然就错了。原因就出在临时表上。要解决这个“无法显示的数据”问题,就必须搞清楚什么是临时表。?

绝大多数关系型数据库都有临时表,这在SQL-92中也是一个标准。临时表的特性在于分布式运用,也即任何一个用户连接到数据库,即使使用的是同名的临时表,这个用户的所有操作也对另一连接的用户不可见。换句话说,就是“临时表多用户并行不是问题”。?

在标准的SQL-92中,临时表的定义是这样的:?

(1)使用CREATE TEMPORARY TABLE…定义临时表。?

(2)定义临时表的结尾部分可以存在ON COMMIT DELETE ROWS子句或ON COMMIT PRESERVE ROWS子句。?

(3)若缺省ON COMMIT子句的情况下,将使用ON COMMIT DELETE ROWS子句所提供的行为。?

通过ON COMMIT DELETE ROWS子句定义的临时表它的特性在于:“临时表的所有数据将在一次事务提交后被全部删除”?

通过ON COMMIT PRESERVE ROWS子句定义的临时表它的特性在于:“临时表的所有数据在一次事务提交后将依旧保留”?

但是无论使用哪种ON COMMIT子句定义的临时表,它在一次数据库连接结束后都将被删除所有数据。?

请注意:一次数据库连接和一次事务提交是两个概念,前者读者可以简单的理解为Connection连接的关闭,也即Java中“connection.close()”方法的调用;后者读者可以理解为Connection连接中的事务提交,也即Java中“connection.commit()”方法的调用。?

每一种数据库对于临时表的定义都存在着兼容性的问题,在SQL-92编码规则中临时表创建后即使连接结束也不会被drop(不是DELETE)掉,符合这个标准的数据库具有代表性的就是Oracle,但是有些数据库则定义临时表在连接结束后将连同整个表都会被drop掉,PostgreSQL就是其中的一种,MySQL也是如此。因此若要使用临时表,则必须在项目启动后对该项目所使用的数据库文档进行必要的了解。?

由于临时表的先天特性(多用户并行无关性),在项目中使用临时表是很常见的。?

如何使用临时表?(二) 分类:我的著作2008.1.3 14:31 作者:小仙狗 | 评论:0 | 阅读:660?
5.4.3 查找问题在理解了临时表的相关特性后,不难看出 M的代码所存在的问题。?

(1)首先,L要求M使用JDBC来创建临时表的Schema是没有问题的,由于PostgreSQL每次连接结束都将drop临时表,因此必须手动创建临时表(调用createTable())。?

(2)M在这段代码中最大的问题在于getConnection(),在例5.18中可以看到任何一个DAO方法都会调用getConnection(),而每个DAO方法的finally部分又会关闭Connection。这样的话,客户端在调用createTable()方法结束后已经关闭了数据库连接。按照临时表的特性,此时临时表中的数据已经被自动删除了。?

5.4.4 提供一个ConnectionManager问题(2)是整段代码引起错误的主要原因,解决这个问题有多种方案。最容易想到的就是整个DAO全局共享一个Connection,可是如果就简单的提供一个单例类是有问题的。?

(1)因为临时表本身的特性虽然是多用户并行无关性,但是这个无关性的前提是每个用户一个连接。假设提供一个单例类,那么在整个运行期所有客户端都将使用这个Connection,如此的结果必然导致多个客户共用一个Connection。?

(2)对于Connection连接提供单例类,必然导致一个长连接不被释放,对于任何一个系统来说这都是无法接受的。?

有鉴于此,单例类的实现被否定了。深入的再想一下,不难发现,其实对于临时表的操作需要的是以下两个条件:?

(1)提供一种方式,让多个操作临时表的方法共享一个连接。?

(2)而这样一个连接对于任何请求都将是独立的。?

假设仅以J2EE模型来说,这是很容易实现的。因为从Servlet请求到来的每一个客户端都只发生在自己的线程中。这就给实现两个条件带来了契机,只需要利用Java的ThreadLocal类。?

提供一个ConnectionManager类,该类将使用ThreadLocal类来管理Connection连接,以保证该Connection连接对于一次请求的线程是独立的。请见例5.19:?

例5.19:ConnectionManager.java?

package dao.jdbc;?



import java.sql.Connection;?



public class ConnectionManager {?

???????? //静态变量"当前线程",用以管理Connection?

???????? private static final ThreadLocal currentConnection = new ThreadLocal();?



???????? //静态方法取得"当前线程"所使用的Connection?

???????? public static Connection getConnection() {?

?????????????????? return (Connection)currentConnection.get();?

???????? }?



???????? //将"当前线程"与"当前连接"绑定?

???????? static Connection setCurrentConnection(Connection connection) {?

?????????????????? Connection priorConnection = (Connection)currentConnection.get();?

?????????????????? currentConnection.set(connection);?

?????????????????? return priorConnection;?

???????? }?



}?

客户端只需要在每次请求到来时取得一个Connection连接,调用setCurrentConnection()方法,将Connection连接与当前线程绑定,而DAO中的每个方法都调用getConnection()方法来获取当前线程绑定的Connection连接,在DAO的每个方法中finally时不应该关闭Connection连接,将关闭的动作交给客户端处理。?

5.4.5 不能被忽略的ON COMMIT DELETE ROWS仅利用ConnectionManager.java还是不能完全结束工作,因为临时表的ON COMMIT DELETE ROWS子句的本意是“临时表的所有数据将在一次事务提交后被全部删除”。?

在JDBC的Connection连接中事务本身是被设置为AutoCommit的,这意味着要想在“创表->插表->查表”三个动作结束后才提交事务,那势必要设置AutoCommit为false。否则在第二个动作“插表”的行为结束时事务就已经提交了,即使Connection连接依然保持,但临时表还是会将所有数据在这次事务提交后全部删除。正确的客户端操作如下:?

//取得绑定的连接?

Connection con = ConnectionManager.getConnection();?

//设置AutoCommit为false?

con.setAutoCommit(false);?

//实现DAO方法中与临时表相关的操作?

…?

//提交事务?

con.commit();?

//还原AutoCommit?

con.setAutoCommit(true);?

以上可以完全操控临时表了。?

5.4.6 被改写的完整代码以下将对M的代码进行改写,请读者注意该段代码中加粗的部分。请见例5.20:?

例5.20:TestTempTableDAO.java?

package dao.jdbc;?



import java.sql.Connection;?

import java.sql.DriverManager;?

import java.sql.PreparedStatement;?

import java.sql.ResultSet;?

import java.sql.SQLException;?



import entity.Room;?



public class TestTempTableDAO {?

???????? // 针对temproom表的操作?

???????? private final String CREAT_ROOM_TABLE = "CREATE GLOBAL TEMP TABLE temproom"?

??????????????????????????? "( id int8 NOT NULL,? "?

??????????????????????????? "address varchar(255),? "?

??????????????????????????? "number varchar,? "?

??????????????????????????? "CONSTRAINT room_pkey PRIMARY KEY (id)) "?

??????????????????????????? "ON COMMIT DELETE ROWS";?



???????? private final String INSERT_ROOM_TABLE = "insert into temproom (id, address, number) values (?,?,?)";?



???????? private final String FIND_ROOM_BY_KEY = "select * from temproom where id=?";?



???????? public void createTable() {?

?????????????????? // 提供一个连接?

?????????????????? Connection con = null;?

?????????????????? // 提供一个创建预编译SQL 语句的变量?

?????????????????? PreparedStatement ps = null;?



?????????????????? try {?

????????????? //取得绑定的连接?

??????????????????????????? con = ConnectionManager.getConnection();?

??????????????????????????? ps = con.prepareStatement(this.CREAT_ROOM_TABLE);?

??????????????????????????? ps.executeUpdate();?

?????????????????? } catch (SQLException ex) {?

??????????????????????????? ex.printStackTrace();?

?????????????????? } finally {?

??????????????????????????? // 必须进行的处理,关闭PreparedStatement、ResultSet?

??????????????????????????? try {?

???????????????????????????????????? if (ps != null)?

?????????????????????????????????????????????? ps.close();?

???????????????????????????????????? // 请注意不用关闭Connection,否则无法实现连接的传递?

???????????????????????????????????? /*?

????????????????????????????????????? * if (con != null) con.close();?

????????????????????????????????????? */?

??????????????????????????? } catch (SQLException ex) {?

???????????????????????????????????? ex.printStackTrace();?

??????????????????????????? }?

?????????????????? }?

???????? }?



???????? public void insertRoom(Room room) throws Exception {?

?????????????????? // 提供一个连接?

?????????????????? Connection con = null;?

?????????????????? // 提供一个创建预编译SQL 语句的变量?

?????????????????? PreparedStatement ps = null;?

?????????????????? // 提供一个返回SQL查询结果的ResultSet接口变量?

?????????????????? // ResultSet带有游标可以指向返回结果中的某条记录?

?????????????????? ResultSet rs = null;?



?????????????????? try {?

????????????? //取得绑定的连接?

??????????????????????????? con = ConnectionManager.getConnection();?

??????????????????????????? // 预编译SQL语句并执行insertSql?

??????????????????????????? ps = con.prepareStatement(this.INSERT_ROOM_TABLE);?

??????????????????????????? ps.setLong(1, room.getId());?

??????????????????????????? ps.setString(2, room.getAddress());?

??????????????????????????? ps.setString(3, room.getNumber());?

??????????????????????????? // 若新增失败?

??????????????????????????? if (ps.executeUpdate() != 1) {?

???????????????????????????????????? throw new Exception("更新失败");?

??????????????????????????? }?

?????????????????? } catch (SQLException ex) {?

??????????????????????????? ex.printStackTrace();?

?????????????????? } finally {?

??????????????????????????? // 必须进行的处理,关闭PreparedStatement、ResultSet?

??????????????????????????? try {?

???????????????????????????????????? if (rs != null)?

?????????????????????????????????????????????? rs.close();?

???????????????????????????????????? if (ps != null)?

?????????????????????????????????????????????? ps.close();?

???????????????????????????????????? // 请注意不用关闭Connection,否则无法实现连接的传递?

???????????????????????????????????? /*?

????????????????????????????????????? * if (con != null) con.close();?

????????????????????????????????????? */?

??????????????????????????? } catch (SQLException ex) {?

???????????????????????????????????? ex.printStackTrace();?

??????????????????????????? }?

?????????????????? }?

???????? }?



???????? /**?

????????? * 根据Room表的主键返回Room实体?

????????? */?

???????? public Room findRoom(Long id) {?

?????????????????? // 提供一个连接?

?????????????????? Connection con = null;?

?????????????????? // 提供一个创建预编译SQL 语句的变量?

?????????????????? PreparedStatement ps = null;?

?????????????????? // 提供一个返回SQL查询结果的ResultSet接口变量?

?????????????????? // ResultSet带有游标可以指向返回结果中的某条记录?

?????????????????? ResultSet rs = null;?

?????????????????? // 提供一个Room实体的变量?

?????????????????? Room room = null;?

???????????????????

?????????????????? try {?

????????????? //取得绑定的连接?

??????????????????????????? con = ConnectionManager.getConnection();?



??????????????????????????? // 预编译SQL语句并执行findSql?

??????????????????????????? ps = con.prepareStatement(this.FIND_ROOM_BY_KEY);?

??????????????????????????? ps.setLong(1, id);?

??????????????????????????? rs = ps.executeQuery();?

??????????????????????????? // 当返回结果集中无记录时返回null?

??????????????????????????? if (!rs.next()) {?

???????????????????????????????????? return null;?

??????????????????????????? }?

??????????????????????????? // 以下的情况将保证在结果集中有记录时的应用?

??????????????????????????? // 创建Room实体的实例以作处理?

??????????????????????????? room = new Room();?

??????????????????????????? room.setId(rs.getLong("id"));?

??????????????????????????? room.setAddress(rs.getString("address"));?

??????????????????????????? room.setNumber(rs.getString("number"));?

?????????????????? } catch (SQLException ex) {?

??????????????????????????? ex.printStackTrace();?

?????????????????? } finally {?

??????????????????????????? // 必须进行的处理,关闭PreparedStatement、ResultSet、Connection?

??????????????????????????? try {?

???????????????????????????????????? if (rs != null)?

?????????????????????????????????????????????? rs.close();?

???????????????????????????????????? if (ps != null)?

?????????????????????????????????????????????? ps.close();?

???????????????????????????????????? // 请注意不用关闭Connection,否则无法实现连接的传递?

???????????????????????????????????? /*?

????????????????????????????????????? * if (con != null) con.close();?

????????????????????????????????????? */?

??????????????????????????? } catch (SQLException ex) {?

???????????????????????????????????? ex.printStackTrace();?

??????????????????????????? }?

?????????????????? }?

?????????????????? return room;?

???????? }?



???????? public static void main(String[] args) {?



?????????????????? try {?

??????????????????????????? //绑定连接的代码不应该存在于这里,但是为了测试方便依然放在了这里?

??????????????????????????? final String url = "jdbc:postgresql://localhost/TESTDB";?

??????????????????????????? final String user = "sa";?

??????????????????????????? final String password = "1111";?

??????????????????????????? Class.forName("org.postgresql.Driver");?

??????????????????????????? Connection connection = DriverManager.getConnection(url, user,?

?????????????????????????????????????????????? password);?

??????????????????????????? //设置AutoCommit为false?

??????????????????????????? connection.setAutoCommit(false);?

??????????????????????????? //绑定连接?

??????????????????????????? ConnectionManager.setCurrentConnection(connection);?

????????????????????????????

??????????????????????????? TestTempTableDAO testTempTableDAO = new TestTempTableDAO();?

??????????????????????????? // 创建表temproom?

??????????????????????????? testTempTableDAO.createTable();?



??????????????????????????? // 新建Room实体?

??????????????????????????? Room room = new Room();?

??????????????????????????? room.setId(1L);?

??????????????????????????? room.setNumber("001");?

??????????????????????????? room.setAddress("RW Room");?

??????????????????????????? // 插表temproom?

??????????????????????????? testTempTableDAO.insertRoom(room);?



??????????????????????????? // 显示结果?

??????????????????????????? Room showRoom = new Room();?

??????????????????????????? showRoom = testTempTableDAO.findRoom(1L);?

????????????????????????????

??????????????????????????? //提交事务?

??????????????????????????? connection.commit();?

??????????????????????????? //还原AutoCommit?

??????????????????????????? connection.setAutoCommit(true);?

??????????????????????????? System.out.println("-----Room id:" showRoom.getId());?

??????????????????????????? System.out.println("-----Room Address:" showRoom.getAddress());?

??????????????????????????? System.out.println("-----Room Number:" showRoom.getNumber());?

?????????????????? } catch (ClassNotFoundException e) {?

??????????????????????????? e.printStackTrace();?

?????????????????? } catch (Exception ex) {?

??????????????????????????? ex.printStackTrace();?

?????????????????? }?

???????? }?

}?

1 楼?dolphin_ygj?2009-04-13???引用Oracle临时表 优化查询速度?
1、前言?
??? 目前所有使用Oracle作为数据库支撑平台的应用,大部分数据量比较庞大的系统,即表的数据量一般情况下都是在百万级以上的数据量。当然在Oracle中创建分区是一种不错的选择,但是当你发现你的应用有多张表关联的时候,并且这些表大部分都是比较庞大,而你关联的时候发现其中的某一张或者某几张表关联之后得到的结果集非常小并且查询得到这个结果集的速度非常快,那么这个时候我考虑在Oracle中创建“临时表”。?
??? 我对临时表的理解:在Oracle中创建一张表,这个表不用于其他的什么功能,主要用于自己的软件系统一些特有功能才用的,而当你用完之后表中的数据就没用了。Oracle的临时表创建之后基本不占用表空间,如果你没有指定临时表(包括临时表的索引)存放的表空的时候,你插入到临时表的数据是存放在ORACLE系统的临时表空间中(TEMP)。?
2、临时表的创建?
??? 创建Oracle临时表,可以有两种类型的临时表:会话级的临时表和事务级的临时表。?


??? 1)会话级的临时表因为这这个临时表中的数据和你的当前会话有关系,当你当前SESSION不退出的情况下,临时表中的数据就还存在,而当你退出当前SESSION的时候,临时表中的数据就全部没有了,当然这个时候你如果以另外一个SESSION登陆的时候是看不到另外一个SESSION中插入到临时表中的数据的。即两个不同的SESSION所插入的数据是互不相干的。?

当某一个SESSION退出之后临时表中的数据就被截断(truncate table,即数据清空)了。?



会话级的临时表创建方法:?

Create Global Temporary Table Table_Name(Col1 Type1,Col2 Type2...) On Commit Preserve Rows;举例create global temporary table Student(Stu_id Number(5),Class_id? Number(5),Stu_Name Varchar2(8),Stu_Memo varchar2(200)) on Commit Preserve Rows ;?


??? 2)事务级临时表是指该临时表与事务相关,当进行事务提交或者事务回滚的时候,临时表中的数据将自行被截断,其他的内容和会话级的临时表的一致(包括退出SESSION的时候,事务级的临时表也会被自动截断)。?



事务级临时表的创建方法:?

Create Global Temporary Table Table_Name(Col1 Type1,Col2 Type2...) On Commit Delete Rows;?



举例:?

create global temporary table Classes(Class_id Number(5),Class_Name Varchar2(8),Class_Memo varchar2(200)) on Commit delete Rows ;?


??? 3)、两种不通类型的临时表的区别:?

??????????? 语法上,会话级临时表采用on commit preserve rows而事务级则采用on commit delete rows;?

??????????? 用法上,会话级别只有当会话结束临时表中的数据才会被截断,而且事务级临时表则不管是commit、rollback或者是会话结束,临时表中的数据都将被截断。?


3、例子:?

??? 1)、会话级(Session关闭掉之后数据就没有了,当Commit的时候则数据还在,当Rollback的时候则数据也是一样被回滚):?
???? insert into student(stu_id,class_id,stu_name,stu_memo) values(1,1,'张三','福建');?
???? insert into student(stu_id,class_id,stu_name,stu_memo) values(2,1,'刘德华','福州');?
???? insert into student(stu_id,class_id,stu_name,stu_memo) values(3,2,'S.H.E','厦门');?
SQL> select *from student ;?

STU_ID CLASS_ID STU_NAME STU_MEMO?
------ -------- -------- --------------------------------------------?
???? 1??????? 1 张三???? 福建?
???? 2??????? 1 刘德华?? 福州?
???? 3??????? 2 S.H.E??? 厦门?
???? 4??????? 2 张惠妹?? 厦门?

SQL> commit;?

Commit complete?

SQL> select * from student ;?

STU_ID CLASS_ID STU_NAME STU_MEMO?
------ -------- -------- --------------------------------------------?
???? 1??????? 1 张三???? 福建?
???? 2??????? 1 刘德华?? 福州?
???? 3??????? 2 S.H.E??? 厦门?
???? 4??????? 2 张惠妹?? 厦门?

SQL>insert into student(stu_id,class_id,stu_name,stu_memo) values(4,2,'张惠妹','厦门');?

1 row inserted?

SQL> select * from student ;?

STU_ID CLASS_ID STU_NAME STU_MEMO?
------ -------- -------- --------------------------------------------?
???? 1?????   1 张三???? 福建?
???? 2??????? 1 刘德华?? 福州?
???? 3??????? 2 S.H.E??? 厦门?
???? 4??????? 2 张惠妹?? 厦门?
???? 4??????? 2 张惠妹?? 厦门?



SQL> rollback ;?

Rollback complete?

SQL> select * from student ;?

STU_ID CLASS_ID STU_NAME STU_MEMO?
------ -------- -------- --------------------------------------------?
???? 1??????? 1 张三???? 福建?
???? 2??????? 1 刘德华?? 福州?
???? 3??????? 2 S.H.E??? 厦门?
???? 4??????? 2 张惠妹?? 厦门?

SQL>?
??? 2)、事务级(Commit之后就删除数据):本例子将采用以下的数据:?
????? insert into classes(Class_id,Class_Name,Class_Memo) values(1,'计算机','9608');?
????? insert into classes(Class_id,Class_Name,Class_Memo) values(2,'经济信息','9602');?
????? insert into classes(Class_id,Class_Name,Class_Memo) values(3,'经济信息','9603');?
? 在一个SESSION中(比如SQLPLUS登陆)插入上面3条记录,然后再以另外一个SESSION(用SQLPLUS再登陆一次)登陆,当你select * from classes;的时候,classes表是空的,而你再第一次登陆的SQLPLUS中select的时候可以查询到,这个时候你没有进行commit或者rollback之前你可以对刚才插入的3条记录进行update、delete等操作,当你进行commit或者rollback的时候,这个时候由于你的表是事务级的临时表,那么在插入数据的session也看不到数据了,这个时候数据就已经被截断了。?
???? 运行结果如下:?
SQL> insert into classes(Class_id,Class_Name,Class_Memo) values(1,'计算机','9608');?

1 row inserted?

SQL> insert into classes(Class_id,Class_Name,Class_Memo) values(2,'经济信息','9602');?

1 row inserted?

SQL> insert into classes(Class_id,Class_Name,Class_Memo) values(3,'经济信息','9603');?

1 row inserted?

SQL> update classes set class_memo ='' where class_id=3 ;?

1 row updated?

SQL> select * from classes ;?

CLASS_ID CLASS_NAME CLASS_MEMO?
-------- ---------- --------------------------------------------?
?????? 1 计算机???? 9608?
?????? 2 经济信息?? 9602?
?????? 3 经济信息???

SQL> delete from classes where class_id=3 ;?

1 row deleted?

SQL> select * from classes ;?

CLASS_ID CLASS_NAME CLASS_MEMO?
-------- ---------- --------------------------------------------?
?????? 1 计算机???? 9608?
?????? 2 经济信息?? 9602?
SQL> commit;?

Commit complete?

SQL> select *from classes ;?

CLASS_ID CLASS_NAME CLASS_MEMO?
-------- ---------- --------------------------------------------?

SQL>?
再重复插入一次,然后rollback。?
SQL> Rollback ;?

Rollback complete?

SQL> select * from classes ;?

CLASS_ID CLASS_NAME CLASS_MEMO?
-------- ---------- --------------------------------------------?

SQL>?

4、临时表的应用?
??? 1)、当某一个SQL语句关联的表在2张及以上,并且和一些小表关联。可以采用将大表进行分拆并且得到比较小的结果集合存放在临时表中。?
??? 2)、程序执行过程中可能需要存放一些临时的数据,这些数据在整个程序的会话过程中都需要用的等等。?
5、注意事项:?
??? 1)、临时表的索引以及对表的修改、删除等和正常的表是一致的。?
??? 2)、Oracle的临时表是Oracle8i才支持的功能特性,如果你的Oracle版本比较低的话,那么就可能没有办法用到了,如果你的Oracle版本是8i的话,你还需要把$ORACLE_HOME/admin/${ORACLE_SID}/pfile目录下的init<ORACLE_SID>.ora初始参数配置文件的compatible修改为compatible = "8.1.0",我的服务器上就是这样子配置的。当然也可以修改为compatible = "8.1.6"?

以上是我在对大表进行优化的时候采用的一些手段,效果显著。?

热点排行