jdbc jdbc绑定变量量可以这样写吗

我来Hacking JDBC,你并不需要它 - ImportNew
我们喜欢和JDBC打交道,以前从未有人这样说过。很严肃的说,JDBC是一个非常优秀的API。这可能是 在JDK1.1之前,出现之前(很久之前的事情了),很难去想象有平台会标准化数据库的访问。在那个时候甚至本身还没有标准化,随后出现的面向Java的简单的JDBC API,工作中你需要也就是一下几点:
使之能够让领域模型与数据库交互
让你能够运行SQL语句的对象
让你能够从数据库中获取数据的对象
这些都是理论,在实际中,企业级软件操作JDBC却是像下面这样:
JDBC是Java开发者的最后一种手段之一,你可以用很多方式来探索解密这个非常详细的神秘的API。几乎每个人都在JDBC操作将实现基于这些API的包装器来防止:
常见的语法错误
绑定变量索引不匹配
动态的SQL构建
在使用lob引起的边界情况
资源的操作与关闭
数组和UDT(户定义类型(User Defined Type))管理
存储过程的抽象
所以当大多数的人在设计上层框架的时候,他们不是在和业务逻辑打交道,但是相当多的人在使用JDBC的时候,几乎都在做这些事情。Hibernate 和JPA大多数没有这些问题,可是他们也不再是SQL API了。
下面是一些我们在JOOQ框架内解决问题的例子,所以你就不需要在解决了:
如何在某些数据库获取生成的键
case DERBY:
case MARIADB:
case MYSQL: {
listener.executeStart(ctx);
result = ctx.statement().executeUpdate();
ctx.rows(result);
listener.executeEnd(ctx);
// Yes. Not all warnings may have been consumed yet
consumeWarnings(ctx, listener);
// Yep. Should be as simple as this. But it isn't.
rs = ctx.statement().getGeneratedKeys();
List&Object& list = new ArrayList&Object&();
// Some JDBC drivers seem to illegally return null
// from getGeneratedKeys() sometimes
if (rs != null) {
while (rs.next()) {
list.add(rs.getObject(1));
// Because most JDBC drivers cannot fetch all
// columns, only identity columns
selectReturning(ctx.configuration(), list.toArray());
JDBCUtils.safeClose(rs);
怎么样去处理BigInteger and BigDecimal
else if (type == BigInteger.class) {
// The SQLite JDBC driver doesn't support BigDecimals
if (ctx.configuration().dialect() == SQLDialect.SQLITE) {
return Convert.convert(rs.getString(index),
(Class) BigInteger.class);
BigDecimal result = rs.getBigDecimal(index);
return (T) (result == null ? null :
result.toBigInteger());
else if (type == BigDecimal.class) {
// The SQLite JDBC driver doesn't support BigDecimals
if (ctx.configuration().dialect() == SQLDialect.SQLITE) {
return Convert.convert(rs.getString(index),
(Class) BigDecimal.class);
return (T) rs.getBigDecimal(index);
怎么去从SQL Server获取所有的异常
switch (configuration.dialect().family()) {
case SQLSERVER:
consumeLoop: for (;;)
if (!stmt.getMoreResults() &&
stmt.getUpdateCount() == -1)
break consumeL
catch (SQLException e) {
previous.setNextException(e);
previous =
这是令人不快的代码,并且我们这儿还有,或者就在我们的 这里所有的例子在向你表明当你在使用JDBC的时候,你要在你的程序中写那些你不想写或者不是很必要的代码,这就是为什么:“我们已经在Hacking JDBC了,你就不需要掺和了”。
关于作者:Lukas 是一个狂热的Java和SQL开发者。他创建了。他是的创建者之一,一个面向Java的综合的SQL库,他的博客主要关注三个话题:Java,SQL和JOOQ。
原文链接:
- 译文链接: [ 转载请保留原文出处、译者和译文链接。]
关于作者:
(新浪微博:)
CyclicBarrier只能唤起一个任务,CountDownLatch可以唤起多个任务 // 好像...
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
新浪微博:
推荐微信号
反馈建议:@
广告与商务合作QQ:
– 好的话题、有启发的回复、值得信赖的圈子
– 写了文章?看干货?去头条!
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 活跃 & 专业的翻译小组
– 国内外的精选博客文章
– UI,网页,交互和用户体验
– JavaScript, HTML5, CSS
– 专注Android技术分享
– 专注iOS技术分享
– 专注Java技术分享
– 专注Python技术分享
& 2017 ImportNew绑定变量(binding variable),共享池(shared
buffer pool), SGA(system global area);
在开发一个数据库系统前,有谁对Oracle
系统了解很多,尤其是它的特性,好象很少吧;对初学者来讲,这更是不可能的事情;仅仅简单掌握了SQL的写法,就开始了数据库的开发,其结果只能是开发一个没有效率,也没有可扩展的系统;
因此,我写这个主题也是希望让大家更多地掌握Oracle数据库的特性,从而在架构一个新系统时,能考虑系统的可扩展,可伸缩性,也兼顾系统的效率和稳定;
使用绑定变量是Oracle数据库的特性之一;于是大家要问,为什么使用,怎样使用,它的使用限制条件是什么?我会按照这样的想法去解答大家的疑问,我也会以举例子的方式来回答这些问题;
1. 为什么使用绑定变量?
这是解决Oracle应用程序可伸缩性的一个关键环节;而Oracle的共享池就决定了开发人员必须使用绑定变量;如果想要Oracle
运行减慢,甚至完全终止,那就可以不用绑定变量;
这里举例说明上述问题;
为了查询一个员工代号是123,你可以这样查询:
select * from emp where empno=’123’;
你也可以这样查询:
select * from emp where empno=:
象我们往常一样,你查询员工’123’一次以后,有可能再也不用;接着你有可能查询员工’456’,然后查询’789’等等;如果查询使用象第一个查询语句,你每次查询都是一个新的查询(我们叫它硬编码的查询方法);因此,Oracle每次必须分析,解析,安全检查,
第二个查询语句提供了绑定变量:empno,它的值在查询执行时提供,查询经过一次编译后,查询方案存储在共享池中,可以用来检索和重用;在性能和伸缩性方面,这两者的差异是巨大的,甚至是惊人的;通俗点讲,就不是一个级别;
第一个查询使用的频率越高,所消耗的系统硬件资源越大,从而降低了用户的使用数量;它也会把优化好的其它查询语句从共享池中踢出;就象一个老鼠坏了一锅汤似的,系统的整体性能降低;
而执行绑定变量,提交相同对象的完全相同的查询的用户(这句话,大家听起来比较难理解,随后我会给出详细的解释),一次性使用就可重复使用,其效率不言耳语;
打个形象的比喻来说,第一个查询就象一次性使用的筷子,而第二个查询象是铁筷子,只要洗干净,张三李四都能用,合理有效地使用了资源;
下面举例子去详细论证上述的问题,不使用绑定变量为生病状况:
这是一个未使用的绑定变量(吃药前):
(把执行结果显示出来)
alter system flush shared_
这条语句是清空共项池,每次都必须使用,确保共享池是空的,以提高执行效率;
set timing on(打开记时器.)
l_dummy all_objects.object_name%
l_start number default dbms_utility.get_
for i in 1 .. 1000
open l_rc for
'select object_name
from all_objects
where object_id = ' ||
fetch l_rc into l_
dbms_output.put_line
( round( (dbms_utility.get_time-l_start)/100, 2 ) ||
' seconds...' );
PL/SQL 过程已成功完成。
执行时间: 已用时间: 00: 00: 07.03
这是一个使用的绑定变量(吃药后):
set echo on
alter system flush shared_
l_dummy all_objects.object_name%
l_start number default dbms_utility.get_
for i in 1 .. 1000
open l_rc for
'select object_name
from all_objects
where object_id = :x'
fetch l_rc into l_
dbms_output.put_line
( round( (dbms_utility.get_time-l_start)/100, 2 ) ||
' seconds...' );
PL/SQL 过程已成功完成。
执行时间: 已用时间: 00: 00: 00.75
大家自己比较结果,相差就是一个数量级;使用绑定变量不仅仅是运行快,而且允许多个用户同时使用;
上述的环境是在数据库Oracle 8.1.7, 操作系统: Windows
Server 2003,内存 1G , CPU: P4 3.4GHZ ; 电脑配置不同,执行的结果是有差异的;
2. 怎样使用绑定变量?
下面举例说明:
2.1.让Oracle自己绑定变量(也叫静态绑定变量)
l_sql varchar2(2000);
l_param1 varchar2(100);
l_param2 varchar2(100);
l_param1:='a';
l_param2:='b';
select count(*) into l_count from table1 where col_1=l_param1 and
col_2=l_param2;&
dbms_output.put_line(l_count);
在上面的情况,Oracle会自己绑定变量,即,如果参数保存在一个数组中,select语句放在一个循环中,
select 语句只会编译一次。
2.2 .动态绑定变量
l_sql varchar2(2000);
l_param1 varchar2(100);
l_param2 varchar2(100);
l_param1:='a';
l_param2:='b';
l_sql:='select count(*) into :x from table1 where col_1=:y and
col_2=:z ';
Execute Immediate l_sql into l_count using
l_param1,l_param2;&
dbms_output.put_line(l_count);
2.3. dbms_output的绑定变量使用
xSql Varchar2(200);
xOut varchar2(200);
l_start number default dbms_utility.get_
cursor_id:=DBMS_Sql.open_
For i in 1..1000 Loop
DBMS_Sql.parse(cursor_id,'insert into t
values(:username,:user_id,Sysdate)',DBMS_SQL.V7);
DBMS_Sql.bind_variable(cursor_id,'username','test'||to_char(i));
DBMS_Sql.bind_variable(cursor_id,'user_id',i);
xRow:=DBMS_Sql.execute(cursor_id);
--insert into t values('test'||to_char(i),i,Sysdate);
--xSql:='insert into t values(:username,:user_id,Sysdate)';
--execute immediate xSql using 'test'||to_char(i),i;
DBMS_sql.close_cursor(cursor_id);
dbms_output.put_line(round((dbms_utility.get_time-l_start)/100,2)
||'seconds...');
--xOut:=to_char(round((dbms_utility.get_time-l_start)/100,2))
||'seconds...';
--xOut:='seconds...';
这里强烈推荐使用静态绑定变量,有兴趣的话可以自己比较;
3. 我怎样知道正在使用绑定变量的方法;
下面举例说明;
Create table t (xx int);
执行下面的语句;
For I in 1..100 loop
Execute immediate’insert into t values(‘|| t ||’)’;
现在准备好了脚本,开始创建一个字符串中删除常数的一个函数,它采用的是SQL语句为:
insert into t values(‘hello’,55);
insert into t values(‘world’,56);
将其转换为
insert into t values(‘#’,@);
所有相同的语句很显然是可见的(使用绑定变量);上述两个独特的插入语句经过转换后变成同样的语句; 完成的转换函数为:
Create or replace function remove_constants(p_query in varchar2)
return varchar2 as
l_char varchar2(1);
l_in_quates b
for i in 1..length(p_query)
l_char:=substr(p_query,i,1);
if l_char='''' and l_in_quates then
l_in_quates:=F
elsif l_char='''' and not l_in_quates then
l_in_quates:=
l_query=:l_query||'#';
if not l_in_quates then
l_query=:l_query||l_
l_query:=tranlate(l_query,'','@@@@@@@@@');
for i in 1..8 loop
l_query:=replace(l_query,lpad();
l_query:=replace(l_query,lpad('',10-i,''),'');
return upper(l_query);
接着我们建立一个临时表去保存V$SQLAREA里的语句,所有 Sql的执行结果都写在这里;
建立临时表;
create global temporary table sql_area_tmp on commit preserve rows
select sql_text,sql_text sql_text_wo_constants
v$sqlarea where 1=0;
保存数据到临时表上;
insert into sql_area_tmp(sql_text) select sql_text from
对临时表中的数据进行更新;删除掉常数;
Update sql_area_tmp set SQL_TEXT_WO_CONSTANTS=
remove_constants(sql_text);
现在我们要找到哪个糟糕的查询
select SQL_TEXT_WO_CONSTANTS,count(*) from sql_area_tmp
group by SQL_TEXT_WO_CONSTANTS
having count(*)&10
order by 2;
SQL_TEXT_WO_CONSTANTS count(*)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
INSERT INTO T VALUES(@) 100
另外, 设定如下参数
Alter session set sql_trace=
Alter session set timed_statictics=T
Alter session set events ‘10046 trace name context forever,level
这里的’N’ 表示的是1,4,8,12,详细内容请参考相关文档
Alter session set events ‘10046 trace name context off’;
可以用 TKPROF 工具查看绑顶变量执行的结果,更详细内容请查看相关文档;
4. 绑定变量在应用开发环境下的使用;
4.1 在VB.Net or ASP.NET and VB中的的使用
建议用Oracleclient DB的连接方法,OleDB不支持;下面是使用
OracleClient连接的使用例子(这个代码执行只需要2秒不到);
Dim cn01 As New OracleConnection
Dim CMD01 As New OracleCommand
Dim Cmd As New OleDbCommand
Dim i As Integer
xConnStr =
System.Configuration.ConfigurationSettings.AppSettings("DBCONN_SFCFA")
'cn01.ConnectionString()
cn01.ConnectionString = xConnStr
cn01.Open()
TextBox1.Text = Now
Application.DoEvents()
xSql = "insert into t
values(:username,:userid,sysdate) "
For i = 1 To 1000
CMD01 = New OracleClient.OracleCommand(xSql, cn01)
mandType = CommandType.Text
CMD01.Parameters.Add("username", "test" + CStr(i))
CMD01.Parameters.Add("userid", i)
CMD01.ExecuteNonQuery()
CMD01.Parameters.Clear()
TextBox2.Text = Now
Catch ex As OleDbException
MsgBox(ex.Message)
Catch ex As Exception
MsgBox(ex.HelpLink + ex.Message)
OleDB(VB,ASP等)不支持绑定变量,或者我没有找到更好的方法去实现它;它有变量的概念但不支持绑定;网络上,有很多帖子说;他实现了绑定变量用VB
or ASP;我按照他们的方法去试,发现他们与单纯传参数没有什么区别,请看下面的内容;
OleDB(这个执行需要5秒;) :
Dim xConnStr, xSql As String
Dim Cn As New OleDbConnection
Dim cn01 As New
OracleConnection
Dim CMD01 As New OracleCommand
Dim Cmd As New OleDbCommand
Dim i As Integer
xConnStr =
System.Configuration.ConfigurationSettings.AppSettings("DBCONN_SFCFA")
'Cn.ConnectionString()
Cn.ConnectionString = xConnStr
TextBox1.Text = Now
Application.DoEvents()
xSql = "insert into t
values(?,?,sysdate) "
For i = 1 To 1000
Cmd = New OleDbCommand(xSql, Cn)
mandType = CommandType.Text
Cmd.Parameters.Add("username", "test" + CStr(i))
Cmd.Parameters.Add("userid", i)
Cmd.ExecuteNonQuery()
Cmd.Parameters.Clear()
TextBox2.Text = Now
Catch ex As OleDbException
MsgBox(ex.Message)
Catch ex As Exception
MsgBox(ex.HelpLink + ex.Message)
VB or ASP(耗时也是5秒左右…):
Private Sub Command1_Click()
Dim sConn As String
Dim BVCS_CN As ADODB.Connection
'Dim BVCS as ADODB.
Dim xCMD mand
Dim xPre As ADODB.Parameter
Dim xSql As String
Dim xSql01 As String
Dim xRS As ADODB.Recordset
On Error GoTo 1
SetDBConnection = True
Set BVCS_CN = New ADODB.Connection
'BVCS_CN.Provider = "MSDAORA"
'sConn = "DATA SOURCE=" & ServerName & ";"
sConn = "Provider=MSDAORA.1;Password=User ID=Data
With BVCS_CN
.Open sConn
If BVCS_CN.State = 0 Then
MsgBox "DB Connection is error"
Text1.Text = Now
Set xCMD = mand
Dim xTest As String
Set xPre = New ADODB.Parameter
For i = 1 To 1000
.ActiveConnection = BVCS_CN
.CommandText = " Insert into
TT(username,userid) values(?,?) "
.CommandType = adCmdText
.Parameters.Append .CreateParameter("username", adBSTR,
adParamInput, 30, "test" + CStr(i))
.Parameters.Append .CreateParameter("userid", adInteger,
adParamInput, 4, i)
.Prepared = True
xCMD.Parameters.Delete 1
xCMD.Parameters.Delete 0
Set xCMD = Nothing
Text2.Text = Now
Set xCMD = Nothing
MsgBox Error$
For Each objErr In BVCS_CN.Errors
MsgBox objErr.Description
BVCS_CN.Errors.Clear
Resume Next
4.2 在Delphi中的使用情况;
这里特殊说明, Borland Delphi
4.0以上的版本已经开始完全支持绑定变量的概念,因此,它执行数据库的查询效率要好于其他开发工具;执行的结果不到2秒;
procedure TForm1.Button1Click(Sender: TObject);
edit1.text:=DatetimeToStr(now);
For i := 1 to 1000 do
With Query1 do
Sql.add('Insert into t Values(:username,:user_id,sysdate) ');
ParamByName('username').AsString :='test' ;
ParamByName('user_id').AsInteger :=
//edit2.text:=DateToStr(now);
edit2.text:=DatetimetoStr(now);
4.3. 在Java中的使用绑定变量
String v_id = 'xxxxx';
String v_sql = 'select name from table_a where id = ? ';
//嵌入绑定变量
stmt = con.prepareStatement( v_sql );
stmt.setString(1, v_id ); //为绑定变量赋值
stmt.executeQuery();
在Java中,结合使用setXXX 系列方法,可以为不同数据类型的绑定变量进行赋值,从而大大优化了SQL 语句的性能。
4.4 C#同VB.NET ,这里不在赘述;
5. 绑定变量使用限制条件是什么?
为了不重复解析相同的SQL语句,在第一次解析之后, ORACLE将SQL语句存放在内存中.这块位于系统全局区域SGA(system
global area)的共享池(shared buffer pool)中的内存可以被所有的数据库用户共享.
因此,当你执行一个SQL语句(有时被称为一个游标)时,如果它和之前的执行过的语句完全相同,
ORACLE就能很快获得已经被解析的语句以及最好的执行路径.
ORACLE的这个功能大大地提高了SQL的执行性能并节省了内存的使用.可惜的是ORACLE只对简单的表提供高速缓冲(cache
buffering) ,这个功能并不适用于多表连接查询(这句并不完全可信,有兴趣的可以自己琢磨).
数据库管理员必须在init.ora中为这个区域设置合适的参数,当这个内存区域越大,就可以保留更多的语句,当然被共享的可能性也就越大了.
当你向ORACLE
提交一个SQL语句,ORACLE会首先在这块内存中查找相同的语句.这里需要注明的是,ORACLE对两者采取的是一种严格匹配,要达成共享,SQL语句必须完全相同(包括空格,换行等).
共享的语句必须满足三个条件:
A. 字符级的比较:
当前被执行的语句和共享池中的语句必须完全相同.
SELECT * FROM EMP;
和下列每一个都不同
SELECT * from EMP;
Select * From E
SELECT * FROM EMP;
B. 两个语句所指的对象必须完全相同:
用户 对象名 如何访问
Jack sal_limit private synonym
Work_city public synonym
Plant_detail public synonym
Jill sal_limit private synonym
Work_city public synonym
Plant_detail table owner
考虑一下下列SQL语句能否在这两个用户之间共享.
SQL 能否共享 原因
select max(sal_cap) from sal_ 不能 每个用户都有一个private synonym -
sal_limit , 它们是不同的对象
select count(*0 from work_city where sdesc like 'NEW%'; 能
两个用户访问相同的对象public synonym - work_city&
select a.sdesc,b.location from work_city a , plant_detail b where
a.city_id = b.city_id 不能 用户jack 通过private synonym访问plant_detail
而jill 是表的所有者,对象不同.
C. 两个SQL语句中必须使用相同的名字的绑定变量(bind variables)
第一组的两个SQL语句是相同的(可以共享),而第二组中的两个语句是不同的(即使在运行时,赋于不同的绑定变量相同的值)
select pin , name from people where pin = :blk1.
select pin , name from people where pin = :blk1.
select pin , name from people where pin = :blk1.ot_
select pin , name from people where pin = :blk1.ov_
不使用绑定变量是做着等死,使用绑定变量不一定不会死;没有任何的良药会包治百病,所以在如何合理有效地使用绑定变量仍就需要大家去摸索;
[全文完]..
1. [Oracle 高级专家编程] 作者:Thomas Kytes 袁勤勇,张玉魁编译;清华大学出版社;
2. ORACLE SQL性能优化系列 from&
3. Oracle 绑定变量的用法 from 王者之剑()
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。jdbc测试mysql数据库sql预解析(绑定变量)-数据库-电脑编程网jdbc测试mysql数据库sql预解析(绑定变量)作者:yzsind 和相关&&
jdbc测试mysql数据库sql预解析(绑定变量)& & & & 用习惯了oracle,学习mysql,想测试一下mysql绑定变量的效果。以前看网上介绍大部份都说mysql没有sql共享池的概念,所以也不存在sql预解析或绑定变量的说法。& & & & 今天测试了一下(通过网络抓包和看服务器端sql日志的方法),发现mysql还是有sql预解析的实现。& & & & 服务器端是mysql 5.1.58(win32),用jdbc做客户端,默认的连接方式是不会有sql预解析效果,即使我们用PreparedStatement对象也差不多,它只是把SQL和变量拼接成一个完整的SQL发送给服务器,如下代码:preparedstatement pstmt = conn.preparestatement(&select * from t1 where c1=?&);
pstmt.setstring(1, &abc&);
pstmt.execute();实际上不会有预解析的过程,而是经过简单的拼接,把如下SQL发送给服务器select * from t1 where c1='abc'& & & & & &要实现预解析的效果,我们必须设置jdbc Connection的参数useServerPrepStmts=true,再使用PreparedStatement后就OK了,创建PreparedStatement时客户端先把&select * from t1 where c1=?&发送到服务器端预解析,execute时只是把变量传送到服务器执行。mysql服务器的sql语句缓存可以通过状态变量Prepared_stmt_count查看mysql& show status
like 'prepared_stmt_count';
+---------------------+-------+
| variable_name
+---------------------+-------+
| prepared_stmt_count | 1
+---------------------+-------+
1 row in set
& & & & 不过mysql的sql语句缓存与oracle有很大不同,它是会话语句级的,不是全局共享,当会话断开或PreparedStatement.close后这个缓存就没有了。我们需要设置Connection的参数cachePrepStmts=true把PreparedStatement缓存起来,prepStmtCacheSize=xxx来设置每个会话缓存语句的最大数量(很多连接池也有类似的功能)。& & & & OK,已经知道如何启用预解析了,想看看启用与不启用预解析性能有多少差别,会不会也像oracle那么明显呢?经过简单的测试,发现当没有PreparedStatement缓存(cachePrepStmts=false)时,打开预解析性能下降很多, 当有PreparedStatement缓存(cachePrepStmts=true)时,两者性能基本一样。这个结果让人很失望,个人分析有几个原因:& & & & 启用预解析但没有PreparedStatement缓存时,每次创建PreparedStatement都需要解析一次,execute时又需要交互一次,而预解析的SQL在PreparedStatement.close又不能重用,所以性能反而更差。& & & & 当有PreparedStatement缓存时,预解析的SQL是可以重用了,但是由于mysql的SQL解析器与优化器性能不错(待考证),性能瓶颈也不在解析SQL,所以两者性能差不多。& & & & 还有一种可能是mysql的预解析基本上什么都没做,只是保存一下SQL文本而己,执行时还是需要解析和生成执行计划(待考证)。& & & & 经过上面分析,个人认为不需要打开SQL预解析的效果,PreparedStatement对象还是尽量使用,因为虽然不能提升性能,但可以避免SQL注入安全问题 。相关资料:|||||||jdbc测试mysql数据库sql预解析(绑定变量)来源网络,如有侵权请告知,即处理!编程Tags:                &                    oracle(3)
应用程序方面
(1)从应用程序角度,如果使用PL/SQL来编程,使用绑定变量就会更加简单。
(2)如果使用JDBC来开发,不适用绑定变量会使SQL语句更加简单。
可以在库缓存中共享游标,这样可以避免硬解析以及与之相关额外的开销。但这种情况并不是绝对的。
v varchar2(32);
--第一次执行
n:=1;v:='first';
insert into t(n,v) values(:n,:v);
--第二次执行
n:=2;v:='second';
insert into t(n,v) values(:n,:v);
--第三次执行
n:=3;v:='third';
insert into t(n,v) values(:n,:v);
v varchar(33)
--第四次执行
n:=4;v:='fourth';
insert into t(n,v) values(:n,:v);
前三次执行只产生了一个游标,也就是说前三次insert共享了一个子游标,而第四次执行则另外产生了一个游标,也就是说并未与前三次产生的游标共享。
产生这种情况的原因就是因为数据库引擎使用了绑定变量分级(graduation)。这个功能的目的是最小化子游标的数量,它是根据绑定变量的长短将绑定变量分为四个级别:
32个字节及其业内被分在第一个级别;33到128个字节被分在第二个级别;129到2000个字节被分在第三个级别,其余大于2000个字节的被分在第四个级别。NUMBER被分在
它的最大产度22个字节级别上。
通过绑定变量分级可以看出,第四次的时候绑定变量长度为33,在第二个级别,这时候数据库引擎认为执行环境发生了变化,则产生新的子游标。
在WHERE子句中使用绑定变量的缺点是会有一些至关重要的信息对查询优化视而不见,事实上,对于查询优化器来讲,使用字面变量比绑定变量来得更好,使用字面变量可以
提高开销估算的准确性。
在一个1000记录的表中,字段id的值在1和1000之间。
如果查询id小于990的数据,那么查询优化器知道差不多有99%的记录被选中,这时会执行全表扫描。
select * from t where id&990;
如果查询id小于10的记录,那么查询优化器知道差不多有1%的记录被选中,那么会执行索引扫描。
select * from t where id &10;
但是如果使用绑定变量,那么查询优化器就会忽略它们的具体值。为此,oracle9i引入了绑定变量窥测功能(bind variable peeking)。
所谓绑定变量窥测功能就是在物理优化阶段,查询优化器会窥测绑定变量的值,将它作为字面变量来使用,但是这样的问题是它生成的执行计划会依赖第一次产生执行计划所提供
的值。如果第一次执行小于990的查询,那么第二次查询小于10的时候就也会进行全表扫描。
为了解决这个问题,oracle11g引入了扩展的游标共享,也就是数据库引擎为每个子游标的每个选择条件相关的选择性范围,这就部分解决了绑定变量窥测功能的缺陷。
总体来讲,为了提高查询优化器的性能,最好不要使用绑定变量。
总结如下:
SQL语句处理少量数据时--解析时间可能等于或者高于执行时间,使用绑定变量是一种比较好的选择。
SQL语句处理大量数据时--解析时间远远低于执行时间,这是应考虑尽量避免使用绑定变量。
参考资料:Oracle性能诊断艺术&
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:102010次
积分:2131
积分:2131
排名:第14552名
原创:10篇
转载:24篇
译文:101篇
评论:12条
(3)(10)(1)(4)(1)(22)(16)(14)(1)(1)(3)(1)(2)(5)(8)(9)(2)(2)(4)(1)(2)(23)

我要回帖

更多关于 sql绑定变量 的文章

 

随机推荐