guiying

2012年8月1日

Linux网络编程中的地址问题

平时我们使用的IP地址是192.168.1.11这种类型的字符串;而在Linux内核中是用二进制方式表达的IP地址。在程序设计中经常要用到字符串表达方式的IP地址和二进制的IP地址之间的转换。

  面对网络编程中众多的地址函数,你hold住了么,tiger哥没hold住,所以就写了此篇文章,希望大家能hold住网络编程。

  前言:结构体struct in_addr结构struct in_addr 在文件中定义,结构in_addr 有一个unsigned long int 类型的成员变量s_addr.通常所说的IP地址的二进制形式就保存在成员变量s_addr中。

  结构struct in_addr的原型如下:structin_addr{ unsigned long int s_addr;/*IP地址*/ }一。字符串IP地址转换为二进制形式的IP地址函数1.inet系列函数的原型:#include #include #include int inet_aton(const char *cp,struct in_addr *inp);int addr_t inet_addr(const char *cp);int addr_t inet_network(const char *cp);char * inet_ntoa(struct in_addr in);struct in_addr inet_makeaddr(int net,int host);in_addr_t inet_lnaof(struct in_addr in);in_addr_t inet_netof(struct in_addr in);2.inet_aton()函数int inet_aton(const char*cp,struct in_addr *inp)

  1>函数作用:inet_ation()函数将在cp中存储的点分十进制字符串类型的IP地址,转换为二进制的IP地址,转换后的值保存在指针inp指向的结构struct in_addr中。

  2>形参? const char *cp:指向字符类型的IP地址。例如“192.168.1.11”? struct in_addr *inp:指向二进制的网络字节顺序的IP地址structin_addr{ unsignedlong s_addr } 3>函数执行成功则返回非0值,参数无效则返回0. 4>实例:#include #include #include

  #include #include #include int main()

  { char buffer[32];structin_addr in;in.s_addr =0;/*输入一个字符串形式的IP地址*/ printf(“pleaseinput the ip addrss\n”);scanf(“%s”,buffer);buffer[31]=‘\0’;printf(“original IP地址:%s\n”,buffer);if( 0 == inet_aton(buffer,&in)){ perror(“inet_aton”);exit(1);} else { printf(“after transfer:0x%0x\n”,in.s_addr);} 5> inet_aton函数最后计算出来的是网络字节序的二进制IP 3.inet_addr()函数in_addr_t inet_addr(const char *cp)

  1>函数作用:它将参数cp所指向的字符串形式的IP地址(“192.168.1.11”)转换为二进制的网络字节序的IP地址形式该函数的缺点是:如果IP地址是255.255.255.255.那么调用inet_addr()函数后将返回-1(因为-1的补码形式是0xFFFFFFFF)。所以不建议使用inet_addr()函数,而是使用inet_aton()函数。

  2>函数形参:const char*cp:cp指向字符串形式的IP地址。

  3>函数返回值:函数成功后返回二进制的网络字节序的IP地址(struct in_add),否则返回-1. 4>inet_addr计算出来的是网络字节序的二进制IP 5>函数实例:#include #include #include

  #include #include #include int main()

  { char buffer[32];structin_addr in;in.s_addr =0;/*输入一个字符串形式的IP地址*/ printf(“pleaseinput the ip addrss\n”);scanf(“%s”,buffer);buffer[31]=‘\0’;printf(“original IPaddress:%s\n”,buffer);if((in.s_addr = inet_addr(buffer))==INADDR_NONE){ perror(“inet_addr”);exit(1);} else { printf(“after transfer:0x%0x\n”,in.s_addr);} 4.inet_network in_addr_t inet_network(constchar *cp)

  1>函数作用:将参数cp指向的字符串形式的网络地址转换为主机字节顺序形式的二进制IP地址。

  2>函数形参? const char *cp: cp指向字符串形式的IP地址。

  3>函数返回值:执行成功后返回转换后的结果,参数无效后返回-1. 4>inet_network返回的是主机字节序5>函数实例:#include #include #include

  #include #include #include int main()

  { char buffer[32];structin_addr in;in.s_addr =0;/*输入一个字符串形式的IP地址*/ printf(“pleaseinput the ip addrss\n”);scanf(“%s”,buffer);buffer[31]=‘\0’;printf(“original IPaddress:%s\n”,buffer);if((in.s_addr = inet_addr(buffer))==INADDR_NONE){ perror(“inet_addr”);exit(1);} else { printf(“after transfer:0x%0x\n”,in.s_addr);}总结:1.inet_addr和inet_network函数都是用于将字符串形式转换为整数形式用的;2.inet_addr返回的整数形式是网络字节序,而inet_network返回的整数形式是主机字节序。

  3.inet_addr 和inet_network有一个小缺陷,那就是当IP是255.255.255.255时,这两个函数会认为这是个无效的IP地址,这是历史遗留问题,其实在目前大部分的路由器上,这个 255.255.255.255的IP都是有效的。

  4.inet_aton认为255.255.255.255是有效的,所以建议使用inet_aton.并且inet_aton函数返回的是网络字节序的IP地址。

  二。进制IP地址转换为字符串形式的IP地址1.char * inet_ntoa(struct in_addr in);1>函数作用:将数值为in的网络字节序形式的二进制IP地址转换为字符串形式的IP地址。

  2>形参:struct in_addr in:指向二进制的IP地址3>函数返回值:执行成功返回结果字符串的指针,参数无效返回NULL. 4>函数实例:#include #include #include

  #include #include #include int main()

  { char buffer[32];char *str;structin_addr in;in.s_addr =0;/*输入一个字符串形式的IP地址*/ printf(“pleaseinput the ip addrss\n”);scanf(“%s”,buffer);buffer[31]=‘\0’;printf(“original IP地址:%s\n”,buffer);if( 0 == inet_aton(buffer,&in)){ perror(“inet_aton”);exit(1);} else { printf(“the first transfer:0x%0x\n”,in.s_addr);} printf(“begin two process:\n”);if( (str =inet_ntoa(in) == NULL ){ printf(“inet_ntoa:argumentinvalid\n”);} else { printf(“thesecond transfer:%s\n”,str);}说明:1.函数inet_ntoa()的返回值为一个指向字符串的指针,该内存函数inet_ntoa()每次调用都会重新覆盖,因此函数不安全,可能存在某种隐患2.实例:#include #include #include

  #include #include #include int main()

  { structin_addr ip1,ip2;char *str1 ;char *str2;ip1.s_addr = 192<<24| 168 <<16 |1 <<8 | 1;ip2.s_addr =255<<24 | 255 <<16 |255<<8|255;str1 = inet_ntoa(ip1);str2 = inet_ntoa(ip2);printf(“ip1:0x%xà%s\n”,ip1.s_addr,str1);printf(“ip2:0x%xà%s\n”,ip2.s_addr,str2);}输出结果为:ip1: 0xc0a80101à255.255.255.255;ip2:0xffffffffà255.255.255.255;表明函数inet_ntoa在进行二进制IP地址到字符串IP地址的转换过程中是不可重入的,这个函数转换两个不同的IP地址得到了同一个结果。此类函数在调用之后,需要立即将结果取出,没有取出结果之前不能进行同样函数的调用。

  三。最新的地址转换函数inet_pton()和inet_ntop()函数inet_pton和inet_ntop这2个函数能够处理ipv4和ipv6.算是比较新的函数了。inet_pton函数原型如下[将“点分十进制” -> “整数”],并且这两个函数是一套安全的协议无关的地址转换函数。所谓安全即这两个函数是可重入的,并且这些函数支持多种地址类型,包括IPv4和IPv6. 1.inet_pton()函数1>函数功能inet_pton()函数将字符串类型的IP地址转换为二进制类型。

  2>函数原型#include #include #include intinet_pton(int af, const char *src, void *dst);3>函数形参:? int af:af表示网络类型的协议族,在IPv4下的值为AF_INET;? src:存放需要转换的字符串? dst :存放转换后的结果,在IPv4下,dst指向结构struct in_addr的指针。

  4>函数返回值当函数inet_pton()的返回值为-1的时候,通常是用于af所指定的协议族不支持造成,此时errno的返回值为EAFNOSUPPORT;当函数的返回值为0时,表示src指向的值不是合法的IP地址;当函数的返回值为正值时,表示转换成功。

  2.inet_ntop()函数1>函数功能inet_pton()函数将二进制的网络IP地址转换为字符串。

  2>函数原型:#include #include #include intinet_nton(int af, const void *src, char *dst,socklen_t cnt);3>函数形参:? int af:af表示网络类型的协议族,在IPv4下的值为AF_INET;? src :为需要转换的二进制IP地址,在IPv4下,src指向一个structin_addr结构类型的指针。? dst指向保存结果缓冲区的指针? cnt的值是dst缓冲区的大小4>函数返回值Inet_ntop()函数返回一个指向dst的指针。当发生错误时,返回NULL.当af设定的协议族不支持时,errno设置为EAFNOSUPPORT;当dst缓冲区大小过小的时候errno的值为ENOSPC. 3.函数实例:#include #include #include

  #include #include #include int main(int argc,char *argv[])

  { struct in_addr ip;char ipstr[] = “192.168.1.1”;char addr[ADDRLEN];const char * str = NULL;int err = 0;if(err > 0){ printf(“inet_pton:ip %s value is :0x%x\n”,ipstr,ip.s_addr);} //把192.168.12.255转换为网络字节序ip.s_addr = htonl(192 << 24 | 168<<16 | 12<<8 | 255);str = (const char *)inet_ntop(AF_INET,(void*)&ip,(char *)&addr[0],ADDRLEN);if(str){ printf(“inet_ntop :ip 0x%x is%s\n”,ip.s_addr,str);}四。inet_makeaddr()函数,inet_lnaof()函数和inet_netof()函数1.struct in_addr inet_makeaddr(int net,int host)

  1>函数功能:一个主机的IP地址分为网络地址和主机地址,inet_makeaddr()函数将主机字节序的网络地址net和主机地址host合并成一个网络字节序的IP地址。

  2>函数形参:? int net:存放网络号参数(二进制形式的主机字节序)? int host:存放主机号地址(二进制形式的主机字节序)

  3>函数返回值:返回一个网络字节序的IP地址4>函数实例:unsigned long net,host;net = 0x0000007F;host = 0x00000001;struct in_addr ip = inet_makeaddr(net,hst);2.in_addr_t inet_lnaof(struct in_addr in)

posted @ 2012-08-18 23:14 手心上的冰山 阅读(1676) | 评论 (0) | 编辑 收藏
 
离职引发的诸多感触
 由于最近家里诸事繁多,目前的现状再也无法为继了,于是做出了一个重要的决定--离职。事实上,在现在的公司做出这样的决定也是顺理成章的事情,毕竟在去年的这个时候一起风雨同舟的同事们都已经陆续离职了,我是为数不多坚持到现在的项目成员了。当然我也并没有继续之前的项目,而且被外派到北京的另外一个项目中。刚到伊始,由于该项目组的压力很大,周围的同事们也都比较敬业,再有这也是我第一次正式和北京的同事们一起合作,当然也是我第一次感受到北京软件开发的浓浓氛围。坦白的讲,这个项目组所做的东西和我的技术匹配度并不是很高,还好大家都比较职业,并且分工明确,而分到我负责的部分也是相对比较擅长的,即核心逻辑组件的开发和设计,至于UI逻辑和底层技术攻关部分都交给了其他人。
      之前也有过几次辞职的经历,只是这次的感觉比较奇怪,可能是年龄的原因吧,确实不能做到年轻时那样毫无顾忌了。即便这次仍然能顺利找到新的工作,但是下一次呢?这种想法在之前的离职中是从未考虑的。有鉴于此,就用了将近一周的时间进行了深入的思考。离职的真实原因究竟是什么,即便是有多个理由,到底哪一个才是最最重要的呢?然后又想,离职的目的又是什么,也就是说为了什么,而这个目的一旦达到之后又能给你带来什么,所带来的这些又能对今后的发展有什么样的帮助,归根结底一句话,主因和长远的规划是否是匹配的。带着这样一连串的疑问开始了自我反省的过程。
      还是先说说原因吧。
      第一,家庭的负担。主要来自父母的身体,再有就是孩子还小,这样便无法保持这种每周出差的现状了。
      第二,工作感觉不好。每周在天津工作的时候,周围没有一个Team Member,连沟通和开开玩笑的人都没有,周围都是其他项目组的成员,没有一个认识的。
      第三,沟通效率偏低,有的时候因为一个很简单的Bug或新需求,不得不反复很多次,这种异地的工作方式在沟通上确实存在极大的障碍。
      第四,现有的工作和我未来的技术方向不是非常匹配。即便如此,毕竟是老Programmer了,应付起来问题不大。
      第五,对移动设备的开发仍然保持着极大的热情,希望能够继续从事这一方面的技术研发工作。
      第六、在了解之后,感觉外包公司的文化和所要求的技能对我来说,确实非常不合适。不过这里的项目运作方式确实给了我一种耳目一新的感觉。
      差不多就是这些了吧。从上面的几条中进行分析,第一条是刚性的需求,没有什么可说的,这是充分必要条件。而在其他原因之中,第五条是最最重要的,尽管已经Coding很多很多年,但继续Coding下去的热情仍然和当初一样炽烈,也真的希望能够兑现入行之初的誓言,Coding到退休。然而所不同的是,随着年龄的改变和经验的增多,不可能再像以前那样浑浑噩噩的去Coding了。自己一直非常渴望能够找到一个非常非常适合自己,同时又非常非常有兴趣的方向,在这条路上,最大化自己的经验值,用几年的时间做出一个真正属于自己,同时又能证明自己价值的软件产品来。

      当然每个人都希望有一份薪水丰厚的工作,毕竟需要养家糊口,生儿育女。所以我们在急于实现理想的同时也不能不顾及来自于现实的考验。我想,能够完全放得下去殊死一搏并最终取得成功的人应该是极少数,更多的人没有这样的勇气和运气。我作为一名老Programmer,在心态上相比于以前更为平和了,毕竟积蓄和收入都还算说的过去。然而希望赢得尊重的心却更为浓烈了,比如说,在最近这一年多的时间里,坚持写博客和技术分享;之前独立开发的服务器平台已经为多家小型公司使用并成功部署上线;随着架构设计经验的不断提升,设计期间考虑的视角也更为多样。尽管如此,心里始终还是埋藏着那个巨大的缺憾,为什么仍然没有一个真正属于自己的产品呢?为什么不能像一个建筑师那样告诉周围人,这个建筑是我的作品。

posted @ 2012-08-18 23:13 手心上的冰山 阅读(2798) | 评论 (2) | 编辑 收藏
 
cas ajax登录方式跨域时session丢失问题解决

问题:在ie浏览器下,域名A通过iframe的方式把登录信息post到域名为B的cas服务器上,经过debug发现A获取loginTicket时的sessionId和post过去时的sessionid不一致。只有ie浏览器有这个问题,火狐谷歌浏览器正常。

原因:查资料知道IE6/IE7支持的P3P(Platform for Privacy Preferences Project (P3P) specification)协议默认阻止第三方无隐私安全声明的cookie,火狐和谷歌目前还不支持P3P安全特性,所以它们不存在此问题了。

解决方法:在第一次获取loginTicket时在response中设置p3p声明,如下:

response.setHeader("P3P", "CP=CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR");

posted @ 2012-08-18 23:13 手心上的冰山 阅读(3351) | 评论 (1) | 编辑 收藏
 
Linux中修改IP

现在正在复习linux,将一些比较常用的东西记下来,一遍以后再次复习

使用root用户登录,或者用普通用户登录之后使用su root切换,相信正常人都会选择前者。

1、切换目录:

cd  /etc/sysconfig/network-scripts

2、使用vi编辑器打开ifcfg-eth0

vi  ifcfg-eth0

修改其中内容:

DEVICE=eth0

BOOTPROTO=static

IPADDR=192.168.1.3

NETMASK=255.255.255.0

GATEWAY=192.168.1.1

HWADDR=原来是什么就是什么

ONBOOT=yes

 

然后重启网卡服务:

service network restart

如果无法使用service,那么将/sbin加入环境变量,如果可以使用则不需要执行下面命令:

export PATH=$PATH:/sbin

 

 

这种修改方法是永久的,即使你重启后仍然有效,还有一种比较快捷的方法如下:

 

ifconfig eth0 192.168.1.3 netmask=255.255.255.0 up

立马生效。

posted @ 2012-08-08 23:47 手心上的冰山 阅读(1129) | 评论 (0) | 编辑 收藏
 
Linux中常用命令学习总结

前面的章节里对linux的文件的权限做了简单的解释。现在来看看常见的命令,从简单的开始:

1 、切换目录

cd

到/tmp 目录:cd /tmp

到上层目录:cd ..

2 、查看当前目录

pwd

3、 创建一个新的文件夹:

mkdir

创建一层目录:

创建多层目录:

4 、删除目录:

rmdir [-p]   如果需要层级删除目录,就需要带上p(只能删除空目录)

5、查询环境变量

echo $PATH  或者$PATH

 

6、切换用户:

su 用户名

7、移动文件

 

 

仔细阅读上面的命令,你会发现mv还可以对文件进行从命名,上面的命令将hellot.txt从a中移动到了b中,并改名为hello1.txt

 

8、查看文件与目录

ls

ls -a  目录名称:列出目录中所有的文件

ls -al 目录名:列出长字符串,包含文件的一些详细信息

如果没有给定目录名,那么就是当前目录

 

9、文件的复制:

cp [-adfilprsu] 源文件  目标文件    //将源文件拷贝到目标文件

cp  src1,src2,... des   //将多个源文件拷贝到目的文件夹

 

cp这个命令非常重要,不同的身份执行对命令产生不同的效果,尤其是-a,-p参数,对不同的身份来说,区别非常大。

例1:

使用root执行:

如果我们要将文件的所有的属性复制过来,则要加上参数-a

 

复制一个目录到另外一个目录 cp -r /src /desc

 

 

10 、移除文件或目录

rm [-fir] 文件或目录

-f 强制的意思,忽略不存在的文件,不会出现警告信息

-i互动模式:删除前,会询问是否删除

-r :递归删除

 

这里不再演示,记得之前的rmdir吗,只能删除空目录,所以删除非空目录只能使用rm -r

11、文件类容查询

cat

-b:列出行号

-n:列出行号,包括空白行

 

cat 是一次性将数据显示到屏幕上,如果想一页一页的看怎么办?

使用more命令

more在运行的过程中,你有几个按键可以按:

空格键:代表向下翻一页

Enter:代表向下滚动一行

q:离开more

b:往回翻页

 

 

12 、创建文件

touch

touch a.txt 就会在当前目录下创建a.txt

 

13、查找文件的命令

whereis ,find

whereis [-bmsu]  文件或目录名

-b:二进制文件

-m:只找在说明文件manual路径下的问津

-s:只找source源文件

-u:查找不在上述三个选项中的其他特殊文件

 

whereis ifconfig

 

下面看看find命令

 

find [path] [option] [actioin]

查找/home下面属于gavin的文件

find   /home  -user  gavin

查找系统中不属于任何人的文件

find /  -nouser

查找文件名为passwd的这个文件

find    /     -name passwd

查找文件类型为socket的文件

find   /    -type s

 

14、磁盘和目录的容量

df:列出文件系统的整体磁盘使用量

du:评估文件系统的磁盘使用量

15 创建链接文件

ln  [-sf] 源文件   目标文件

 

-s  :创建软连接,如果不加则是硬连接

-f:如果目标文件存在,则删除后再建

 

[root@localhost test2]# echo 'good'>a.txt
[root@localhost test2]# ls
a.txt
[root@localhost test2]# ln -s a.txt b
[root@localhost test2]# ls
a.txt  b
[root@localhost test2]# ll
total 12
-rw-r--r-- 1 root root 5 Aug  8 01:09 a.txt
lrwxrwxrwx 1 root root 5 Aug  8 01:09 b -> a.txt
[root@localhost test2]# echo 'hello'>>b
[root@localhost test2]# cat b
good
hello
[root@localhost test2]# cat a.txt
good
hello
[root@localhost test2]# ln a.txt c
[root@localhost test2]# ll
total 20
-rw-r--r-- 2 root root 11 Aug  8 01:09 a.txt
lrwxrwxrwx 1 root root  5 Aug  8 01:09 b -> a.txt
-rw-r--r-- 2 root root 11 Aug  8 01:09 c
[root@localhost test2]# echo 'bad'>>c
[root@localhost test2]# cat c
good
hello
bad
[root@localhost test2]# cat a.txt
good
hello
bad
[root@localhost test2]# cat b
good
hello
bad
[root@localhost test2]# rm a.txt
rm: remove regular file `a.txt'? y
[root@localhost test2]# cat b
cat: b: No such file or directory
[root@localhost test2]# cat c
good
hello
bad
[root@localhost test2]#

 

运行上面的命令行,相信你对ln的使用会非常清楚的。

 

15、挂载CD

16、文件压缩

tar

-c:创建一个压缩文件

-v:显示压缩过程

-f:给出压缩文件名

-x:解压文件

-t::查看压缩包中又哪些文件

posted @ 2012-08-08 23:46 手心上的冰山 阅读(7741) | 评论 (0) | 编辑 收藏
 
算法学习之字符串左移和右移

1.设计一个算法,把一个含有N个元素的数组循环右移K位,要求时间复杂度为O(N),

且只允许使用两个附加变量。
方法一:
每次将数组中的元素右移一位,循环K次,则实现了右移K位。
例如,
原始字符串:abcd1234
右移一位:4abcd123
右移一位:34abcd12
右移一位:234abcd1
右移一位:1234abcd
循环4次,则实现了右移4次

实现函数如下:

void right_shift(char *str, int N, int K)
{
    
char temp;
    K 
%= N; //目的是,当K > N时,移动K次与移动K-i*N次是一样的。
    while(K--)
    {
        t 
= str[N -1];
        
for(int i = N - 1; i > 0; i--)
            str[i] 
= str[i - 1];
        str[
0] = t;
    }
}

从上面的实现代码可以看出,
由于K %= N, 所以while循环的K值是小于N的。所以时间复杂度最大为O(N^2), 空间复杂度为O(1),不符合题目要求。

方法二:
对于原始字符串abcd1234,右移2位后为:34abcd12。
通过比较可以看出,有两段子字符串的顺序是不变的。abcd12和34。
则可发现,右移K位的过程就是把数组的两部分交换的过程。

例如:abcd12|34.
对abcd12逆序排列:21dcba
对34逆序排列:   43
对全部的21dcba|43进行逆序排列:34abcd21.

得出如下结论:
将字符串S="abcd1234"分为两部分X="abcd12"和Y="34"。那么S=(X, Y)
X逆序记为X'="21dcba"
Y逆序记为Y'="43"
则(X', Y')="21dcba43"整体再逆序为"34abcd12" = (Y, X)

即(X', Y')' = (Y, X)

代码实现如下:


void reverse(char* str, int begin, int end)
{
    char temp;
    for( ; begin < end; begin++)
    {
        temp = str[end];
        str[end] = str[begin];
        str[begin] = temp;
    }
}

void right_shift(char *str, int N, int K)
{
    K %= N;
    reverse(str, 0, N - K -1);
    reverse(str, N - K, N - 1);
    reverse(str, 0, N - 1);
}


该算法则实现了在线性时间内实现右移操作了。

编写主函数测试如下:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char str[] = "abcd1234";
    printf("The initial string:%s\n", str);
    
    right_shift(str, 8, 2);
    printf("The string after right shift:%s\n", str);
    
    system("pause");
    return 0
}

2.实现对字符串进行左旋转的函数,要求对长度为n的字符串操作的时间复杂度为O(n), 空间复杂度为O(1).
例如,原始字符串:abcd1234,左旋转2位后为:cd1234ab
通过上面的1的分析,只是把右移改为左移。其他方法相同。
代码实现如下: 

void reverse(char* str, int begin, int end)
{
    char temp;
    for( ; begin < end; begin++)
    {
        temp = str[end];
        str[end] = str[begin];
        str[begin] = temp;
    }
}

void right_shift(char *str, int N, int K)
{
    K %= N;
    reverse(str, 0, K -1);
    reverse(str, K, N - 1);
    reverse(str, 0, N - 1);
}

int main()
{
    char str[] = "abcd1234";
    printf("The initial string:%s\n", str);
    
    right_shift(str, 8, 2);
    printf("The string after right shift:%s\n", str);
    
    system("pause");
    return 0
}










 

posted @ 2012-08-08 23:45 手心上的冰山 阅读(6692) | 评论 (1) | 编辑 收藏
 
让数据库游标变得简单

(一)浅谈游标

      (1)游标的概念

      游标是指向查询结果集的一个指针,它是一个通过定义语句与一条Select语句相关联的一组SQL语句,即从结果集中逐一的读取一条记录。游标包含两方面的内容:

       ●游标结果集:执行其中的Select语句所得到的结果集;

       ●游标位置:一个指向游标结果集内的某一条记录的指针

       利用游标可以单独操纵结果集中的每一行。游标在定义以后存在两种状态:关闭和打开。当游标关闭时,其查询结果集不存在;只有当游标打开时,才能按行读取或修改结果集中的数据。

    (2)浅谈游标

         游标我们可以通俗的解释为变动的标示。正如它的解释一样,数据库中的游标其实也是一种读取数据的方式。举个简单的例子来说:我有一个电话本,电话本上的号码首先是按地域划分的,现在我想找个家住廊坊的李四。首先我们要做的是先找到廊坊地区的电话表,找到后的表也即是我们上面所说的游标结果集;而为了找到李四我们可能会用手一条一条逐行的扫过,以帮助我们找到所需的那条记录。对应于数据库来说,这就是游标的模型。所以,你可以这样想象:表格是数据库中的表,而我们的手好比是游标。

       总结来说游标就好比是在电话本上逐一扫描号码的手指。

(二)使用游标

       一个应用程序中可以使用两种类型的游标:前端(客户)游标和后端(服务器)游标,它们是两个不同的概念。

       但无论使用哪种游标,都必须经过如下的步骤:

       ●声明游标

       ●打开游标

       ●从游标中操作数据

       ●关闭游标

      下面我们主要讲述下服务器游标:

      (1)定义游标

         使用游标之前必须先声明它。声明指定定义游标结果集的查询。通过使用for update或for read only关键词将游标显式定义成可更新的或只读的。

      Declare cursor_name  cursor

     For select_statement

     [for{read only|update[of colum_name_list]}]

      举例:

      Declare  company_crsr cursor

      For select name,salary from company where salary>2000

      For update of name,salary

      上面我们声明了一个名为company_crsr的游标。

      (2)打开游标

         open的语法为:

                         open 游标名

        在声明游标后,必须打开它以便用fetch,update,delete读取、修改、删除行。在打开一个游标后,它将被放在游标结果集的首行前,必须用fetch语句访问该首行。

      (3)读取游标数据

      在声明并打开一个游标后,可用fetch命令从游标结果集中获取数据行。

      Fetch的语法为:

           Fetch [[Next | Prior | First | Last | Absolute{n|@nvar} |Relative {n|@nvar}]

         From]  游标名 [into 变量列表]

      参数说明:

      Next:返回结果集中当前行的下一行,如果该语句是第一次读取结果集中数据则返回的是第一行

      Prior:返回结果集中当前行的上一行,如果该语句是第一次读取结果集中的数据则无记录结果返回并把游标位置设置为第一行。

      First:返回游标第一行;Last:返回游标中的最后一行;

      Absolute{n|@nvar}:如果 n 或 @nvar 为正数,返回从游标头开始的第 n 行并将返回的行变成新的当前行。如果 n 或 @nvar 为负数,返回游标尾之前的第 n 行并将返回的行变成新的当前行。如果 n 或 @nvar 为 0,则没有行返回。n 必须为整型常量且 @nvar 必须为smallint、tinyint 或 int。

      RELATIVE {n | @nvar}:如果 n 或 @nvar 为正数,返回当前行之后的第 n 行并将返回的行变成新的当前行。如果 n 或 @nvar 为负数,返回当前行之前的第 n 行并将返回的行变成新的当前行。如果 n 或 @nvar 为 0,返回当前行。如果对游标的第一次提取操作时将FETCH RELATIVE 的 n 或 @nvar 指定为负数或 0,则没有行返回。n 必须为整型常量且@nvar 必须为 smallint、tinyint 或 int。

      举例:

       Fetch  next

       company_crsr into @name,@salary

      SQL Server在每次读取后返回一个状态值。可用@@sql_status访问该值,下表给出了可能的@@sql_status值及其意义。

    值意义:

             0——Fetch语句成功

             1——Fetch语句导致一错误

             2——结果集没有更多的数据,当前位置位于结果集最后一行,而客户对该游标仍发出Fetch语句时。

     若游标是可更新的,可用update和delete语句来更新和删除行。

     删除游标当前行的语法为:

                      Delete [from] 表名

                      where current of 游标名

     举例:delete from authors where current of authors_crsr

     当游标删除一行后,SQL Server将游标置于被删除行的前一行上。

     更新游标当前行的语法为:

       update 表名

       set column_name1={expression1|NULL|(select_statement)}

              [,column_name2={expression2|NULL|(select_statement)}

              [……]

       where current of 游标名

      举例:

        update company

        set name=”张三”,salary=”5000”

        where current of company_crsr

    (4)关闭游标

       当结束一个游标结果集时,可用close关闭。该语法为:

           close 游标名

       关闭游标并不改变其定义,可用open再次打开。若想放弃游标,必须使用deallocate释放它,deallocater的语法为:

        deallocater cursor 游标名

        deallocater语句通知SQL Server释放Declare语句使用的共享内存,不再允许另一进程在其上执行Open操作。

posted @ 2012-08-01 22:13 手心上的冰山 阅读(2873) | 评论 (0) | 编辑 收藏
 
oracle函数之case和decode的用法区别及性能比较

   在oracle世界,你可以使用:

    1)case表达式      或者

    2)decode函数

    来实现逻辑判断。Oracle的DECODE函数功能很强,灵活运用的话可以避免多次扫描,从而提高查询的性能。而CASE是9i以后提供的语法,这个语法更加的灵活,提供了IF THEN ELSE的功能。   

    case表达式

    case表达式,可分两种,简单和搜索,简单case后接表达式,如:

    对于简单的case需要几点注意:

    1)寻找when的优先级:从上到下

    2)再多的when,也只有一个出口,即其中有一个满足了expr就马上退出case

    3)不能把return_expr和else_expr指定为null,而且,expr、comparison_expr和return_expr的数据类型必须相同。

    搜索case:

    CASE WHEN condition THEN return_expr

              [WHEN condition THEN return_expr]

              ...

    ELSE else_expr

    END

    例子:

[sql] view plaincopy
  1. SELECT (CASE WHEN cust_credit_limit BETWEEN  0 AND 3999 THEN  ' 0 - 3999'  
  2.    WHEN cust_credit_limit BETWEEN  4000 AND 7999 THEN ' 4000 - 7999'  
  3.    WHEN cust_credit_limit BETWEEN  8000 AND 11999 THEN  ' 8000 - 11999'  
  4.    WHEN cust_credit_limit BETWEEN  12000 AND 16000 THEN '12000 - 16000' END)  
  5.   AS BUCKET, COUNT(*) AS Count_in_Group  
  6. FROM customers WHERE cust_city = 'Marshal' GROUP BY  
  7.  (CASE WHEN cust_credit_limit BETWEEN  0 AND 3999 THEN ' 0 - 3999'  
  8.  WHEN cust_credit_limit BETWEEN  4000 AND 7999 THEN ' 4000 - 7999'  
  9.  WHEN cust_credit_limit BETWEEN  8000 AND 11999 THEN  ' 8000 - 11999'  
  10.  WHEN cust_credit_limit BETWEEN  12000 AND 16000 THEN '12000 - 16000' END);  
  11.   
  12. BUCKET        COUNT_IN_GROUP  
  13. ------------- --------------  
  14.  0 - 3999                  8  
  15.  4000 - 7999               7  
  16.  8000 - 11999              7  
  17. 12000 - 16000              1  

    用decode可以违反第3NF(行不可再分,列不可再分,列不可重复):列重复

[sql] view plaincopy
  1. hr@ORCL> select * from a;  
  2.   
  3.         ID NAME  
  4. ---------- ----------  
  5.          1 a  
  6.          2 b  
  7.          3 c  
  8.          1 a  
  9.   
  10. hr@ORCL> select sum(decode(id,1,1,0)) think,  
  11.   2             sum(decode(id,2,2,0)) water,  
  12.   3             sum(decode(id,3,3,0)) linshuibin  
  13.   4        from a;  
  14.   
  15.      THINK      WATER LINSHUIBIN  
  16. ---------- ---------- ----------  
  17.          2          2          3  

 

    一个字段,decode函数可以完全改写简单case;

    多个字段,需要复杂的case,方可。

    语法:
    DECODE(value,if1,then1,if2,then2,if3,then3,...,else),表示如果value等于if1时,DECODE函数的
结果返then1,...,如果不等于任何一个if值,则返回else。可以用函数或表达式来替代value,if,then,else从而作出一些更有用的比较。

    来看看具体的运用:
    1 假设我们想给百度职员加工资,其标准是:工资在8000元以下的将加20%;工资在8000元以上的加
15%
    则:

    select decode(sign(salary - 8000),1,salary*1.15,-1,salary*1.2,salary) "revised_salary" from employee

    2 表table_subject,有subject_name列。要求按照:语、数、外的顺序进行排序
    则:

    select * from table_subject order by decode(subject_name, '语文', 1, '数学', 2, , '外语',3)

    decode和简单case的性能比较

 

   

Oracle的DECODE函数功能很强,灵活运用的话可以避免多次扫描,从而提高查询的性能。而CASE是9i以后提供的语法,这个语法更加的灵活,提供了IF THEN ELSE的功能。


对于很多情况,DECODE和CASE都能解决问题,个人更倾向于使用DECODE,一方面是从8i保留下来的习惯,另一方面是DECODE的语法更加的简洁,代码量要小一些。

不过今天在看Oracle9i的数据仓库手册时发现,Oracle在文档中提到CASE语句的效率会更高一些,尤其是CASE表达式 WHEN 常量 THEN的语法,效率要比CASE WHEN表达式 THEN的语法更高一些。对于后面这种说法倒是没有太多的疑问,对于CASE比DECODE效率高这种说法倒是第一次看到,印象中DECODE效率很高,应该不会比CASE的效率差。

到底效率如何,还是要具体的实例来说:

SQL> CREATE TABLE T AS
2 SELECT A.*
3 FROM DBA_OBJECTS A, DBA_MVIEWS;

Table created.

SQL> SELECT COUNT(*) FROM T;

COUNT(*)
----------
6075760

下面检查DECODE和两种CASE语句的效率:

SQL> SET ARRAY 1000
SQL> SET TIMING ON
SQL> SET AUTOT TRACE 
SQL> SELECT DECODE(OWNER, 'SYSTEM', 'SYSTEM', 'SYS', 'SYSTEM', 'USER') 
2 FROM T;

6075760 rows selected.

Elapsed: 00:00:07.24

Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4245K| 68M| 13828 (1)| 00:03:14 |
| 1 | TABLE ACCESS FULL| T | 4245K| 68M| 13828 (1)| 00:03:14 |
--------------------------------------------------------------------------

Note
-----
- dynamic sampling used for this statement


Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
47551 consistent gets
0 physical reads
0 redo size
46288564 bytes sent via SQL*Net to client
67317 bytes received via SQL*Net from client
6077 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
6075760 rows processed

SQL> SELECT CASE OWNER WHEN 'SYSTEM' THEN 'SYSTEM'
2 WHEN 'SYS' THEN 'SYSTEM' 
3 ELSE 'USER' END 
4 FROM T;

6075760 rows selected.

Elapsed: 00:00:07.22

Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4245K| 68M| 13828 (1)| 00:03:14 |
| 1 | TABLE ACCESS FULL| T | 4245K| 68M| 13828 (1)| 00:03:14 |
--------------------------------------------------------------------------

Note
-----
- dynamic sampling used for this statement


Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
47551 consistent gets
0 physical reads
0 redo size
46288578 bytes sent via SQL*Net to client
67317 bytes received via SQL*Net from client
6077 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
6075760 rows processed

SQL> SELECT CASE WHEN OWNER = 'SYSTEM' THEN 'SYSTEM'
2 WHEN OWNER = 'SYS' THEN 'SYSTEM' 
3 ELSE 'USER' END 
4 FROM T;

6075760 rows selected.

Elapsed: 00:00:07.23

Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4245K| 68M| 13828 (1)| 00:03:14 |
| 1 | TABLE ACCESS FULL| T | 4245K| 68M| 13828 (1)| 00:03:14 |
--------------------------------------------------------------------------

Note
-----
- dynamic sampling used for this statement


Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
47551 consistent gets
0 physical reads
0 redo size
46288585 bytes sent via SQL*Net to client
67317 bytes received via SQL*Net from client
6077 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
6075760 rows processed

posted @ 2012-08-01 22:09 手心上的冰山 阅读(22441) | 评论 (1) | 编辑 收藏
 
 
<2012年8月>
日一二三四五六
2930311234
567891011
12131415161718
19202122232425
2627282930311
2345678

 导航

  • BlogJava
  • 首页
  • 发新随笔
  • 发新文章
  • 联系
  • 聚合
  • 管理

 统计

  • 随笔: 17
  • 文章: 0
  • 评论: 10
  • 引用: 0

常用链接

  • 我的随笔
  • 我的评论
  • 我的参与
  • 最新评论

留言簿

  • 给我留言
  • 查看公开留言
  • 查看私人留言

随笔档案

  • 2012年8月 (8)
  • 2012年7月 (9)

搜索

  •  

最新评论

  • 1. re: oracle函数之case和decode的用法区别及性能比较
  • re: oracle函数之case和decode的用法区别及性能比较
  • --黄冰冰
  • 2. re: cas ajax登录方式跨域时session丢失问题解决
  • 需要修改CAS Server的源码?
  • --Liuzy
  • 3. re: 离职引发的诸多感触
  • 评论内容较长,点击标题查看
  • --思达商智
  • 4. re: 离职引发的诸多感触
  • 同感啊. 年纪大了, 有了老婆孩子, 就很难殊死一搏, 去做自己想做的事情了. 但总觉得如果此生连一件拿到出手的作品都没有, 实在是太遗憾了.
  • --黑灵
  • 5. re: 算法学习之字符串左移和右移
  • 评论内容较长,点击标题查看
  • --罗来枫

阅读排行榜

  • 1. oracle函数之case和decode的用法区别及性能比较(22441)
  • 2. Linux中常用命令学习总结(7741)
  • 3. 算法学习之字符串左移和右移(6692)
  • 4. cas ajax登录方式跨域时session丢失问题解决(3351)
  • 5. 让数据库游标变得简单(2873)

评论排行榜

  • 1. 离职引发的诸多感触(2)
  • 2. cas ajax登录方式跨域时session丢失问题解决(1)
  • 3. 算法学习之字符串左移和右移(1)
  • 4. oracle函数之case和decode的用法区别及性能比较(1)
  • 5. 多两个“0”带来的思考(1)

Powered by: 博客园
模板提供:沪江博客
Copyright ©2025 手心上的冰山