PHP&Oracle学习笔记之:连接数据与单纯SQL语句的效率比较

发布于: 2008年04月25日

本文的目的:

1. 众所周知,连接一个数据库过程对资源开销很大 ,Oracle官方文档针对PHP部分也佐证了这一点。所以第一个目的是为了证明跟一条普通的SQL语句相比,到底开销相差有多大。

2. 本人接触Oracle 没有多久,听说Oracle有一个较大的缓存池,保持最近运行的语句,以提高重复运行相同语句的效率。目的二是为了证明缓存池有多大影响。

测试环境:

1. 本机安装:Apache 2.2.8+PHP5.2.5 +Oracle Client 10.2.01

2. 连接Oracle 独立服务器,服务器版本:Oracle 10g

测试代码:

//PHP连接Oracle函数
function conn_ora(){
$username='test';
$passwd='test';
$protocol='TCP';
$SERVICE_NAME='testdb';
$ORACLE_SERVER_IP_ADDRESS='192.168.0.10';
$Port='1521';
PutEnv("NLS_LANG=SIMPLIFIED CHINESE_CHINA.AL32UTF8");
if(!$conn = @oci_pconnect($username, $passwd, "(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = $protocol)(HOST = $ORACLE_SERVER_IP_ADDRESS)(PORT = $Port)) ) (CONNECT_DATA = (SERVICE_NAME = $SERVICE_NAME) ) )")) echo("Error in Connecting to DataBase");
return $conn;
}
//计时函数:
function microtime_float(){
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
//query 函数
function ora_query($sql){
global $conn_ora;
$result=oci_parse($conn_ora,$sql);
oci_execute($result,OCI_DEFAULT);
return $result;
}

连接数据库测试代码:


$time_start=microtime_float();
$num=1000; // 测试的次数,将进行100次和1000次测试
for($i=0;$i<$num;$i++){
$conn_ora=conn_ora();
oci_close($conn_ora);
}
$time_end=microtime_float();
$time_spend=$time_end-$time_start;
echo "Script run time: $time_spend sec. \n";

SQL语句执行测试代码:

$conn_ora=conn_ora();
$time_start=microtime_float();
$num=1000; // 测试的次数,将进行100次和1000次测试
for($i=0;$i<$num;$i++){
$sql="select * from test_table where rownum<10 ";
$result=ora_query($sql);
}
$time_end=microtime_float();
$time_spend=$time_end-$time_start;
oci_close($conn_ora);
echo "Script run time: $time_spend sec. \n";

执行顺序:
条件一:执行1000次‘SQL语句执行测试代码’;
条件二:执行1000次‘连接数据库测试代码’;
条件三:执行100次‘连接数据库测试代码’;
条件四:执行100次‘SQL语句执行测试代码’;
以上每个条件的脚本执行5次,取时间
结果如下:

条件一:执行1000次‘SQL语句执行测试代码’;
0.9970sec
0.8482sec
0.8777sec
0.8552sec
0.8505sec
条件二:执行1000次‘连接数据库测试代码’;
1.6175sec
1.6457sec
1.6064sec
1.6380sec
1.6083sec
条件三:执行100次‘连接数据库测试代码’;
0.1833sec
0.1532sec
0.1552sec
0.1561sec
0.1818sec
条件四:执行100次‘SQL语句执行测试代码’;
0.0984sec
0.1203sec
0.1004sec
0.1011sec
0.1177sec

通过以上数据得出以下结论:

1.连接数据库比普通SQL语句的系统开销稍大,但没有“很大”:1000次测试在时间上大概在2 倍左右;100次测试仅为1.5倍左右。当然CPU,内存等其他开销,本次测试无法给出

2.从’条件一’的5个时间结果可以看到,第一个时间0.9970比其余时间明显偏大0.1sec以上,看起来缓存机制在其作用。后续测试没有相同情况出现,也正印证了缓存机制的作用。

3.1000测试与100次测试,相同代码花费时间基本呈线性的10倍关系 。可见当中没有明显的其他因素影响。

##########################################
##############以下为改变条件重复测试##############
##########################################
在观众的提点下,修改条件重复测试。

首先,将oci_pconnect()改为oci_connect(),重做以上测试:(测试代码参考上文,不再重复书写)
测试结果:

条件一:执行1000次‘SQL语句执行测试代码’;
0.8038sec
0.8344sec
0.8591sec
0.8523sec
0.8402sec
条件二:执行1000次‘连接数据库测试代码’;
超过页面执行时间,php.ini设定为30sec
条件三:执行100次‘连接数据库测试代码’;
7.1286sec
7.1195sec
6.8513sec
7.0622sec
6.6788sec
条件四:执行100次‘SQL语句执行测试代码’;
0.1170sec
0.1138sec
0.0836sec
0.0839sec
0.0838sec

然后,新曾测试条件:先连接数据库,然后执行一条SQL语句,最后关闭,循坏执行以上步骤,测试代码:

$conn_ora=conn_ora();
$time_start=microtime_float();
$num=1000; // 测试的次数,将进行100次和1000次测试
for($i=0;$i<$num;$i++){
$conn_ora=conn_ora();
$sql="select * from test_table where rownum<10 ";
$result=ora_query($sql);
oci_close($conn_ora);
}
$time_end=microtime_float();
$time_spend=$time_end-$time_start;
oci_close($conn_ora);
echo "Script run time: $time_spend sec. \n";

条件五:上面代码执行100次
条件六:上面代码执行1000次
条件七:将上面代码改用oci_pconnect执行1000次

结果数据:

条件五:
0.4013sec
0.3804sec
0.3789sec
0.3482sec
0.3447sec
条件六:
2.6904sec
2.7077sec
2.7644sec
2.7573sec
2.7476sec
条件七:
2.6194sec
2.5633sec
2.8788sec
2.7847sec
2.5538sec

结论更新:
1. oci_pconnect()改为oci_connect()后,‘条件一’和’条件四’结果几乎没有变化。这是可以预期的,因为这两个条件只执行了一次连接。
2.oci_pconnect()改为oci_connect()后,’条件三’产生了超过40倍的增加,‘条件四’没有数据,按照线性倍增推算,该条件运行会超过70sec。这点跟新增条件结果放在一起,让我非常不解,还请高人指点。
3.新增条件(条件五,六,七)重复“连接数据库,运行SQL,断开数据库”步骤,原‘条件一’和’条件四’相比,时间增加约为3.3倍,
4.新增条件(条件五,六,七)下,采用“oci_pconnect()”和“oci_connect()”几乎没有变化。但是这个与’条件三’产生了巨大的矛盾。

针对oci_pconnect()和oci_connect()疑问:
为何重复进行”连接和断开”过程中,oci_pconnect()和oci_connect()有如此大的差异,而中间有查询过程的情况下,差异可以忽略?
##########################################
####################END###################
##########################################

本文为寻找人生的起点(http://blog.cn-ic.org)原创,如需转载,请注明出处,并保留原文链接 http://blog.cn-ic.org/?p=84。

PHP&Oracle学习笔记之:group by

发布于: 2008年04月18日

从MySQL转到Oracle之后,group by 变成了我的另外一个噩梦。曾经多么好用的group by, 虽然与distinct作用类似,但是由于在SQL语句位置的不同,可以轻松的得到很多难缠问题的解法。但是在Oracle里面硬生生的变成了一个鸡肋。

在MySQL里面group by 可以随心所欲的在任何场合使用,比如单表查询时:
select id,name,addr from table1 group by addr
或者多表查询时:
select t1.id, t2.name,t2.addr as addr from table1 as t1, table2 as t2 where t1.id=t2.id group by addr
但是这样的写法在oracle里面就会报错,并非因为上一篇提到的as 用法的差异,而是oracle 对group by 用法的苛刻要求。

这里有个英文网页告诉我们

oracle 要求使用group by 时必须与下列统计函数一起使用

AVG
COUNT
FIRST
LAST
MAX
MIN
STDDEV
SUM
VARIANCE

比如
select addr,count(*) from table1 group by addr
列出的结果会对相同的addr字段数据技术
但是,我发现这样写也会报错
select addr,count(*),name from table1 group by addr
当然逻辑上讲,addr一致的时候,不能保证name一致,所以oracle不能判断怎么显示name,所以报错(MySQL能显示第一个找到的name数据),如果一定要写,只能这么写,
select addr,count(*),count(name) from table1 group by addr
当然我看不出这么写还有这么意义。

我找不到一个总结oracle group by 用法的中文网页,如果你也遇到相同的困扰,上面提到的一个英文网页也许能帮到你:http://www.psoug.org/reference/group_by.html

如果你也总结了oracle group by 的用法,希望你不吝于我分享。

本文为寻找人生的起点(http://blog.cn-ic.org)原创,如需转载,请注明出处,并保留原文链接 http://blog.cn-ic.org/?p=83。

PHP&Oracle学习笔记之:AS

发布于: 2008年04月18日

MySQL写多表联合查询时经常用到as来达到简写表或者字段的目的, 比如:

select a.id as id,b.name as name from table1 as a, table2 as b where a.id=b.id

这样的 写法在oracle里面不能被识别,因为oracle里面不需要as, 所以oracle的写法如下

select a.id id,b.name name from table1 a, table2 b where a.id=b.id

但是值得注意的是,oracle并不完全排斥as, 只是在from talbe处不能使用,所以这样的写法也是别oracle接受的
select a.id as id,b.name as name from table1 a, table2 b where a.id=b.id

另外我还发现的一个与as无关,但是与简写有关的问题,比如单表查询时,我们也可以用简写:
select a.id as id,a.name as name from table1 a

但是使用上一篇提到的rownum时,oracle却不接受这样的写法:
select a.id as id,a.name as name from table1 a where a.rownum<30

因为,oracle不认为rownum是上面table1的一个字段,而只是在处理SQL查询时自动生成的序列而已。我们只能这么写:
select a.id as id,a.name as name from table1 a where rownum<30

本文为寻找人生的起点(http://blog.cn-ic.org)原创,如需转载,请注明出处,并保留原文链接 http://blog.cn-ic.org/?p=82。

PHP&Oracle学习笔记之:limit

发布于: 2008年04月18日

MySQL 有个很好的用法就是Limit, 就连phpMyAdmin的浏览页,默认的SQL语句也是这么写的。

select * from table limt 30
表示默认取最先取到的30行数据。

这种写法可以扩展成
select * from table limt 30,60
表示取第30到60行的数据

转到Oracle后的一个重大问题就是所有有limit写法的语句都要改成新的。因为Oracle并不支持。确切的说,limit并不是标准SQL语句

幸好,Oracle支持另外一种写法,实现相同的功能,那就是rownum。 rownum的写法是这样的
select * from table where rownum <30
或者
select * from table where rownum >30 and rownum<60

本文为寻找人生的起点(http://blog.cn-ic.org)原创,如需转载,请注明出处,并保留原文链接 http://blog.cn-ic.org/?p=81。

javascript兼容之:window.location.href

发布于: 2008年04月18日

写网页程序,不用javascript那是相当的困难。用javascipt调试Firefox和IE统一兼容,那也是相当的困难。

我十分佩服IE的兼容,比如
document.getElementById('sampleid')
如果当前表单错写,没有id,只有name如下:

<input name="sampleid" id="sampleid" type="text" >

IE照样识别,而FF就不行,让我一度认为FF不支持
getElementById
这样的写法。

今天发现的问题是window.location.href跳转问题

写了一个跳转确认的小函数如下:

//跳转确认函数
function delconfirm(msg,link){
if(confirm(msg)){
window.location.href(link);
}
}

结果FF死活不认。搞半天发现,FF的承认的写法是这样的

//跳转确认函数
function delconfirm(msg,link){
if(confirm(msg)){
window.location.href=link;
}
}

好吧FF,我服了,以后就这么写。

本文为寻找人生的起点(http://blog.cn-ic.org)原创,如需转载,请注明出处,并保留原文链接 http://blog.cn-ic.org/?p=80。

oracle字符处理函数

发布于: 2008年04月10日

本文转载自http://thinbug.bokele.com/?ArticleID=31453

为了自己查找方便在放到这里来:
F.1字符函数——返回字符值

这些函数全都接收的是字符族类型的参数(CHR除外)并且返回字符值.除了特别说明的之外,这些函数大部分返回VARCHAR2类型的数值.字符函数的返回类型所受的限制和基本数据库类型所受的限制是相同的,比如: VARCHAR2数值被限制为2000字符(ORACLE 8中为4000字符),而CHAR数值被限制为255字符(在ORACLE8中是2000).当在过程性语句中使用时,它们可以被赋值给VARCHAR2 或者CHAR类型的PL/SQL变量.

l CHR

语法: chr(x)

功能:返回在数据库字符集中与X拥有等价数值的字符。CHR和ASCII是一对反函数。经过CHR转换后的字符再经过ASCII转换又得到了原来的字符。

使用位置:过程性语句和SQL语句。

l CONCAT

语法: CONCAT(string1,string2)

功能:返回string1,并且在后面连接string2。

使用位置:过程性语句和SQL语句。

l INITCAP

语法:INITCAP(string)

功能:返回字符串的每个单词的第一个字母大写而单词中的其他字母小写的string。单词是用.空格或给字母数字字符进行分隔。不是字母的字符不变动。

使用位置:过程性语句和SQL语句。

l LTRIM

语法:LTRIM(string1,string2)

功能:返回删除从左边算起出现在string2中的字符的string1。String2被缺省设置为单个的空格。数据库将扫描string1,从最左边开始。当遇到不在string2中的第一个字符,结果就被返回了。LTRIM的行为方式与RTRIM很相似。

使用位置:过程性语句和SQL语句。

l NLS_INITCAP

语法:NLS_INITCAP(string[,nlsparams])

功能:返回字符串每个单词第一个字母大写而单词中的其他字母小写的string,nlsparams

指定了不同于该会话缺省值的不同排序序列。如果不指定参数,则功能和INITCAP相同。Nlsparams可以使用的形式是:

‘NLS_SORT=sort’

这里sort制订了一个语言排序序列。

使用位置:过程性语句和SQL语句。

l NLS_LOWER

语法:NLS_LOWER(string[,nlsparams])

功能:返回字符串中的所有字母都是小写形式的string。不是字母的字符不变。

Nlsparams参数的形式与用途和NLS_INITCAP中的nlsparams参数是相同的。如果nlsparams没有被包含,那么NLS_LOWER所作的处理和LOWER相同。

使用位置;过程性语句和SQL语句。

l NLS_UPPER

语法:nls_upper(string[,nlsparams])

功能:返回字符串中的所有字母都是大写的形式的string。不是字母的字符不变。nlsparams参数的形式与用途和NLS_INITCAP中的相同。如果没有设定参数,则NLS_UPPER功能和UPPER相同。

使用位置:过程性语句和SQL语句。

l REPLACE

语法:REPLACE(string,search_str[,replace_str])

功能:把string中的所有的子字符串search_str用可选的replace_str替换,如果没有指定replace_str,所有的string中的子字符串search_str都将被删除。REPLACE是TRANSLATE所提供的功能的一个子集。

使用位置:过程性语句和SQL语句。

l RPAD

语法:RPAD(string1,x[,string2])

功能:返回在X字符长度的位置上插入一个string2中的字符的string1。如果string2的长度要比X字符少,就按照需要进行复制。如果 string2多于X字符,则仅string1前面的X各字符被使用。如果没有指定string2,那么使用空格进行填充。X是使用显示长度可以比字符串的实际长度要长。RPAD的行为方式与LPAD很相似,除了它是在右边而不是在左边进行填充。

使用位置:过程性语句和SQL语句。

l RTRIM
语法: RTRIM(string1,[,string2])

功能: 返回删除从右边算起出现在string1中出现的字符string2. string2被缺省设置为单个的空格.数据库将扫描string1,从右边开始.当遇到不在string2中的第一个字符,结果就被返回了RTRIM的行为方式与LTRIM很相似.

使用位置:过程性语句和SQL语句。

l SOUNDEX

语法: SOUNDEX(string)

功能: 返回string的声音表示形式.这对于比较两个拼写不同但是发音类似的单词而言很有帮助.

使用位置:过程性语句和SQL语句。

l SUBSTR

语法: SUBSTR(string,a[,b])

功能: 返回从字母为值a开始b个字符长的string的一个子字符串.如果a是0,那么它就被认为从第一个字符开始.如果是正数,返回字符是从左边向右边进行计算的.如果b是负数,那么返回的字符是从string的末尾开始从右向左进行计算的.如果b不存在,那么它将缺省的设置为整个字符串.如果b小于1,那么将返回NULL.如果a或b使用了浮点数,那么该数值将在处理进行以前首先被却为一个整数.

使用位置:过程性语句和SQL语句。

l TRANSLATE

语法: TRANSLATE(string,from_str,to_str)

功能: 返回将所出现的from_str中的每个字符替换为to_str中的相应字符以后的string. TRANSLATE是REPLACE所提供的功能的一个超集.如果from_str比to_str长,那么在from_str中而不在to_str中而外的字符将从string中被删除,因为它们没有相应的替换字符. to_str不能为空.Oracle把空字符串认为是NULL,并且如果TRANSLATE中的任何参数为NULL,那么结果也是NULL.

使用位置:过程性语句和SQL语句。

l UPPER

语法: UPPER(string)

功能: 返回大写的string.不是字母的字符不变.如果string是CHAR数据类型的,那么结果也是CHAR类型的.如果string是VARCHAR2类型的,那么结果也是VARCHAR2类型的.

使用位置: 过程性语句和SQL语句。

F.2 字符函数——返回数字

这些函数接受字符参数回数字结果.参数可以是CHAR或者是VARCHAR2类型的.尽管实际下许多结果都是整数值,但是返回结果都是简单的NUMBER类型的,没有定义任何的精度或刻度范围.

l ASCII

语法: ASCII(string)

功能: 数据库字符集返回string的第一个字节的十进制表示.请注意该函数仍然称作为ASCII.尽管许多字符集不是7位ASCII.CHR和ASCII是互为相反的函数.CHR得到给定字符编码的响应字符. ASCII得到给定字符的字符编码.

使用位置: 过程性语句和SQL语句。

l INSTR

语法: INSTR(string1, string2[a,b])

功能: 得到在string1中包含string2的位置. string1时从左边开始检查的,开始的位置为a,如果a是一个负数,那么string1是从右边开始进行扫描的.第b次出现的位置将被返回. a和b都缺省设置为1,这将会返回在string1中第一次出现string2的位置.如果string2在a和b的规定下没有找到,那么返回0.位置的计算是相对于string1的开始位置的,不管a和b的取值是多少.

使用位置: 过程性语句和SQL语句。

l INSTRB

语法: INSTRB(string1, string2[a,[b]])

功能: 和INSTR相同,只是操作的对参数字符使用的位置的是字节.

使用位置: 过程性语句和SQL语句。

l LENGTH

语法: LENGTH(string)

功能: 返回string的字节单位的长度.CHAR数值是填充空格类型的,如果string由数据类型CHAR,它的结尾的空格都被计算到字符串长度中间.如果string是NULL,返回结果是NULL,而不是0.

使用位置: 过程性语句和SQL语句。

l LENGTHB

语法: LENGTHB(string)

功能: 返回以字节为单位的string的长度.对于单字节字符集LENGTHB和LENGTH是一样的.

使用位置: 过程性语句和SQL语句。

l NLSSORT

语法: NLSSORT(string[,nlsparams])

功能: 得到用于排序string的字符串字节.所有的数值都被转换为字节字符串,这样在不同数据库之间就保持了一致性. Nlsparams的作用和NLS_INITCAP中的相同.如果忽略参数,会话使用缺省排序.

使用位置: 过程性语句和SQL语句。

浙ICP备18056264号-1