Posted on 2010-05-13 23:14
Prayer 阅读(512)
评论(0) 编辑 收藏 引用 所属分类:
数据库,SQL 、
DB2
系统临时表的使用:
在一些程序中,我们需要一些仅仅在程序中使用的临时表,但是程序结束后就不再使用,数据库系统对这些应用提供系统临时表,关于系统临时表的生命存在周期和临时表的数据存在周期说明一下:
1、生命存在周期:
A、理论上系统临时表是不需要显示的drop的,它是基于会话的,当系统临时表基于的连接关闭的时候,系统临时表将结束它的生命,这是最普通也是最常用的。
B、当一个被调用对象的返回值是一个在临时表上执行的结果集,这个被调用对象执行完毕的时候,是关闭连接的,但是,这时候临时表是不消失的,这时候系统临时表在调用者结束的时候才消失。因为返回的结果集只是一个相当于指针的东西,指向临时表内存中的地址,指向临时表的指针还在使用的时候,临时表是不能drop掉的。这带来了很复杂的问题,首先:存储过程中是不能drop临时表的,而程序中没有创建临时表,也应该是不drop的,临时表什么时间drop呢?其次:我们在一个连接中,无法2次调用一个存储过程,他将告诉你临时表已经存在。可以做一个简单的例子:写一个带有临时表返回结果的存储过程,在调用存储存储过程的程序中,我们完全可以访问这个临时表。
简单例子:
存储过程
/**
* JDBC 存储过程 ADMINISTRATOR.P1
*/
import java.sql.*; // JDBC 类
public class P1
{
public static void p1 ( ResultSet[] rs ) throws SQLException, Exception
{
// 获取与数据库的连接
Connection con = DriverManager.getConnection("jdbc:default:connection");
PreparedStatement stmt = null;
String sql;
sql = "declare global temporary table session.temp(cc char(5)) not logged";
stmt = con.prepareStatement( sql );
stmt.executeUpdate();
sql = "insert into session.temp values ('1'),('2')";
stmt = con.prepareStatement( sql );
stmt.executeUpdate();
sql = "SELECT * from session.temp";
stmt = con.prepareStatement( sql );
rs[0] = stmt.executeQuery();
//关闭连接(注意,这个地方在rs[0]为临时表的结果集返回的时候,stmt是无法关闭的,临时表是无法drop的,而con是可以关闭的,关闭后临时表还存在)
if (con != null) con.close();
}
}
客户端调用我直接用命令行调用的。
db2 =>; connect to sample
数据库连接信息
数据库服务器 = DB2/NT 7.2.1
SQL 授权标识 = ADMINIST...
本地数据库别名 = SAMPLE
//将自动提交设为false。
db2 =>; update command options using c off
DB20000I UPDATE COMMAND OPTIONS 命令成功完成。
//调用过程
db2 =>; call p1()
CC
1
2
"P1" RETURN_STATUS:"0"
//看看存储过程中使用的临时表,数据是可以看到的。
db2 =>; select * from session.temp
CC
-----
1
2
2 条记录已选择。
//一个连接中再次调用,失败了
db2 =>; call p1()
SQL0601N 要创建的对象名与类型为"DECLARED TEMPORARY TABLE" 的现存名称 "SESSION.TEMP" 相同。 SQLSTATE=42710
//drop一下?ok,可以的
db2 =>; drop table session.temp
DB20000I SQL 命令成功完成。
//看看还有吗?没了!
db2 =>; select * from session.temp
SQL0204N "SESSION.TEMP" 是未定义的名称。 SQLSTATE=42704
//再次调用,成功了
db2 =>; call p1()
CC
1
2
"P1" RETURN_STATUS:"0"
C、Websphere上的程序中使用系统临时表:因为websphere的连接使用连接池的技术,这带来了好处,但是同时也带来了一些让人容易误解的地方,我们在程序中要关闭连接,很多时候看上去是关闭了数据库的连接,事实上也是这样的,但是当websphere的连接数在websphere连接池规定的连接数的范围之内的时候,程序中关闭连接是不能直接关闭数据库的连接的,连接池使连接继续保持,这时候,我们的关闭连接释放的是该连接和相关因素在websphere和java程序中使用的资源,而该连接使用的数据库资源是无法得到释放的。也就是说,当我们在该连接上使用临时表的时候,我们在程序中关闭了连接,但是临时表是还存在的,连接池中把这个连接分配给其他程序使用的时候,其他程序还可以使用这个临时表,这并不是我们想要的。这要求我们在程序的中显式的drop掉临时表。(这是在南宁解决系统临时表使用问题中,碰到的一个问题,大家可以很容易的模拟出来)
同时这也让我们注意,一些设置,应该采取人为控制的方式,而不要采取默认,比如autoCommit,我们不能觉得程序结束了,就提交了,因为默认是程序结束提交的,如果错了就都回滚了,但是如果连接上设定的是autoCommit为true的话,程序出错就只能回滚你的出错前最后一次未提交事务,也许你还在为你程序中出现的这种错误头疼,他为什么前面的会提交呢?
一段代码,应尽可能的保持它的独立性,执行不要过多依赖于环境和其他的代码,我想这是应该考虑的。就象在oracle数据库上执行sql和在db2数据库上执行sql有很明显的区别一样,oracle默认是不提交的,而db2默认是提交的,这样在不主动控制事务的情况下,一个sql执行的结果是完全不同的。
2、数据存在周期
临时表的数据是在内存中的,当你向一个临时表插入数据的时候,他同时是直接的写到硬盘中的,如果你的缓冲池可以满足临时表的数据都存在内存中,它在使用的时候是不需要读硬盘上的数据的,除非你的缓冲池不能满足,这样会降低临时表的性能。我们知道,事务的提交和回滚是对数据库的更改做永久化,从内存中的更改到硬盘上的更改或者放弃更改(在更改实现的同时,是回收曾经占用的内存资源的)。对一个永久表,插入数据,就是在你提交之前,别的程序访问不了你插入的数据,在你提交之后,所有的程序都可以访问你插入的数据;而临时表不是这样的,无论执行提交还是回滚,临时表数据占用的内存资源都将被释放,同时临时表写到硬盘上的数据也全部删除。
也就是说,无论执行提交还是回滚,临时表的数据都没了,但是临时表还是存在的,这一点需要大家注意,在使用的过程中说插入了数据,但是没有数据,为什么呢??
看一个简单的命令行模拟的例子:
db2 =>; connect to sample
数据库连接信息
数据库服务器 = DB2/NT 7.2.1
SQL 授权标识 = ADMINIST...
本地数据库别名 = SAMPLE
//创建临时表
db2 =>; declare global temporary table session.test(col1 char(5)) not logged
DB20000I SQL 命令成功完成。
//插入数据
db2 =>; insert into session.test values('5')
DB20000I SQL 命令成功完成。
//选择数据,没有数据?是的!
db2 =>; select * from session.test
COL1
-----
0 条记录已选择。
//我们把提交方式改为默认不提交
db2 =>; update command options using c off
DB20000I UPDATE COMMAND OPTIONS 命令成功完成。
//再次插入数据
db2 =>; insert into session.test values('5')
DB20000I SQL 命令成功完成。
//选择数据,ok,我们看到数据了!
db2 =>; select * from session.test
COL1
-----
5
1 条记录已选择。
//提交一下,或者执行rollback也可以
db2 =>; commit
DB20000I SQL 命令成功完成。
//数据没了
db2 =>; select * from session.test
COL1
-----
0 条记录已选择。
以上是系统临时表使用的过程中我们程序开发者需要注意的可能出现的问题,知道问题是怎么出现的,我们应该如何解决出现的问题和如何更好的使用临时表,这是我们的目标。