in

SDT Community Server

SDT Forums, Blogs, Photos server.

slash

  • 信凤姐,得自信


    PS达人将凤姐PS成阿凡凤·姐和《知音》《故事会》封面

     

    近期,凤姐其人其事其语录就像一阵飓风,强劲地登陆互联网。猫扑、天涯等论坛头版被其占领,豆瓣有了她的小组,百度有了她的贴吧,PS、段子恶搞成风。对于“向前三百年,向后三百年,总共六百年无人超过”的凤姐来说,究竟当今世上谁能符合她的择偶标准呢?昨日罗玉凤在接受一家视频网站访问时说:“我想强调一点,奥巴马是非常符合我心目中择偶标准的。”

    凤姐说,奥巴马符合她的择偶标准

    昨日,罗玉凤接受了一家知名视频网站访问。主持人对凤姐苛刻的征婚条件十分感兴趣,想知道在凤姐的心目中,世上究竟有谁符合她的标准。凤姐淡淡地说:“有一天我看到一本奥巴马演讲的书,我只看了一页,觉得还可以。”对于奥巴马这样的大人物,凤姐表示可以放宽一点要求,不必苛求奥巴马是清华北大毕业,也不是非要经济学硕士才行,只要名校毕业就行。于是,网友们终于“内牛满面”地看到有人符合凤姐的要求了,凤姐说:“我想强调一点,奥巴马是非常符合我心目中择偶标准的。”

    凤姐的择偶要求虽然苛刻到了极致,但是却只求才不求财,她表示:“即使两个人都一无所有,只要在一起,就能产生无限的想象力。”

    凤姐:爱因斯坦发明电灯

    凤姐曾经曰过:“往前推三百年,往后推三百年,总共六百年没有人超过我。”网友向她提问:“为什么是三百年,而不是五百年呢?”凤姐显得比较谦虚:“我这个人目光短浅,只能看到前后三百年的事情。”看到前三百年可以理解,看到后三百年又如何解释呢?凤姐的回答颇为神秘:“ 对于后三百年,我可以通过现在的事情进行推断。”

    接着,网友想尽了前三百年中的名人,最后搬出爱因斯坦,让凤姐比一比。“爱因斯坦不及我,不及我,差远了。”凤姐连想都没想,直接把爱因斯坦PK掉,“我不能发明电灯,我也不知道相对论,爱因斯坦的确影响了人类,但是他从宏观上对人类的调整,还是不如我。”主持人满头大汗问凤姐:“爱因斯坦究竟哪里比不上你呢?”凤姐答:“没有爱因斯坦发明电灯,还有其他人能发明电灯,他的才能可以复制,但是我的才能没人能复制。”网友也满头大汗,因为电灯是爱迪生发明的。

    “自信哥”敢于求婚

    这两天的猫扑、天涯等论坛头版上,多条帖子都被凤姐占领。凤姐的风采,引人尽折腰。同时,豆瓣有了她的小组,名字很霸气,叫“信凤姐,我自信”,创建没几天就有近400人参加,小组成员名叫“凤凰”。

    凤姐雷人要求也有“自信哥”,敢于向凤姐求婚。一位猫扑网友,便洋洋洒洒写下了“古今六百年,只有我能配上玉凤姐”的帖子。他说:“往前推三百年,往后推三百年,总计六百年,只有我才能配上凤姐,你们这些人不要笑,且听我细说。”

    他细说道:“本人自幼熟读唐诗二首:李白的《静夜思》与骆宾王的《鹅》。你们一般人是不可能背下来的,在俺村古今六百年,只有俺会背这两首诗。”至于身高方面,“自信哥”自称身高1米59,“比玉凤姐足足高了13厘米,俺村的老奶奶们都夸俺一表人才,所以我自信与玉凤姐的标准一致,可谓高大英俊。”

    《知音》和《故事会》都火了

    随着凤姐成为网络红人,她的玉照也不可避免地被网友ps,其中一张被ps成《阿凡达》的最为经典,网友取名为“阿凡凤·姐”。当然,凤姐所谓的社科人文书籍《知音》《故事会》也遭网友ps,封面竟是凤姐。在各大论坛上,到处可见网友回帖:“为了做一个有国际视野的人,我正在研读《知音》和《故事会》。”还有网友表示:“据可靠消息,《知音》和《故事会》这两本社会人文类的书近日销量大增。”但也有网友遇到了困难,“我也想提高自己的层次,向凤姐看齐,可是《知音》和《故事会》太难读懂了。”

    恶搞最为彻底的,算是猫扑网友的帖子“我在火车上揍了一偷看我《故事会》的小伙子”。帖子中写道:“在回家的火车上,我在座位上津津有味地读着我心爱的《故事会》,突然我感觉到后面有人在偷看我的书。我转头一看,原来是一个大概二十来岁的小伙子,他没买到座票站在我后面。”然后楼主就把小伙子揍了,并讲解说:“《故事会》,前三百年,后三百年,共六百年都很少有人能看懂,比《易经》更深奥,被称为天下第一奇书。《故事会》和《知音》一样,都是社会文学类巨著,是我这种高级白领才能读懂的。”最后,“小伙子流下了激动的泪水,他握着我的手,目光中充满了执着和坚定,‘你相信我,我以后一定会每天都背唐诗,争取早日能读懂《故事会》!’”

    罗玉凤-简介 

    罗玉凤[1],女,1985年8月9日生人,重庆綦江赶水镇人,身高1.46米,大专学历,在上海家乐福超市工作,月收入千余元,自称9岁博览群书、20岁达到顶峰,“前后300年无人超越”,曾在上海地铁站发过成千上万份征婚传单,也曾在电视台情感类节目上公布七大极为苛刻的征婚条件,誓嫁1.8米的名校硕士生,并且长得要阳光、帅气。因各种雷言囧语层出不穷、开出令人咋舌的高标准征婚条件,罗玉凤一“炮”而红,引起各路媒体和广大网民的关注,被网友戏称为“宇宙无敌超级第一自信”。罗玉凤自嘲称:青春就是拿来挥霍

    的,她现在正在挥霍青春。

    罗玉凤是重庆綦江赶水镇人,在綦江师范学校获得中师文凭,在重庆教育学院汉语言文学专业获得大专文凭。她自称懂诗画、会弹琴,精通古汉语,自称“9岁起博览群书,20岁达到顶峰,智商前300年后300年无人能及”。现主要研读经济类和《知音》、《故事会》等人文社科类书籍。罗玉凤还透露,7岁时她父母离婚。在离开重庆前曾谈过4次恋爱,但没有具体进展,都不了了之。

      罗玉凤自称,她曾经在重庆奉节黄泗小学做了两年的老师,学校开始让她教毕业班语文,一学期后,又将她调整到三年级,随后又调整到一年级。这所小学的老师表示对罗玉凤没有印象,只觉得她比较爱干净。

      到上海之后,为了找工作,她曾经投出了一万多份简历。从总经理到服务员,几乎各行各业她都试过。罗玉凤认为,从事金融业最适合自己,所以最终她选择在上海一个家乐福超市工作,月收入不过千余元。但走红后,她声称已经离开了该工作单位。

      罗玉凤仍在上海家乐福南方店作收银员

    罗玉凤其人其事

    罗玉凤,成名江苏卫视《人间》。在一次《人间》征婚节目中,罗玉凤口出雷言,“我九岁博览群书,二十岁达到顶峰。我现在都是看社会人文类的书,例如《知音》《故事会》……往前推三百年,往后推三百年,总共六百年没有人超过我。”她自称身高1米46,在网上单征男友,条件是“必须为清华北大硕士、身高1米76到1米83”。该期《人间》节目视频被网友发到网络上后,凤姐在网络迅速走红。

     

    罗玉凤-囧语  1.以我的智商和以我的能力的话,往前推三百年,往后推三百年,总共六百年之内不会有第二个人超过我。

      2.爱因斯坦绝对没我聪明,他发明电灯的嘛!

      3.必须具备国际视野,有征服世界的欲望。奥巴马才符合我的征婚标准。(谈择偶)

      4.本人找伴侣,一不求帅,二不求富。

      5.9岁博览群书,20岁到达顶峰,往前300年往后推300年,没有人会超过我。在智力上他们是不可能比我强的,那就在身高和外貌上弥补吧……

      6.他太老了,而且身高也不够。他也不可能是北大清华,更不可能是经济学专业,我不会选他(陈坤)。

      7.这个标准不高,这个标准很低。

      8.看到其他女的就他妈花痴一样。(疯了!上电视说脏话)

      9.过了三十岁自己滚蛋。

      10.男人过了三十岁就没看点了,就人老珠黄了。

      11.你给我十万。

      12.吾日三省吾身(凤姐念错字了)省应该读 xǐng,她读shěng。

      13.我用的是A4纸,因为A4是非常标准的纸。

      14.因为上海是一个经济中心,我这个人对征服经济世界蛮有兴趣的。

      15.我经常看的都是人文社会的书,例如《知音》、《故事会》。

      16.我一般按长相将人分五等。我是第三等。(主持人指着她前男朋友问:他是几等)他啊,没有等(捂嘴笑)……

      17.我在家乐福超市工作,世界500强。

      18.世界上有一半的男人看到我就想逃跑,另一半我看到他就想逃跑。

      19.我这个人有点洁癖,以前读书时衣服每天要洗,现在基本上过两三天就洗一次,洗头也这样,现在很多时间都浪费在这上面。

      20.罗指着台下众人说:你们这些普通院校的,如果撇开这上面的七条,你们肯定有人会愿意娶我……

      21.真的美女真多啊。我对面的一位男孩,开始很仔细的打量我,我想我应该还没有出名到他已经看过我的视频吧。有次有人对我说你上电视了,我说什么电视?人家说东方卫视啊。我打开电视,东方卫视正在播奥巴马的新闻。我觉得我和奥巴马之间还有很大的距离。做人,自知之明还是有的。

      22.你看看你的身高你的长相,我觉得你配不上我,我们之间差距太大了,带回家的话,我家里的人肯定会嘲笑我的,他们会说,罗玉凤,你找的男朋友怎么这么丢脸啊,连话都不会讲,我觉得我们还是算了吧,我一定能够找到一个比你好的多的男朋友。

      23.像我这样的一个人,独自在外面闯荡。很引人注目,可是自己想想。论交际,论人际关系,论工作能力。实在是不但女人,就是男人,也很难和我相比。论健康状况,却是一天不如一天的。我想我不能在这么下去了。

      24.我最喜欢的诗人是顾城 顾城(强调) 你知道吧???

      25.要说我写诗的风格嘛,比较像顾城,写文章嘛,人家都说我像鲁迅。

      26.山东走出去的我可以考虑一下,目前还在山东工作的不予考虑。

      27.凤姐:爱因斯坦宏观上不如我……

      激动网主持人:你指的宏观是?

      凤姐:把全人类更上一层吧。

      28.我弟弟长得很阳光帅气。

      你弟弟长得和你很像?

      对,很像!

      30.我在上海的时候,一直会有人在我肩膀上摸一摸什么的,有目的性的佧我油。

      31.(与主持人对话)

      —如果说他(指陈坤,凤姐的偶像之一)向你求婚了,跪在你面前了,玉凤你嫁给我吧,你会同意吗?

      —我不会同意的。首先他年龄三十三了,我觉得这个太老了哦。然后他身高可能应该也不足,然后他不是北大清华的,他根本就不可能是经济学专业的。

      32.中国人民银行、花旗银行、渣打银行、汇丰银行、交通银行、中国人寿等金融公司驻中国区首席执行官向我表达爱意,愿意与我结婚,而本人觉得他们年老色衰,所以不愿意。

      33.我的七大要求全国还能找到100个我认为不高,全国找到1个是还差一点,全国一个都找不出我认为是刚好。

      34.征婚者:“你从哪里来?”  罗玉凤:“我从地球来。”  征婚者:“好巧我也是。”

      35.我爱干净,比较洁癖,男朋友看到女的别他妈花痴样。

      36.主持人:你觉得爱因斯坦也不及你聪明么?

      罗玉凤:不及我不及我,差远了,他是一名科学家。

      主持人:对,他是一名科学家,但是你知道他所知道的东西么?

      罗玉凤:我不知道他知道的东西,我不能发明电灯。

      37.罗玉凤在谈自己最喜欢的一首诗,是凤姐自己写的:天还没有黑,天已经黑了。

      38.我平常接触的朋友多,因为我交际面广嘛。

      39.凭我的智慧,我的相貌,我完全可以找一个比他(指前男友)更优秀的男朋友。

      40.你去死!

      41.很多人都说我漂亮。我也知道我漂亮。

      42.我喜欢蓝莓的味道。蓝莓是一种优雅的水果,即使我毫无姿态地坐在路边的水泥地,拣起没有洗过的蓝莓塞进嘴里,我依然认为我是优雅的,因为优雅的蓝莓。

     
    罗玉凤-征婚标准

      本人找伴侣。一不求帅。二不求富。但求同甘苦,共患难。

      本人对伴侣要求如下:[3]

      第一,必须为北京大学或清华大学硕士毕业生。必须本科硕士连读,中途无跳级,不留级,不转校。在外参加工作后再回校读书者免。

      第二,必须为经济学专业毕业。非经济学专业毕业则必须精通经济学。或对经济学有浓厚的兴趣。

      第三,必须具备国际视野,但是无长期定居国外甚至移民的打算。

      第四,身高176--183左右。长得越帅越好。

      第五,无生育史。过往所有女友均无因自身而致的堕胎史。

      第六,东部沿海户籍,即江,浙,沪三地户籍或广东,天津,山东。北京,东北三省,内蒙古等地户籍。西南地区即重庆。贵州。云南。西藏,湖南,湖北等地籍贯者不予考虑。

      第七,年龄25--28岁左右。即06届,07届,08届,09届毕业生。有一至两年的工作经验,06级毕业生需年龄在28岁左右,09级毕业生则需聪明过人。且具备丰富的社会实践经验。就职于国家机关,国有企事业单位者不愿考虑。但就职于中石油,中石化等世界顶尖型企业或银行者又比较喜欢。现自主创业者要商榷一番了。

      本人85年旧历8月初9日生。新历生日为9月23日。身高146。平时穿高跟鞋153。体重40kg.先就读于綦江师范学校获中师文凭。

      后连读重庆教育学院获汉语言文学专业大专文凭。懂诗画,唱歌,弹琴,刺绣等。最擅长诗歌与散文。并精通古汉语。博览群书。较为狂妄。无堕胎史,无生育史。交过几个不了了之的男朋友。具体进展却无。主要要求男方身家清白,聪慧过人。

  • 使用 BlackBerry 8800 半年啦~

    从认识黑莓到现在, 半年; 从使用黑莓到现在, 半年. 没错, 我几乎是认识后不久就买了的, 对于一个对自己完全陌生的品牌, 这样的行动, 很大胆吧~~ 嘿嘿, 事实证明, 我是对的, 黑莓, 满足了我的需求, 或者说, 是欲望.

    不知道黑莓是啥的同学呢, 先看看维基百科吧: 黑莓
    这个解释不错吧 :-) 嘿嘿.. 跟同学们开了个小小的玩笑~~ 其实, wiki 上的解释在这里~~

    好了, 言归正传. 我买的是 BlackBerry 8800(ZY倒置版), 实物照片如下(真的是我买的实物啊!!)

    BlackBerry 8800

    P.S.: 由于使用有一段时间了, 轨迹球已经洁白不再了…. 背景呢, 是我们学校的课桌啦~~

    至于黑莓手机有什么特点啊什么的, 已经给广大博友写到烂了, 我就不再重复了, 就只写我对 BlackBerry 8800 的感觉. 有想知道其他型号的情况同学, 可以直接问 google 哦~~ 先给出 8800 的参数(仅显示我认为有用的)啦~

    手机型号: BlackBerry 8800
    手机制式: GSM (毕竟 3G 咱还用不起.. 就不去考虑了)
    网络连接: GSM/GPRS/EDGE (有 wi-fi 功能的是 8820~考虑到资金问题….)
    内存容量: 64 M (拿来装软件的..)
    电池类型: 锂电池 (….)
    屏幕分辨率: 320*240像素 (还是比较大的了^_^)
    主屏颜色: 26万色 (肃静.. 路过……)
    主屏尺寸: 2.4英寸 (同屏幕分辨率..)
    主屏材质: TFT
    视频支持: 3GP(H.263)、MP4、RealVideo、WMV等
    音频支持: MP3、AMR-NB、AAC、AAC+、eAAC+、WAV、WMA等
    浏览器    : HTML 浏览器
    GPS 与 蓝牙: 支持
    存储卡    : microSD(T-Flash)

    这样.. 我决定采用先抑后扬的手法来写我对我的 bb 半年来的感觉吧~

    我对 BB8800 的不满:

    • 娱乐功能太弱
      (哼..  谁让你个小 P 孩没钱买 8300 的..)
      恩.. 是的, 8800 的定位是商务机, 而娱乐机是 8300, 所以, 这点以及音质不佳等娱乐功能上的瑕疵都不能强求. 但是.. 但是…. RIM 啊.. 你是怎么想的啊, 为什么 8800 在放歌的时候, 长按增大音量键可以返回上一首, 而长按减小音量键却是重新开始正在播放的歌曲…… 这点就是你的不对啦!!
    • 重启的时间里, 一定要准备好一首歌
      因为.. 重启(因为安装了软件或者插拔电池而重启, 并不是普通的关机)需要 3-5 分钟.. 听一首歌吧~ 听完就重启好了….
    • 不能自动锁键盘
      非得用户使用第三方软件……
    • 蓝牙模式太繁琐
      在想要文件的时候, 除了对没有匹配过的机子进行匹配外, 还需要手动进入多媒体, 然后选择从蓝牙设备接收, 之后才能接收文件, 接收到信号后, 还得确认是否接收….
      虽然官方的解释是安全第一!! 但是.. 能不能给要求没那么高的用户简化的步骤呢?? 毕竟.. 遇到蓝牙攻击的概率不高吧.. 遇到了, 我要去买彩票!!
    • “多媒体” 没有快捷键
      很多很多有用没有的自带功能都有快捷键, 唯独 “多媒体” 没有.. 真 “人性化” 啊..
    • 插 TF 卡的位置
      非得掰开整个后盖才能取出来.. 挫, 真挫!!
    • 开/锁键盘的过程不是互逆的
      开键盘是: A+拨号键, 而锁键盘直接在大屏幕按 K 就可以了..
      而我一些同学, 开锁之后, 想解锁的时候, 直接 A+拨号键…. 打电话给了我联系人(A 是联系人的快捷键)的第一位…. 我爸………

    在说优点前, 来张背面的大图~~ 顺便看看 TF 卡的位置…..

    BlackBerry 8800

    好了, 接下来, 就说它的可爱的地方吧^_^

    • 自动补全
      在浏览器里面输入网址的时候, 可以直接用空格键来输入网址中的 “.”;
      在输入 email 的时候, 第一次按空格键, 将出现 “@”, 第二次输入就会出现 “.”;
      在发英文短信的时候, 输入 “ive” 然后空格, 就会变成 “I’ve”, 诸如此类.
    • 信号控制
      可以在连接管理里面选择开关通讯信号及蓝牙, 不用关机就可以达到避免电话短信的烦扰(考试的时候.. 嘿嘿), 多好啊~
    • 软件快捷键
      在大屏幕或者主屏幕, 直接按键(前提是关闭了屏幕拨号)就可以进入某个程序, 比如 “M” 进入短信(Massage), 很棒的.
    • 主屏幕软件图标可以隐藏
      我把短信的图标给隐藏了…….. 嘿嘿.
    • 多任务执行
      超超超喜欢的功能!! 顾名思义, 就是能够同时执行多个软件啦~ 比如, 我开个 Opera Mini 国际版, 再开个 Opera Mini 中国版一起来冲浪…. 嘿嘿. 还有就是开个字典软件, 英语课装 B 专用..
    • 轨迹球
      这个太太好玩了!! 嘿嘿…. 转来转去也巨方便~ RIM 的无敌创新啊!! 唯一不好的是.. 洁白的轨迹球会慢慢的, 慢慢的, 变黄… 唉, 机老珠黄啊!!
    • LED 指示灯
      有电话/短信/任务等的时候, 红灯提醒; 蓝牙连接的时候, 蓝灯提醒; 剩余电量 <= 5% 的时候, 黄灯.. 巨人性化啊!! 而且, 有软件可以调节等的颜色, 多彩缤纷哦~~
    • 剩余电量实在不多的时候, 强制性关闭通讯信号
      这样就避免了由于电池没电而关机了^^ 别忘了…. 因电池没电而重启是要去听一首歌的..
    • BrickBreaker 太好玩!!
      这直接导致我的手机好长一段时间都不在我手上..
    • 可以装 B
      我刚买会宿舍的时候, 同学们都问: 这是什么啊?
      我答: 黑砖头.
    • 与美国总统有交集
      大家都知道, 奥巴马同学用的是 BlackBerry 8700.. 跟他套近乎去吧~

    差不多就这些了吧.. 接下来, 给大家看看我现在用的主题吧(背景是我们凤凰木的海报^^~)

    这个主题很熟悉吧?? 没错, 就是深受大众喜爱的 T-Mobile 主题吧~ 是 BlackBerry 8800 的默认主题. 想不到吧, 我最喜欢的主题竟然是这个~~ 我把好些可以用快捷键进入和对我没用的图标都隐藏了, 呵呵, 简约美呀!!

    最后呢, 就来介绍下现在我装的软件吧.

    • Opera Mini 国际版 + 中国版
      这个就不用多说了吧, 官方版本, 爽!!
    • BerryDict
      采用 Dict.cn 来进行在线查询的简约版字典.
      可自选 cmnet/cmwap 接入点
    • TwitterBerry
      个人最喜欢的黑莓 Twitter 客户端
    • BBFetion && BerryMail-Fetion2
      前者可以直接通过代理连接, 即使你的 APN 是 cmnet, 界面简单, 功能也简单
      后者只能当 APN 为 cmwap 的时候使用, 界面豪华, 功能也豪华
    • Google Mobile App
      Google 手机软件管理程序, 包括 Gmail, Google Map, Google Sync(手机联系人与 Gmail 及手机日程及 Google Calendar 同步的软件
    • gCalc!
      一款无比强大的计算器.. 可以绘图!!
    • Documents To Go
      可以让黑莓打开并编辑 doc/ppt/xls 文件.. 强大无比..
    • BBFileScout
      一款超级强大的黑莓文件管理器, 可以压缩及解压 zip 文件
    • JadBuilder
      根据存储卡中的 cod 文件生成 jad 文件保存在相应目录

    历史上用过的软件其实有很多, 但是, 能在历经半年后存活下来的, 就只有这些了, 可见, 是精品软件来的啊!!(自恋 + 自大..)

    入手半年了才写关于黑莓的文章.. 呼.. 总算又了了一件事, 最后呢, 给大家看张夜景吧~ 撤退咯.

    BlackBerry 8800

  • 虚拟桌面工具-Simpledesktops

    类似Linux 的 多桌面。

     用法好简单:热键ALT + 【1,2,3,4】对应4 个桌面。

  • IIS 6 关于 Service Unavailable 解决方法

    今收到用户疑问,网页打不开 出现Service Unavailable 提示,解决方法:临时对应开启被自动禁用的应用程序池,后续优化网站。

     

    问:为什么我的网站有时会出现"Service Unavailable"的提示。
      答: 出现这种情况是由于您的网站超过了系统资源限制或者是IIS连接数(由于我们虚拟主机是采用2003的操作系统,2003的操作系统在提示IIS过多时并非像2000系统提示"链接人数过多",而是提示"Service Unavailable",如果出现这种情况,您可以选择升级主机,或者查看是否有人盗链。

      可以经常更改您放软件、图片的目录)造成的,主要是程序占用资源太多。比如同样是100人在线的论坛,雷傲论坛所占的资源就是动网论坛所占资源的10倍以上;另外,一些死循环程序,或者不优化的程序都会占用太多的系统资源,而系统资源明显是有限的。由于我们的新虚拟主机是采用WINDOWS2003的操作系统,各网站之间是以独立进程运行的,不会相互影响。

      如果一个网站的程序占资源太多或者发生太多的错误,系统日志就会提示:"应用程序池 'west263pool11' 被自动禁用,原因是为此应用程序池提供服务的进程中出现一系列错误,或者提示:应用程序池 'hui999' 超过了其作业限制设置。有关更多信息,请参阅在 http://go.microsoft.com/fwlink/events.asp 的帮助和支持中心。 这时,访问这个网站就会提示:Service Unavailable。一般系统会在30秒左右恢复正常,多刷新几次就能正常访问了。

      如果经常出现类似的错误,请及时优化网站程序,或者升级你的虚拟主机至更高的款型,以获得更多的系统资源。

  • Oracle Temporary Tables(Oracle 临时表)

    Temporary Tables临时表
    1简介
       ORACLE数据库除了可以保存永久表外,还可以建立临时表temporary tables。这些临时表用来保存一个会话SESSION的数据,或者保存在一个事务中需要的数据。当会话退出或者用户提交commit和回滚 rollback事务的时候,临时表的数据自动清空,但是临时表的结构以及元数据还存储在用户的数据字典中。
       临时表只在oracle8i以及以上产品中支持。
    2详细介绍
       Oracle临时表分为 会话级临时表 和 事务级临时表。
    会话级临时表是指临时表中的数据只在会话生命周期之中存在,当用户退出会话结束的时候,Oracle自动清除临时表中数据。
    事务级临时表是指临时表中的数据只在事务生命周期中存在。当一个事务结束(commit or rollback),Oracle自动清除临时表中数据。
    临时表中的数据只对当前Session有效,每个Session都有自己的临时数据,并且不能访问其它Session的临时表中的数据。因此,临时表不需要DML锁。
       当一个会话结束(用户正常退出 用户不正常退出 ORACLE实例崩溃)或者一个事务结束的时候,Oracle对这个会话的表执行 TRUNCATE 语句清空临时表数据.但不会清空其它会话临时表中的数据.
    你可以索引临时表和在临时表基础上建立视图.同样,建立在临时表上的索引也是临时的,也是只对当前会话或者事务有效.   临时表可以拥有触发器.
    3建立临时表
       临时表的定义对所有会话SESSION都是可见的,但是表中的数据只对当前的会话或者事务有效.
       建立方法:
    1) ON COMMIT DELETE ROWS 定义了建立事务级临时表的方法.
    CREATE GLOBAL TEMPORARY TABLE admin_work_area
            (startdate DATE,
             enddate DATE,
             class CHAR(20))
          ON COMMIT DELETE ROWS;
    EXAMPLE:
    SQL> CREATE GLOBAL TEMPORARY TABLE admin_work_area
      2          (startdate DATE,
      3           enddate DATE,
      4           class CHAR(20))
      5        ON COMMIT DELETE ROWS;
    SQL> create table permernate( a number);
    SQL> insert into admin_work_area values(sysdate,sysdate,'temperary table');
    SQL> insert into permernate values(1);
    SQL> commit;
    SQL> select * from admin_work_area;
    SQL> select  * from permernate;
    A
    1
    2)ON COMMIT PRESERVE ROWS 定义了创建会话级临时表的方法.
    CREATE GLOBAL TEMPORARY TABLE admin_work_area
            (startdate DATE,
             enddate DATE,
             class CHAR(20))
         ON COMMIT PRESERVE ROWS;
    EXAMPLE:

    会话1:
    SQL> drop table admin_work_area;
    SQL> CREATE GLOBAL TEMPORARY TABLE admin_work_area
      2          (startdate DATE,
      3           enddate DATE,
      4           class CHAR(20))
      5       ON COMMIT PRESERVE ROWS;
    SQL> insert into permernate values(2);
    SQL> insert into admin_work_area values(sysdate,sysdate,'session temperary');
    SQL> commit;
    SQL> select * from permernate;

             A
    ----------
             1
             2

    SQL> select * from admin_work_area;

    STARTDATE  ENDDATE    CLASS
    ---------- ---------- --------------------
    17-1ÔÂ -03 17-1ÔÂ -03 session temperary

    会话2:

    SQL> select * from permernate;

             A
    ----------
             1
             2

    SQL> select * from admin_work_area;

    未选择行.

    会话2看不见会话1中临时表的数据.
    Posted May 13 2009, 10:04 AM by slash with no comments
    Filed under:
  • 某一类的数量占总数的比例的函数--ratio_to_report

    drop table test;

    create table test(code varchar2(10),qty number);

    insert into test values('a',1);
    insert into test values('a',3);
    insert into test values('b',2);
    insert into test values('b',3);
    insert into test values('b',4);
    insert into test values('c',3);
    insert into test values('c',3);
    insert into test values('c',2);

    select code,qty,ratio_to_report(qty) over( partition by code) from test;

     

    Result:

    a     1                                         0.25                                    
    a     3                                         0.75                                    
    b     2                                         0.222222222222222222222222222222222222222
    b     3                                         0.333333333333333333333333333333333333333
    b     4                                         0.444444444444444444444444444444444444444
    c     3                                         0.375                                   
    c     3                                         0.375                                   
    c     2                                         0.25                                    

     

    很好很强大!

  • 福布斯:美国年轻人最喜欢的15大网站

    福布斯网站发表文章指出,大约3/4的美国人都有自己的网上生活,每个人的网上生活千差万别,然而,最近市场调研公司Forrester Research的一份消费者网上生活调查显示,网上生活按年龄段不同显示出不同趋势。

    美国第X代,也就是在29岁到42之间的人群,一周里在网上花的个人时间大约有8小时,基本都是收发邮件或者浏览一些产品介绍等,而像博客、社交网络等用得很少。

      但是,大约占网民数80%的美国第Y代年轻人(18到28岁之间),他们每周大约有10多个小时的个人时间在网上度过,他们是网络最流行应用的体验者和促进者。博客、视频、社交网站,他们都是常客。以下是美国第Y代的年轻人最常去的15大网站:

      1,Hulu.com

      类型:视频

      在Hulu上,可以看到最流行、最热门的电视剧。但是,需要忍受一下视频上出现的广告。



      2,Songza.com

      类型:音乐

      Songza就像一个可以定制的广播电台,你可以输入你想听的歌曲的名字,Songza会把那些歌曲按播放列表排列出来并播放。可惜的是,你不能下载那些歌曲。



      3,Veoh.com

      类型:视频

      Veoh自2006年创立,迪斯尼前CEO迈克尔•埃斯纳(Michael Eisner)加入其董事会是当年的一大新闻。Veoh上不仅有用户们自己上传的视频,还有经过版权授权的视频,相比YouTube来说,Veoh做得更专业一些。



      4,Digg.com

      类型:社区型内容

      Digg创立于2004年,Digg的思路是让用户推荐自己感兴趣的内容,凭着这种独特的思路,这种社区型内容风靡全球。当你看到自己感兴趣的内容,只要"Digg"它就可以在社区内进行分享。



      5,Stumbleupon.com

      类型:社区型内容

      用户根据他们分享的内容,找到志同道合的圈子,这就是Stumbleupon的思路。Stumbleupon在2006年被eBay收购,从此用户数猛增。




    6,Thesixtyone.com

      类型:社区型音乐

      这是以美国著名的一条高速公路61命名的网站,旨在扮演"大众音乐发现者"的角色。音乐人可以在线提交自己的音乐作品,而网站用户可以根据自己 的喜好,通过给予"撞击(bump)"来评价音乐作品。但用户手中的"bump"必须通过参与一些活动才能获得,比如每天登录也能获得"bump"。 Thesixtyone.com首页的音乐家可能都不太有名,但都是由用户自己评选出来的。



      7,Friendfeed.com

      类型:信息聚合

      用户在很多社交网站上都有各自的Profiles,比如YouTube、StumbleUpon和Twitter等,要想管理这些 Profiles也是件麻烦的事情。Friendfeed就是专注于这些信息的聚合,它跟踪用户好友的实时动态,比如发表了新的博客、上传了新的视频等, 将这些信息不间断的报告给用户。Friendfeed成立于今年二月,但已经和Google、亚马逊、Digg等建立了合作关系。



      8,Viewzi.com

      类型:搜索

      Viewzi将Google和雅虎上最佳的搜索结果组合在一起,而且还可以还提供模板允许让用户设置搜索结果的显示方式。



      9,Twitter.com

      类型:社交网站

      微型博客Twitter可以让用户用简单的话告诉圈子里的人你正在做什么、想什么。当Twitter和手机结合的时候,这种实时更新显得更有用。



      10,Adultswim.com

      类型:办公室娱乐网站

      当你疲于工作的时候,给自己放松一下,Adultswim.com上有大量的搞笑短片以及有趣的网页游戏。




     11,Wegame.com

      类型:视频

      Wegame一定是游戏狂热分子的杰作,用户可以上传一些游戏视频片段供人欣赏。



      12,Funnyordie.com

      类型:视频

      Funnyordie上有很多用户自己上传的原创视频,另外,Funnyordie允许用户对某个视频给出"搞笑"或者"下架"的评价。



      13,Stitcher.com

      类型:网络收音机网站

      网络收音机在当今这个新媒体时代有点没有得到它该有的地位。然而据研究公司Forrester Research的数据,约35%的美国第Y代人都在收听这个网络收音机。



      14,Lifehacker.com

      类型:博客

      Lifehacker博客提供很多生活上的how-to指导,比如提供节约时间、金钱等建议。



      15,Gizmodo.com

      类型:博客

      根据市场调研公司Forrester Research研究,大部分美国人在购买一些大件商品之前都会在网上研究这些商品。Gizmodo就是提供电子产品的一些研究信息博客。(柯柯编译)

    Posted Apr 28 2009, 09:51 AM by slash with no comments
    Filed under:
  • Google Update 宣布开源

     在安装了谷歌拼音输入法、谷歌浏览器、谷歌软件精选后,通常会有一个名为“Google Update”的进程,用于及时更新谷歌的客户端软件。虽然,这省却了用户自己手动更新的麻烦,但却让不少用户厌烦和担忧——不仅占用内存,还会有隐私泄露的隐患。今天,为了消除用户的担忧,谷歌宣布将Google Update项目开源,项目名为奥马哈(Omaha)。

      谷歌在官方博客中表示:

      Google Update采用Apache License 2.0许可证,以奥马哈(Omaha)为代号发布源代码。Omaha的功能主要是在不干扰用户并且不中断功能的情况下自动更新,在后台自动检查有无更新,这一切都是为了让用户有更好的体验。

      现在,谷歌公开了Google Update源代码,允许用户可以自己控制更新进程。不过,具体怎么做,还得懂行的用户自己动手了……

  • 三种主要的嵌入式数据库

     什么是嵌入式数据库


    嵌入式数据库与非嵌入式数据库的差别,在于运行模式的差别。并不是运行在嵌入式手持设备上的数据库就

    是嵌入式数据库,那种数据库我们通常称做嵌入式移动数据库。理论上讲,嵌入式设备一样可以运行网络数

    据库的服务端程序。

    嵌入式数据库是指运行在本机上、不用启动服务端的轻型数据库,它与应用程序紧密集成,被应用程序所启

    动,并伴随应用程序的退出而终止。

    从这个意义上讲,似乎所有单机数据库都可以算嵌入式数据库,比如Access,Paradox,DBF等等,因为

    它们都不用启动数据库服务器即可使用。然而,我们通常不将上述数据库归入嵌入式数据库,而只将它们归

    入“桌面数据库”,甚至“文件型数据库”,因为这些数据库的完备性、存储容量及性能方面存在较大的缺陷。

    嵌入式数据库支持的数据都是TB文件级别,更由于嵌入式数据库具备高性能的特点,可以预测,单机数据库

    的未来将是嵌入式数据库的天下。

    嵌入式数据库三雄
    目前,嵌入式数据库市场主要由三个产品分割:SQLite,Birkeley DB,Firebird嵌入服务器版,巧的是,

    这三个数据库产品都是开源软件。

     

    SQLite

    主页:http://www.sqlite.org

    SQLite诞生于2000年5月,这几年增长势头迅猛无比,目前版本是3.3.8。

    SQLite的特点如下:

    1、无需安装配置,应用程序只需携带一个动态链接库。

    2、非常小巧,For Windows 3.3.8版本的DLL文件才374KB。

    3、ACID事务支持,ACID即原子性、一致性、隔离性、和持久性(Atomic、Consistent、Isolated、和

    Durable)。

    4、数据库文件可以在不同字节顺序的机器间自由的共享,比如可以直接从Windows移植到Linux或MAC。

    5、支持数据库大小至2TB。

     

     

    Berkeley DB

    主页:http://www.oracle.com/database/berkeley-db/index.html

    Berkeley DB是由美国Sleepycat Software公司开发的一套开放源码的嵌入式数据库的程序库,它于

    1991年发布,号称“为应用程序开发者提供工业级强度的数据库服务”,可谓是老牌悍将。Sleepycat现已被

    甲骨文(ORACLE)公司收购。

    Berkeley DB的特点如下:

    1、嵌入式,无需安装配置。

    2、为多种编程语言提供了API接口,其中包括C、C++、Java、Perl、Tcl、Python和PHP等等。

    3、轻便灵活。它可以运行于几乎所有的UNIX和Linux系统及其变种系统、Windows操作系统以及多种嵌

    入式实时操作系统之下。

    4、可伸缩。它的Database library才几百KB大小,但它能够管理规模高达256TB的数据库。它支持高并

    发度,成千上万个用户可同时操纵同一个数据库。

     

     

    Firebird嵌入服务器版(Embedded Server)

    主页:http://www.firebirdsql.org

    从Interbase开源衍生出的Firebird,充满了勃勃生机。虽然它的体积比前辈Interbase缩小了几十倍,但

    功能并无阉割。为了体现Firebird短小精悍的特色,开发小组在增加了超级服务器版本之后,又增加了嵌入

    版本,最新版本为2.0。

    Firebird的嵌入版有如下特色:

    1、数据库文件与Firebird网络版本完全兼容,差别仅在于连接方式不同,可以实现零成本迁移。

    2、数据库文件仅受操作系统的限制,且支持将一个数据库分割成不同文件,突破了操作系统最大文件的限

    制,提高了IO吞吐量。

    3、完全支持SQL92标准,支持大部分SQL-99标准功能。

    4、丰富的开发工具支持,绝大部分基于Interbase的组件,可以直接使用于Firebird。

    5、支持事务、存储过程、触发器等关系数据库的所有特性。

    6、可自己编写扩展函数(UDF)。

    嵌入式数据库特性对比
    产品名称

    SQLite

    Berkeley DB

    Firebird嵌入服务器版


    当前版本

    3.3.8

    4.5.20

    2.0


    速度

    最快






    稳定性








    数据库容量

    2TB

    256TB

    64TB


    SQL支持

    大部份SQL- 92

    不支持

    完全SQL-92与大部份SQL-99


    Win32平台下最小体积

    374KB

    840KB

    3.68MB


    数据操纵

    SQL

    仅应用程序接口

    SQL


    开发接口

    C, C++, PHP, Java, Delphi, Python .net(有些是第三方厂商开发的)

    从以上对比中,我们可以看到,最短小精悍的是SQLite,它的性能也是最高的,Berkeley DB比较特殊,

    因为它不是用SQL语言来操纵数据的,Firebird嵌入版的体积对比之下显得稍大了些,但它对关系数据库特

    性的支持是最好的,如果要考虑到今后或许要将数据库升级成网络版本,就要选Firebird了。

    嵌入式数据库开发布署举例
    例1、用Delphi开发基于SQLite的单机版应用程序

    因为SQLite自带C、C++、Java接口,所以我这里举用Delphi开发的例子。

    使用组件:第三方组件ASQLite。

    下载地址:http://www.aducom.com/cen/download.php?list.2

    选择后边是.D的,就是for Delphi的组件。

    下载解压之后,即可开始用Delphi打开dpk组件包编译安装。

    如果您的Delphi版本比较高,比如是Delphi 2006,那么就编译asqlite3.dpk与asqlite3pkg.dpk,之后

    再安装asqlite3pkg.dpk包即可。

    然后在Delphi组件面板里可以看得到ASQLite组件,如图:

     

    现在就可以用它开发你的程序了,基于SQLite小巧的原因,特别推荐你在布署远程应用时用它,比如监控啊

    ,木马啊什么的。

    OK,为了使我们的例子更详细,下面讲讲怎么用ASQLite组件创建、连接数据库,并建立一个数据表。

    新建一个窗体,放一个TASQLite3DB,命名为DB1,将它的Database(数据库)设为test.sqb,放一个

    TASQLite3Query,命名为Query1,将它的Connection指向DB1,然后放入两个按钮,第一个按钮的作

    用是创建或连接数据库,第二个按钮的作用是建立数据表。

    如下图:

     

    “连接”按钮事件的代码如下:

    DB1.Open;

    执行完这条语句之后,如果应用程序当前目录下不存在test.sqb文件,则自动创建并连接它。

    “建表”按钮事件的代码如下:

      Query1.StartTransaction;

      try

        Query1.Close;

        Query1.SQL.Text:='create table student(sname vchar(30),age integer)';

        Query1.ExecSQL;

        Query1.Commit;

      except

        Query1.RollBack;

      end;

    执行完这些语句之后,在Test.sqb数据库里将创建数据表student。

    现在,请将您下载的SQLite的动态链接库sqlite3.dll文件复制到应用程序目录下。

    之后,可正常运行。

    布署时,也应将sqlite3.dll文件一起打包。

    例2、使用C++语言开发基于Firebird嵌入版的应用程序。

    由于Firebird衍生于Interbase,所以Delphi对它的支持最好,IBX,DBX,Fibplus等都可以直接使用它

    ,只要注意将接口文件改为fbembed.dll即可。在此不再多言。

    对于C++这种最通用的语言,我们有一个更好的组件可以选择:IBPP。

    IBPP主页:http://www.ibpp.org/

    IBPP是用C++封装的Firebird接口,最新版本2.5.2.2。

    只要在C++里引用all_in_one.cpp文件,就可以使用它的功能。

    可以用IBPP:atabase类连接数据库,用IBPP::Transaction类控制事务,IBPP::Statement类可以获取

    数据集。下面展示一段代码,功能为:先连接d:\demo.fdb文件,然后从student表里选择所有记录,遍历

    所有记录之后,显示最后一条记录的sn与 sname字段。为了使演示更直观,省去了异常处理。

    示例代码:

    #define IBPP_WINDOWS  //运行于Windows平台的预先声明

    #include "ibpp/all_in_one.cpp"

    ……

    IBPP:atabase db1;

    db1=IBPP:atabaseFactory("","d:/demo.fdb","sysdba","masterkey";

    db1->Connect();

    IBPP::Transaction tr1=IBPP::TransactionFactory(db1,IBPP::amWrite,

    IBPP::ilConcurrency,IBPP::lrWait, IBPP::tfNoAutoUndo);

    tr1->Start();

    IBPP::Statement st1=IBPP::StatementFactory(db1,tr1);

    st1->Prepare("select * from student";

    st1->Execute();

    std::string sn,sname;

    st1->Fetch();

    st1->Get(1,sn);

    st1->Get(2,sname);

    tr1->Commit();

    Label1->Caption=sn.c_str();

    Label2->Caption=sname.c_str();

    最后2行语句为显示结果的,不同开发平台应该使用不同的方法演示,请勿直接复制源代码。

    布署时,还应该带上如下文件:fbembed.dll,ib_util.dll,icudt30.dll,icuin30.dll,icuuc30.dll,为

    了更通用,还可以带上VC++ 7.1的运行库msvcp71.dll,msvcr71.dll两个文件。

  • substr substrb

    substr与substrb 字符串截取函数

     substr 按字符来截取,substrb按字节来截取。多字节字符集下需要注意。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
     
    
     SCOTT>select substr('abcdefg',1,3) from dual;
    
    SUBSTR('ABCDEFG',1,3)                                                           
    ----------------------------------------------------------------                
    abc                                                                              
    
    SCOTT>select substrb('abcdefg',1,3) from dual;
    
    SUBSTRB('ABCDEFG',1,3)                                                          
    --------------------------------                                                
    abc                                                                              
    
    SCOTT>select substr('中华人民共和国',1,3) from dual;
    
    SUBSTR('中华人民共和国',1,3)                                                    
    ----------------------------------------------------------------                
    中华人                                                                            
    
    SCOTT>select substrb('中华人民共和国',1,3) from dual;
    
    SUBSTRB('中华人民共和国',1,3)                                                   
    --------------------------------                                                
    中     

     

    类似的还有,

    length与lengthb 长度计算函数 

    Instr与Instrb 字符串查找函数 instr(原字符串,查的字符串,起始位置,第几个匹配) 返回字符串位置,找不到返回0 .

    Posted Apr 02 2009, 10:45 AM by slash with no comments
    Filed under: ,
  • 存在就更新,不存在就插入的sql语句实现1

     存在就更新,不存在就插入的sql语句实现

    语法为:  
    MERGE INTO table  
    USING data_source  
    ON (condition)  
    WHEN MATCHED THEN update_clause  
    WHEN NOT MATCHED THEN insert_clause;



    CREATE TABLE bonuses (employee_id NUMBER, bonus NUMBER DEFAULT 100);

    INSERT INTO bonuses(employee_id)
       (SELECT e.employee_id FROM employees e, orders o
       WHERE e.employee_id = o.sales_rep_id
       GROUP BY e.employee_id);

    SELECT * FROM bonuses;

    EMPLOYEE_ID      BONUS
    ----------- ----------
            153        100
            154        100
            155        100
            156        100
            158        100
            159        100
            160        100
            161        100
            163        100

    MERGE INTO bonuses D
       USING (SELECT employee_id, salary, department_id FROM employees WHERE department_id = 80) S
       ON (D.employee_id = S.employee_id)
       WHEN MATCHED THEN UPDATE SET D.bonus = D.bonus + S.salary*.01
         DELETE WHERE (S.salary > 8000)
       WHEN NOT MATCHED THEN INSERT (D.employee_id, D.bonus)
         VALUES (S.employee_id, S.salary*0.1)
         WHERE (S.salary <= 8000);

    EMPLOYEE_ID      BONUS
    ----------- ----------
            153        180
            154        175
            155        170
            159        180
            160        175
            161        170
            179        620
            173        610
            165        680
            166        640
            164        720
            172        730
            167        620
            171        740

    Posted Mar 11 2009, 03:09 PM by slash with no comments
    Filed under:
  • Oracle SQL Loader的语法1

    SQL*LOADER是ORACLE的数据加载工具,通常用来将操作系统文件迁移到ORACLE数据库中。SQL*LOADER是大型数据
    仓库选择使用的加载方法,因为它提供了最快速的途径(DIRECT,PARALLEL)。现在,我们抛开其理论不谈,用实例来使您快速掌握SQL*LOADER的使用方法。
    首先,我们认识一下SQL*LOADER。
    在NT下,SQL*LOADER的命令为SQLLDR,在UNIX下一般为sqlldr/sqlload。
    如执行:d:\oracle>sqlldr
    SQL*Loader: Release 8.1.6.0.0 - Production on 星期二 1月 8 11:06:42 2002
    (c) Copyright 1999 Oracle Corporation. All rights reserved.
    用法: SQLLOAD 关键字 = 值 [,keyword=value,…]
    有效的关键字:
    userid -- ORACLE username/password
    control -- Control file name
    log -- Log file name
    bad -- Bad file name
    data -- Data file name
    discard -- Discard file name
    discardmax -- Number of discards to allow (全部默认)
    skip -- Number of logical records to skip (默认0)
    load -- Number of logical records to load (全部默认)
    errors -- Number of errors to allow (默认50)
    rows -- Number of rows in conventional path bind array or between direct path data saves
    (默认: 常规路径 64, 所有直接路径)
    bindsize -- Size of conventional path bind array in bytes(默认65536)
    silent -- Suppress messages during run (header,feedback,errors,discards,partitions)
    direct -- use direct path (默认FALSE)
    parfile -- parameter file: name of file that contains parameter specifications
    parallel -- do parallel load (默认FALSE)
    file -- File to allocate extents from
    skip_unusable_indexes -- disallow/allow unusable indexes or index partitions(默认FALSE)
    skip_index_maintenance -- do not maintain indexes, mark affected indexes as unusable(默认FALSE)
    commit_discontinued -- commit loaded rows when load is discontinued(默认FALSE)
    readsize -- Size of Read buffer (默认1048576)
    PLEASE NOTE: 命令行参数可以由位置或关键字指定
    。前者的例子是 ’sqlload scott/tiger foo’;后者的例子是 ’sqlload control=foo userid=scott/tiger’.位置指定参数的时间必须早于但不可迟于由关键字指定的参数。
    例如,’SQLLOAD SCott/tiger control=foo logfile=log’, 但’不允许

    sqlload scott/tiger control=foo log’,即使允许 参数 ‘log’ 的位置正确。
    d:\oracle>
    我们可以从中看到一些基本的帮助信息,这里,我用到的是中文的WIN2000 ADV SERVER。
    我们知道,SQL*LOADER只能导入纯文本,所以我们现在开始以实例来讲解其用法。
    一、已存在数据源result.csv,欲倒入ORACLE中FANCY用户下。
    result.csv内容:
    1,默认 Web 站点,192.168.2.254:80:,RUNNING
    2,other,192.168.2.254:80:test.com,STOPPED
    3,third,192.168.2.254:81:thirdabc.com,RUNNING
    从中,我们看出4列,分别以逗号分隔,为变长字符串。
    二、制定控制文件result.ctl
    result.ctl内容:
    load data
    infile 'result.csv'
    into table resultxt
    (resultid char terminated by ',',
    website char terminated by ',',
    ipport char terminated by ',',
    status char terminated by whitespace)
    说明:
    infile 指数据源文件 这里我们省略了默认的 discardfile result.dsc badfile result.bad
    into table resultxt 默认是INSERT,也可以into table resultxt APPEND为追加方式,或REPLACE
    terminated by ‘,’ 指用逗号分隔
    terminated by whitespace 结尾以空白分隔
    三、此时我们执行加载:
    D:\>sqlldr userid=fancy/testpass control=result.ctl log=resulthis.out
    SQL*Loader: Release 8.1.6.0.0 - Production on 星期二 1月 8 10:25:42 2002
    (c) Copyright 1999 Oracle Corporation. All rights reserved.
    SQL*Loader-941: 在描述表RESULTXT时出现错误
    ORA-04043: 对象 RESULTXT 不存在
    提示出错,因为数据库没有对应的表。
    四、在数据库建立表
    create table resultxt
    (resultid varchar2(500),
    website varchar2(500),
    ipport varchar2(500),
    status varchar2(500))
    /
    五、重新执行加载
    D:\>sqlldr userid=fancy/k1i7l6l8 control=result.ctl log=resulthis.out
    SQL*Loader: Release 8.1.6.0.0 - Production on 星期二 1月 8 10:31:57 2002
    (c) Copyright 1999 Oracle Corporation. All rights reserved.
    达到提交点,逻辑记录计数2
    达到提交点,逻辑记录计数3
    已经成功!我们可以通过日志文件来分析其过程:resulthis.out内容如下:
    SQL*Loader: Release 8.1.6.0.0 - Production on 星期二 1月 8 10:31:57 2002
    (c) Copyright 1999 Oracle Corporation. All rights reserved.
    控制文件: result.ctl
    数据文件: result.csv
    错误文件: result.bad
    废弃文件: 未作指定
    :
    (可废弃所有记录)
    装载数: ALL
    跳过数: 0
    允许的错误: 50
    绑定数组: 64 行,最大 65536 字节
    继续: 未作指定
    所用路径: 常规
    表RESULTXT
    已载入从每个逻辑记录
    插入选项对此表INSERT生效
    列名 位置 长度 中止 包装数据类型
    ---------- ---- -- -- -- -------
    RESULTID FIRST * , CHARACTER
    WEBSITE NEXT * , CHARACTER
    IPPORT NEXT * , CHARACTER
    STATUS NEXT * WHT CHARACTER
    表RESULTXT:
    3 行载入成功
    由于数据错误, 0 行没有载入。
    由于所有 WHEN 子句失败, 0 行没有载入。
    由于所有字段都为空的, 0 行没有载入。
    为结合数组分配的空间: 65016字节(63行)
    除绑定数组外的内存空间分配: 0字节
    跳过的逻辑记录总数: 0
    读取的逻辑记录总数: 3
    拒绝的逻辑记录总数: 0
    废弃的逻辑记录总数: 0
    从星期二 1月 08 10:31:57 2002开始运行
    在星期二 1月 08 10:32:00 2002处运行结束
    经过时间为: 00: 00: 02.70
    CPU 时间为: 00: 00: 00.10(可
    六、并发操作
    sqlldr userid=/ control=result1.ctl direct=true parallel=true
    sqlldr userid=/ control=result2.ctl direct=true parallel=true
    sqlldr userid=/ control=result2.ctl direct=true parallel=true
    当加载大量数据时(大约超过10GB),最好抑制日志的产生:
    SQL>ALTER TABLE RESULTXT nologging;
    这样不产生REDO LOG,可以提高效率。然后在CONTROL文件中load data上面加一行:unrecoverable 此选项必须要与DIRECT共同应用。
    在并发操作时,ORACLE声称可以达到每小时处理100GB数据的能力!其实,估计能到1-10G就算不错了,开始可用结构 相同的文件,但只有少量数据,成功后开始加载大量数据,这样可以避免时间的浪费。
    有关SQLLDR的问题
    控制文件:input.ctl,内容如下:
    load data           --1、控制文件标识
    infile 'test.txt'       --2、要输入的数据文件名为test.txt
    append into table test    --3、向表test中追加记录
    fields terminated by X'09'  --4、字段终止于X'09',是一个制表符(TAB)
    (id,username,password,sj)   -----定义列对应顺序

    其中append为数据装载方式,还有其他选项:
    a、insert,为缺省方式,在数据装载开始时要求表为空
    b、append,在表中追加新记录
    c、replace,删除旧记录,替换成新装载的记录
    d、truncate,
    删除旧记录,替换成新装载的记录

  • Oracle SQL*Loader 使用指南

    SQL*Loader是Oracle数据库导入外部数据的一个工具.它和DB2的Load工具相似,但有更多的选择,它支持变化的加载模式,

    可选的加载及多表加载.
    如何使用 SQL*Loader 工具
    我们可以用Oracle的sqlldr工具来导入数据。例如:
    sqlldr scott/tiger control=loader.ctl
    控制文件(loader.ctl) 将加载一个外部数据文件(含分隔符). loader.ctl如下:
    load data
    infile 'c:\data\mydata.csv'
    into table emp
    fields terminated by "," optionally enclosed by '"'
    ( empno, empname, sal, deptno )

    mydata.csv 如下:
    10001,"Scott Tiger", 1000, 40
    10002,"Frank Naude", 500, 20
    下面是一个指定记录长度的示例控制文件。"*" 代表数据文件与此文件同名,即在后面使用BEGINDATA段来标识数据。
    load data
    infile *
    replace
    into table departments
    ( dept position (02:05) char(4),
    deptname position (08:27) char(20)
    )
    begindata
    COSC COMPUTER SCIENCE
    ENGL ENGLISH LITERATURE
    MATH MATHEMATICS
    POLY POLITICAL SCIENCE

    Unloader这样的工具
    Oracle 没有提供将数据导出到一个文件的工具。但是,我们可以用SQL*Plus的select 及 format 数据来输出到一个文件


    set echo off newpage 0 space 0 pagesize 0 feed off head off trimspool on
    spool oradata.txt
    select col1 || ',' || col2 || ',' || col3
    from tab1
    where col2 = 'XYZ';
    spool off

    另外,也可以使用使用 UTL_FILE PL/SQL 包处理:
    rem Remember to update initSID.ora, utl_file_dir='c:\oradata' parameter
    declare
    fp utl_file.file_type;
    begin
    fp := utl_file.fopen('c:\oradata','tab1.txt','w');
    utl_file.putf(fp, '%s, %s\n', 'TextField', 55);
    utl_file.fclose(fp);
    end;
    /

    当然你也可以使用第三方工具,如SQLWays ,TOAD for Quest等。

    加载可变长度或指定长度的记录
    如:
    LOAD DATA
    INFILE *
    INTO TABLE load_delimited_data
    FIELDS TERMINATED BY "," OPTIONALLY ENCLOSED BY '"'
    TRAILING NULLCOLS
    ( data1,
    data2
    )
    BEGINDATA
    11111,AAAAAAAAAA
    22222,"A,B,C,D,"

    下面是导入固定位置(固定长度)数据示例:
    LOAD DATA
    INFILE *
    INTO TABLE load_positional_data
    ( data1 POSITION(1:5),
    data2 POSITION(6:15)
    )
    BEGINDATA
    11111AAAAAAAAAA
    22222BBBBBBBBBB

    跳过数据行:
    可以用 "SKIP n" 关键字来指定导入时可以跳过多少行数据。如:
    LOAD DATA
    INFILE *
    INTO TABLE load_positional_data
    SKIP 5
    ( data1 POSITION(1:5),
    data2 POSITION(6:15)
    )
    BEGINDATA
    11111AAAAAAAAAA
    22222BBBBBBBBBB

    导入数据时修改数据:
    在导入数据到数据库时,可以修改数据。注意,这仅适合于常规导入,并不适合 direct导入方式.如:
    LOAD DATA
    INFILE *
    INTO TABLE modified_data
    ( rec_no "my_db_sequence.nextval",
    region CONSTANT '31',
    time_loaded "to_char(SYSDATE, 'HH24:MI')",
    data1 POSITION(1:5) ":data1/100",
    data2 POSITION(6:15) "upper(:data2)",
    data3 POSITION(16:22)"to_date(:data3, 'YYMMDD')"
    )
    BEGINDATA
    11111AAAAAAAAAA991201
    22222BBBBBBBBBB990112

    LOAD DATA
    INFILE 'mail_orders.txt'
    BADFILE 'bad_orders.txt'
    APPEND
    INTO TABLE mailing_list
    FIELDS TERMINATED BY ","
    ( addr,
    city,
    state,
    zipcode,
    mailing_addr "decode(:mailing_addr, null, :addr, :mailing_addr)",
    mailing_city "decode(:mailing_city, null, :city, :mailing_city)",
    mailing_state
    )

    将数据导入多个表:
    如:
    LOAD DATA
    INFILE *
    REPLACE
    INTO TABLE emp
    WHEN empno != ' '
    ( empno POSITION(1:4) INTEGER EXTERNAL,
    ename POSITION(6:15) CHAR,
    deptno POSITION(17:18) CHAR,
    mgr POSITION(20:23) INTEGER EXTERNAL
    )
    INTO TABLE proj
    WHEN projno != ' '
    ( projno POSITION(25:27) INTEGER EXTERNAL,
    empno POSITION(1:4) INTEGER EXTERNAL
    )

    导入选定的记录:
    如下例: (01) 代表第一个字符, (30:37) 代表30到37之间的字符:
    LOAD DATA
    INFILE 'mydata.dat' BADFILE 'mydata.bad' DISCARDFILE 'mydata.dis'
    APPEND
    INTO TABLE my_selective_table
    WHEN (01) <> 'H' and (01) <> 'T' and (30:37) = '19991217'
    (
    region CONSTANT '31',
    service_key POSITION(01:11) INTEGER EXTERNAL,
    call_b_no POSITION(12:29) CHAR
    )

    导入时跳过某些字段:
    可用 POSTION(x:y) 来分隔数据. 在Oracle8i中可以通过指定 FILLER 字段实现。FILLER 字段用来跳过、忽略导入数据文

    件中的字段.如:
    LOAD DATA
    TRUNCATE INTO TABLE T1
    FIELDS TERMINATED BY ','
    ( field1,
    field2 FILLER,
    field3
    )

    导入多行记录:
    可以使用下面两个选项之一来实现将多行数据导入为一个记录:

    CONCATENATE: - use when SQL*Loader should combine the same number of physical records together to form one

    logical record.

    CONTINUEIF - use if a condition indicates that multiple records should be treated as one. Eg. by having a

    '#' character in column 1.

    SQL*Loader 数据的提交:
    一般情况下是在导入数据文件数据后提交的。
    也可以通过指定 ROWS= 参数来指定每次提交记录数。

    提高 SQL*Loader 的性能:
    1) 一个简单而容易忽略的问题是,没有对导入的表使用任何索引和/或约束(主键)。如果这样做,甚至在使用ROWS=参数时

    ,会很明显降低数据库导入性能。
    2) 可以添加 DIRECT=TRUE来提高导入数据的性能。当然,在很多情况下,不能使用此参数。
    3) 通过指定 UNRECOVERABLE选项,可以关闭数据库的日志。这个选项只能和 direct 一起使用。
    4) 可以同时运行多个导入任务.

    常规导入与direct导入方式的区别:
    常规导入可以通过使用 INSERT语句来导入数据。Direct导入可以跳过数据库的相关逻辑(DIRECT=TRUE),而直接将数据导

    入到数据文件中。


    ==========================================================================================================

    sql load的一点小总结
     
    sqlldr userid=lgone/tiger control=a.ctl
    LOAD DATA
    INFILE 't.dat' // 要导入的文件
    // INFILE 'tt.date' // 导入多个文件
    // INFILE * // 要导入的内容就在control文件里 下面的BEGINDATA后面就是导入的内容
     
    INTO TABLE table_name // 指定装入的表
    BADFILE 'c:\bad.txt' // 指定坏文件地址
     
    ************* 以下是4种装入表的方式
    APPEND // 原先的表有数据 就加在后面
    // INSERT // 装载空表 如果原先的表有数据 sqlloader会停止 默认值
    // REPLACE // 原先的表有数据 原先的数据会全部删除
    // TRUNCATE // 指定的内容和replace的相同 会用truncate语句删除现存数据
     
    ************* 指定的TERMINATED可以在表的开头 也可在表的内部字段部分
    FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
    // 装载这种数据: 10,lg,"""lg""","lg,lg"
    // 在表中结果: 10 lg "lg" lg,lg
    // TERMINATED BY X '09' // 以十六进制格式 '09' 表示的
    // TERMINATED BY WRITESPACE // 装载这种数据: 10 lg lg
     
    TRAILING NULLCOLS ************* 表的字段没有对应的值时允许为空
     
    ************* 下面是表的字段
    (
    col_1 , col_2 ,col_filler FILLER // FILLER 关键字 此列的数值不会被装载
    // 如: lg,lg,not 结果 lg lg
    )
    // 当没声明FIELDS TERMINATED BY ',' 时
    // (
    // col_1 [interger external] TERMINATED BY ',' ,
    // col_2 [date "dd-mon-yyy"] TERMINATED BY ',' ,
    // col_3 [char] TERMINATED BY ',' OPTIONALLY ENCLOSED BY 'lg'
    // )
    // 当没声明FIELDS TERMINATED BY ','用位置告诉字段装载数据
    // (
    // col_1 position(1:2),
    // col_2 position(3:10),
    // col_3 position(*:16), // 这个字段的开始位置在前一字段的结束位置
    // col_4 position(1:16),
    // col_5 position(3:10) char(8) // 指定字段的类型
    // )
     
    BEGINDATA // 对应开始的 INFILE * 要导入的内容就在control文件里
    10,Sql,what
    20,lg,show
     
    =====================================================================================
    //////////// 注意begindata后的数值前面不能有空格
     
    1 ***** 普通装载
    LOAD DATA
    INFILE *
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
    (DEPTNO,
    DNAME,
    LOC
    )
    BEGINDATA
    10,Sales,"""USA"""
    20,Accounting,"Virginia,USA"
    30,Consulting,Virginia
    40,Finance,Virginia
    50,"Finance","",Virginia // loc 列将为空
    60,"Finance",,Virginia // loc 列将为空
     
    2 ***** FIELDS TERMINATED BY WHITESPACE 和 FIELDS TERMINATED BY x'09' 的情况
    LOAD DATA
    INFILE *
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY WHITESPACE
    -- FIELDS TERMINATED BY x'09'
    (DEPTNO,
    DNAME,
    LOC
    )
    BEGINDATA
    10 Sales Virginia
     
    3 ***** 指定不装载那一列
    LOAD DATA
    INFILE *
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
    ( DEPTNO,
    FILLER_1 FILLER, // 下面的 "Something Not To Be Loaded" 将不会被装载
    DNAME,
    LOC
    )
    BEGINDATA
    20,Something Not To Be Loaded,Accounting,"Virginia,USA"
     
    4 ***** position的列子
    LOAD DATA
    INFILE *
    INTO TABLE DEPT
    REPLACE
    ( DEPTNO position(1:2),
    DNAME position(*:16), // 这个字段的开始位置在前一字段的结束位置
    LOC position(*:29),
    ENTIRE_LINE position(1:29)
    )
    BEGINDATA
    10Accounting Virginia,USA
     
    5 ***** 使用函数 日期的一种表达 TRAILING NULLCOLS的使用
    LOAD DATA
    INFILE *
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ','
    TRAILING NULLCOLS // 其实下面的ENTIRE_LINE在BEGINDATA后面的数据中是没有直接对应
    // 的列的值的 如果第一行改为 10,Sales,Virginia,1/5/2000,, 就不用TRAILING NULLCOLS了
    (DEPTNO,
    DNAME "upper(:dname)", // 使用函数
    LOC "upper(:loc)",
    LAST_UPDATED date 'dd/mm/yyyy', // 日期的一种表达方式 还有'dd-mon-yyyy' 等
    ENTIRE_LINE ":deptno||:dname||:loc||:last_updated"
    )
    BEGINDATA
    10,Sales,Virginia,1/5/2000
    20,Accounting,Virginia,21/6/1999
    30,Consulting,Virginia,5/1/2000
    40,Finance,Virginia,15/3/2001
     
    6 ***** 使用自定义的函数 // 解决的时间问题
    create or replace
    function my_to_date( p_string in varchar2 ) return date
    as
    type fmtArray is table of varchar2(25);
     
    l_fmts fmtArray := fmtArray( 'dd-mon-yyyy', 'dd-month-yyyy',
    'dd/mm/yyyy',
    'dd/mm/yyyy hh24:mi:ss' );
    l_return date;
    begin
    for i in 1 .. l_fmts.count
    loop
    begin
    l_return := to_date( p_string, l_fmts(i) );
    exception
    when others then null;
    end;
    EXIT when l_return is not null;
    end loop;
     
    if ( l_return is null )
    then
    l_return :=
    new_time( to_date('01011970','ddmmyyyy') + 1/24/60/60 *
    p_string, 'GMT', 'EST' );
    end if;
     
    return l_return;
    end;
    /
     
    LOAD DATA
    INFILE *
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ','
    TRAILING NULLCOLS
    (DEPTNO,
    DNAME "upper(:dname)",
    LOC "upper(:loc)",
    LAST_UPDATED "my_to_date( :last_updated )" // 使用自定义的函数
    )
    BEGINDATA
    10,Sales,Virginia,01-april-2001
    20,Accounting,Virginia,13/04/2001
    30,Consulting,Virginia,14/04/2001 12:02:02
    40,Finance,Virginia,987268297
    50,Finance,Virginia,02-apr-2001
    60,Finance,Virginia,Not a date
     
    7 ***** 合并多行记录为一行记录
    LOAD DATA
    INFILE *
    concatenate 3 // 通过关键字concatenate 把几行的记录看成一行记录
    INTO TABLE DEPT
    replace
    FIELDS TERMINATED BY ','
    (DEPTNO,
    DNAME "upper(:dname)",
    LOC "upper(:loc)",
    LAST_UPDATED date 'dd/mm/yyyy'
    )
    BEGINDATA
    10,Sales, // 其实这3行看成一行 10,Sales,Virginia,1/5/2000
    Virginia,
    1/5/2000
    // 这列子用 continueif list="," 也可以
    告诉sqlldr在每行的末尾找逗号 找到逗号就把下一行附加到上一行
     
    LOAD DATA
    INFILE *
    continueif this(1:1) = '-' // 找每行的开始是否有连接字符 - 有就把下一行连接为一行
    // 如 -10,Sales,Virginia,
    // 1/5/2000 就是一行 10,Sales,Virginia,1/5/2000
    // 其中1:1 表示从第一行开始 并在第一行结束 还有continueif next 但continueif list最理想
    INTO TABLE DEPT
    replace
    FIELDS TERMINATED BY ','
    (DEPTNO,
    DNAME "upper(:dname)",
    LOC "upper(:loc)",
    LAST_UPDATED date 'dd/mm/yyyy'
    )
    BEGINDATA // 但是好象不能象右面的那样使用
    -10,Sales,Virginia, -10,Sales,Virginia,
    1/5/2000 1/5/2000
    -40, 40,Finance,Virginia,13/04/2001
    Finance,Virginia,13/04/2001
     
    8 ***** 载入每行的行号
     
    load data
    infile *
    into table t
    replace
    ( seqno RECNUM //载入每行的行号
    text Position(1:1024))
    BEGINDATA
    fsdfasj //自动分配一行号给载入 表t 的seqno字段 此行为 1
    fasdjfasdfl // 此行为 2 ...
     
    9 ***** 载入有换行符的数据
    注意: unix 和 windows 不同 \\n & /n
    &lt; 1 > 使用一个非换行符的字符
    LOAD DATA
    INFILE *
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ','
    TRAILING NULLCOLS
    (DEPTNO,
    DNAME "upper(:dname)",
    LOC "upper(:loc)",
    LAST_UPDATED "my_to_date( :last_updated )",
    COMMENTS "replace(:comments,'\n',chr(10))" // replace 的使用帮助转换换行符
    )
    BEGINDATA
    10,Sales,Virginia,01-april-2001,This is the Sales\nOffice in Virginia
    20,Accounting,Virginia,13/04/2001,This is the Accounting\nOffice in Virginia
    30,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting\nOffice in Virginia
    40,Finance,Virginia,987268297,This is the Finance\nOffice in Virginia
     
    &lt; 2 > 使用fix属性
    LOAD DATA
    INFILE demo17.dat "fix 101"
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ','
    TRAILING NULLCOLS
    (DEPTNO,
    DNAME "upper(:dname)",
    LOC "upper(:loc)",
    LAST_UPDATED "my_to_date( :last_updated )",
    COMMENTS
    )
    demo17.dat
    10,Sales,Virginia,01-april-2001,This is the Sales
    Office in Virginia
    20,Accounting,Virginia,13/04/2001,This is the Accounting
    Office in Virginia
    30,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting
    Office in Virginia
    40,Finance,Virginia,987268297,This is the Finance
    Office in Virginia
     
    // 这样装载会把换行符装入数据库 下面的方法就不会 但要求数据的格式不同
     
    LOAD DATA
    INFILE demo18.dat "fix 101"
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
    TRAILING NULLCOLS
    (DEPTNO,
    DNAME "upper(:dname)",
    LOC "upper(:loc)",
    LAST_UPDATED "my_to_date( :last_updated )",
    COMMENTS
    )
    demo18.dat
    10,Sales,Virginia,01-april-2001,"This is the Sales
    Office in Virginia"
    20,Accounting,Virginia,13/04/2001,"This is the Accounting
    Office in Virginia"
    30,Consulting,Virginia,14/04/2001 12:02:02,"This is the Consulting
    Office in Virginia"
    40,Finance,Virginia,987268297,"This is the Finance
    Office in Virginia"
     
    &lt; 3 > 使用var属性
    LOAD DATA
    INFILE demo19.dat "var 3"
    // 3 告诉每个记录的前3个字节表示记录的长度 如第一个记录的 071 表示此记录有 71 个字节
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ','
    TRAILING NULLCOLS
    (DEPTNO,
    DNAME "upper(:dname)",
    LOC "upper(:loc)",
    LAST_UPDATED "my_to_date( :last_updated )",
    COMMENTS
    )
    demo19.dat
    07110,Sales,Virginia,01-april-2001,This is the Sales
    Office in Virginia
    07820,Accounting,Virginia,13/04/2001,This is the Accounting
    Office in Virginia
    08730,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting
    Office in Virginia
    07140,Finance,Virginia,987268297,This is the Finance
    Office in Virginia
     
    &lt; 4 > 使用str属性
    // 最灵活的一中 可定义一个新的行结尾符 win 回车换行 : chr(13)||chr(10)
     
    此列中记录是以 a|\r\n 结束的
    select utl_raw.cast_to_raw('|'||chr(13)||chr(10)) from dual;
    结果 7C0D0A
     
    LOAD DATA
    INFILE demo20.dat "str X'7C0D0A'"
    INTO TABLE DEPT
    REPLACE
    FIELDS TERMINATED BY ','
    TRAILING NULLCOLS
    (DEPTNO,
    DNAME "upper(:dname)",
    LOC "upper(:loc)",
    LAST_UPDATED "my_to_date( :last_updated )",
    COMMENTS
    )
    demo20.dat
    10,Sales,Virginia,01-april-2001,This is the Sales
    Office in Virginia|
    20,Accounting,Virginia,13/04/2001,This is the Accounting
    Office in Virginia|
    30,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting
    Office in Virginia|
    40,Finance,Virginia,987268297,This is the Finance
    Office in Virginia|
     
    ==============================================================================
    象这样的数据 用 nullif 子句
     
    10-jan-200002350Flipper seemed unusually hungry today.
    10510-jan-200009945Sdivad over three meals.
     
    id position(1:3) nullif id=blanks // 这里可以是blanks 或者别的表达式
    // 下面是另一个列子 第一行的 1 在数据库中将成为 null
    LOAD DATA
    INFILE *
    INTO TABLE T
    REPLACE
    (n position(1:2) integer external nullif n='1',
    v position(3:8)
    )
    BEGINDATA
    1 10
    20lg
    ------------------------------------------------------------
     
    如果是英文的日志 格式,可能需要修改环境变量 nls_lang or nls_date_format

    ==========================================================================================================
    Posted Mar 04 2009, 11:30 AM by slash with no comments
    Filed under:
  • 用URL传参带特殊字符

     用URL传参带特殊字符,特殊字符丢失
        用URL传参数的时候,用&符号连接,如果某一个参数中含"#$ ^ & * + ="这些符号的时候,在另一个页面getParameter就会取不到传过来的参数。


      比如在a.jsp中,我要跳转到b.jsp,在a.jsp中这样写:
           b.jsp?Parameter1=wks&Parameter2=happycosn。
      如果Parameter1中含有"#$ ^ & * + ="这些特殊字符,在b.jsp中就得不到。并且,我在a.jsp中已经通过encode编码了,在b.jsp中也用decode 进行了解码。这时候,只要所传入的参数没有特殊字符都可以在b.jsp页面取到,但是有特殊字符就取不到了。 像这样:
         b.jsp?Parameter1=wks#bamboo&Parameter2=happycosn+#zl
      这时候,所取到的参数就不会有bamboo和zl。

    解决问题如下: 
    (下面也是找的一些资料,测试过没问题。)

    有些符号在URL中是不能直接传递的,如果要在URL中传递这些特殊符号,那么就要使用他们的编码了。编码的格式为:%加字符的ASCII码,即一个百分号%,后面跟对应字符的ASCII(16进制)码值。例如 空格的编码值是"%20"。
    如果不使用转义字符,这些编码就会当URL中定义的特殊字符处理。

    下表中列出了一些URL特殊符号及编码 十六进制值
    1.+ URL 中+号表示空格 %2B
    2.空格 URL中的空格可以用+号或者编码 %20
    3./ 分隔目录和子目录 %2F
    4.? 分隔实际的 URL 和参数 %3F
    5.% 指定特殊字符 %25
    6.# 表示书签 %23
    7.& URL 中指定的参数间的分隔符 %26
    8.= URL 中指定参数的值 %3D

  • Oracle 数据库的绑定变量特性及应用1

     关键词:

    绑定变量(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=:empno;

    象我们往常一样,你查询员工’123’一次以后,有可能再也不用;接着你有可能查询员工’456’,然后查询’789’等等;如

    果查询使用象第一个查询语句,你每次查询都是一个新的查询(我们叫它硬编码的查询方法);因此,Oracle每次必须分析,解

    析,安全检查, 优化等等;
    第二个查询语句提供了绑定变量:empno,它的值在查询执行时提供,查询经过一次编译后,查询方案存储在共享池中,可以用

    来检索和重用;在性能和伸缩性方面,这两者的差异是巨大的,甚至是惊人的;通俗点讲,就不是一个级别;
    第一个查询使用的频率越高,所消耗的系统硬件资源越大,从而降低了用户的使用数量;它也会把优化好的其它查询语句从

    共享池中踢出;就象一个老鼠坏了一锅汤似的,系统的整体性能降低; 而执行绑定变量,提交相同对象的完全相同的查询的

    用户(这句话,大家听起来比较难理解,随后我会给出详细的解释),一次性使用就可重复使用,其效率不言耳语; 打个形象的

    比喻来说,第一个查询就象一次性使用的筷子,而第二个查询象是铁筷子,只要洗干净,张三李四都能用,合理有效地使用了

    资源;

    下面举例子去详细论证上述的问题,不使用绑定变量为生病状况:

    这是一个未使用的绑定变量(吃药前):


    set echo on;(把执行结果显示出来)
    alter system flush shared_pool;
    这条语句是清空共项池,每次都必须使用,确保共享池是空的,以提高执行效率;
    set timing on(打开记时器.)

    declare
    type rc is ref cursor;
    l_rc rc;
    l_dummy all_objects.object_name%type;
    l_start number default dbms_utility.get_time;
    begin
    for i in 1 .. 1000
    loop
    open l_rc for
    'select object_name
    from all_objects
    where object_id = ' || i;
    fetch l_rc into l_dummy;
    close l_rc;
    end loop;
    dbms_output.put_line
    ( round( (dbms_utility.get_time-l_start)/100, 2 ) ||
    ' seconds...' );
    end;
    /
    PL/SQL 过程已成功完成。

    执行时间: 已用时间: 00: 00: 07.03


    这是一个使用的绑定变量(吃药后):
    set echo on

    alter system flush shared_pool;
    declare
    type rc is ref cursor;
    l_rc rc;
    l_dummy all_objects.object_name%type;
    l_start number default dbms_utility.get_time;
    begin
    for i in 1 .. 1000
    loop
    open l_rc for
    'select object_name
    from all_objects
    where object_id = :x'
    using i;
    fetch l_rc into l_dummy;
    close l_rc;
    end loop;
    dbms_output.put_line
    ( round( (dbms_utility.get_time-l_start)/100, 2 ) ||
    ' seconds...' );
    end;
    PL/SQL 过程已成功完成。

    执行时间: 已用时间: 00: 00: 00.75

    大家自己比较结果,相差就是一个数量级;使用绑定变量不仅仅是运行快,而且允许多个用户同时使用;

    上述的环境是在数据库Oracle 8.1.7, 操作系统: Windows Server 2003,内存 1G , CPU: P4 3.4GHZ ; 电脑配置不同,执

    行的结果是有差异的;

    2. 怎样使用绑定变量?
    下面举例说明:
    2.1.让Oracle自己绑定变量(也叫静态绑定变量)

    set serverout on;
    set timing on;
    declare
    l_sql varchar2(2000);
    l_count number;
    l_param1 varchar2(100);
    l_param2 varchar2(100);
    begin
    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);
    end;
    /
    在上面的情况,Oracle会自己绑定变量,即,如果参数保存在一个数组中,select语句放在一个循环中,
    select 语句只会编译一次。

    2.2 .动态绑定变量
    set serverout on;
    set timing on;
    declare
    l_sql varchar2(2000);
    l_count number;
    l_param1 varchar2(100);
    l_param2 varchar2(100);
    begin
    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);
    end;
    /

    2.3. dbms_output的绑定变量使用
    Set echo on;
    Set serveroutput on;
    Set timming on;
    declare
    cursor_id integer;
    i number;
    xSql Varchar2(200);
    xOut varchar2(200);
    l_start number default dbms_utility.get_time;
    xRow integer;
    Begin
    cursor_id:=DBMS_Sql.open_cursor;
    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;
    End loop;
    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...';
    --return xout;
    end;

    这里强烈推荐使用静态绑定变量,有兴趣的话可以自己比较;

    3. 我怎样知道正在使用绑定变量的方法;
    下面举例说明;
    创建一个Table;
    Create table t (xx int);
    执行下面的语句;
    Begin
    For I in 1..100 loop
    Execute immediate’insert into t values(‘|| t ||’)’;
    End loop;
    end;

    现在准备好了脚本,开始创建一个字符串中删除常数的一个函数,它采用的是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_query long;
    l_char varchar2(1);
    l_in_quates boolean default false;
    begin
    for i in 1..length(p_query)
    loop
    l_char:=substr(p_query,i,1);
    if l_char='''' and l_in_quates then
    l_in_quates:=False;
    elsif l_char='''' and not l_in_quates then
    then
    l_in_quates:=true;
    l_query=:l_query||'#';
    end if

    if not l_in_quates then
    l_query=:l_query||l_char;
    end if;
    end loop;

    l_query:=tranlate(l_query,'0123456789','@@@@@@@@@');
    for i in 1..8 loop
    l_query:=replace(l_query,lpad('@',10-i,'@'),'@');
    l_query:=replace(l_query,lpad('',10-i,''),'');

    end loop;
    return upper(l_query);
    end;
    /

    接着我们建立一个临时表去保存V$SQLAREA里的语句,所有 Sql的执行结果都写在这里;
    建立临时表;
    create global temporary table sql_area_tmp on commit preserve rows as
    select sql_text,sql_text sql_text_wo_constants from
    v$sqlarea where 1=0;

    保存数据到临时表上;
    insert into sql_area_tmp(sql_text) select sql_text from v$sqlarea;

    对临时表中的数据进行更新;删除掉常数;
    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=true;
    Alter session set timed_statictics=True;
    Alter session set events ‘10046 trace name context forever,level <N>’;
    这里的’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秒不到);
    Begin
    Dim cn01 As New OracleConnection
    Dim CMD01 As New OracleCommand

    Dim Cmd As New OleDbCommand
    Dim i As Integer
    Try
    <add key="DBCONN_SFCFA" value="User ID=sfcfa;password=SFCFA;Data Source=CIM;" />
    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)
    CMD01.CommandType = CommandType.Text
    CMD01.Parameters.Add("username", "test" + CStr(i))
    CMD01.Parameters.Add("userid", i)
    CMD01.ExecuteNonQuery()
    CMD01.Parameters.Clear()
    Next i

    TextBox2.Text = Now

    Catch ex As OleDbException
    MsgBox(ex.Message)
    Catch ex As Exception
    MsgBox(ex.HelpLink + ex.Message)

    End Try
    End.

    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
    Try
    <add key="DBCONN_SFCFA" value="Provider=MSDAORA.1;User ID=sfcfa;password=SFCFA;Data Source=CIM;"/>
    xConnStr = System.Configuration.ConfigurationSettings.AppSettings("DBCONN_SFCFA")
    'Cn.ConnectionString()
    Cn.ConnectionString = xConnStr
    Cn.Open()

    TextBox1.Text = Now
    Application.DoEvents()

    xSql = "insert into t values(?,?,sysdate) "
    For i = 1 To 1000
    Cmd = New OleDbCommand(xSql, Cn)
    Cmd.CommandType = CommandType.Text
    Cmd.Parameters.Add("username", "test" + CStr(i))
    Cmd.Parameters.Add("userid", i)
    Cmd.ExecuteNonQuery()
    Cmd.Parameters.Clear()
    Next i

    TextBox2.Text = Now

    Catch ex As OleDbException
    MsgBox(ex.Message)
    Catch ex As Exception
    MsgBox(ex.HelpLink + ex.Message)

    End Try

    VB or ASP(耗时也是5秒左右…):

    Private Sub Command1_Click()
    Dim sConn As String
    Dim BVCS_CN As ADODB.Connection
    'Dim BVCS as ADODB.
    Dim xCMD As ADODB.Command
    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=sfcfa;User ID=sfcfa;Data Source=cim;"

    With BVCS_CN
    .Open sConn
    End With


    If BVCS_CN.State = 0 Then
    MsgBox "DB Connection is error"
    Exit Sub
    End If

    Text1.Text = Now
    DoEvents

    Set xCMD = New ADODB.Command

    Dim xTest As String


    Set xPre = New ADODB.Parameter

    'BVCS_CN

    For i = 1 To 1000

    With xCMD

    .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
    .Execute
    End With


    xCMD.Parameters.Delete 1
    xCMD.Parameters.Delete 0

    Next i


    Set xCMD = Nothing

    Text2.Text = Now

    Exit Sub
    1:
    Set xCMD = Nothing

    MsgBox Error$
    For Each objErr In BVCS_CN.Errors
    MsgBox objErr.Description
    Next
    BVCS_CN.Errors.Clear
    Exit Sub
    Resume Next
    End Sub

    4.2 在Delphi中的使用情况;
    这里特殊说明, Borland Delphi 4.0以上的版本已经开始完全支持绑定变量的概念,因此,它执行数据库的查询效率要好于

    其他开发工具;执行的结果不到2秒;
    procedure TForm1.Button1Click(Sender: TObject);
    Var
    i :Integer;
    begin

    edit1.text:=DatetimeToStr(now);
    For i := 1 to 1000 do
    //Begin
    With Query1 do
    Begin
    close;
    Sql.clear;
    Sql.add('Insert into t Values(:username,:user_id,sysdate) ');
    ParamByName('username').AsString :='test' ;
    ParamByName('user_id').AsInteger :=i ;
    execSql;
    End;
    //end;

    //edit2.text:=DateToStr(now);
    edit2.text:=DatetimetoStr(now);
    end;

    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 Emp;
    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_limit; 不能 每个用户都有一个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语句是相同的(可以共享),而第二组中的两个语句是不同的(即使在运行时,赋于不同的绑定变量相同的值

    )
    a.
    select pin , name from people where pin = :blk1.pin;
    select pin , name from people where pin = :blk1.pin;

    b.
    select pin , name from people where pin = :blk1.ot_ind;
    select pin , name from people where pin = :blk1.ov_ind;


    6. 总结
    不使用绑定变量是做着等死,使用绑定变量不一定不会死;没有任何的良药会包治百病,所以在如何合理有效地使用绑定变

    量仍就需要大家去摸索;

    [全文完]..

    相关文献:
    1. [Oracle 高级专家编程] 作者:Thomas Kytes 袁勤勇,张玉魁编译;清华大学出版社;
    2. ORACLE SQL性能优化系列 from www.dbasupport.com
    3. Oracle 绑定变量的用法 from 王者之剑(www.albertsong.com)

    Posted Feb 03 2009, 11:51 AM by slash with no comments
    Filed under:
  • 索引概念

    Oracle提供了大量索引选项。知道在给定条件下使用哪个选项对于一个应用程序的性能来说非常重要。一个错误的选择可能会引发死锁,并导致数据库性能急剧下降或进程终止。而如果做出正确的选择,则可以合理使用资源,使那些已经运行了几个小时甚至几天的进程在几分钟得以完成,这样会使您立刻成为一位英雄。这篇文章就将简单的讨论每个索引选项。主要有以下内容:

    1、基本的索引概念:

    查询DBA_INDEXES视图可得到表中所有索引的列表,注意只能通过USER_INDEXES的方法来检索模式(schema)的索引。访问USER_IND_COLUMNS视图可得到一个给定表中被索引的特定列。

    2、组合索引:

    当某个索引包含有多个已索引的列时,称这个索引为组合(concatented)索引。在 Oracle9i引入跳跃式扫描的索引访问方法之前,查询只能在有限条件下使用该索引。比如:表emp有一个组合索引键,该索引包含了empno、 ename和deptno。在Oracle9i之前除非在where之句中对第一列(empno)指定一个值,否则就不能使用这个索引键进行一次范围扫描。

    特别注意:在Oracle9i之前,只有在使用到索引的前导索引时才可以使用组合索引!

    3、Oracle ROWID:

    通过每个行的ROWID,索引Oracle提供了访问单行数据的能力。ROWID其实就是直接指向单独行的线路图。如果想检查重复值或是其他对ROWID本身的引用,可以在任何表中使用和指定rowid列。

    4、限制索引:

    限制索引是一些没有经验的开发人员经常犯的错误之一。在SQL中有很多陷阱会使一些索引无法使用。下面讨论一些常见的问题:

    4.1 使用不等于操作符(<>、!=):

    下面的查询即使在cust_rating列有一个索引,查询语句仍然执行一次全表扫描。

    select cust_Id,cust_name
    from  customers
    where cust_rating <> 'aa';

    把上面的语句改成如下的查询语句,这样,在采用基于规则的优化器而不是基于代价的优化器(更智能)时,将会使用索引。

    select cust_Id,cust_name
    from  customers
    where cust_rating < 'aa' or cust_rating > 'aa';

    特别注意:通过把不等于操作符改成OR条件,就可以使用索引,以避免全表扫描。

    4.2 使用IS NULL 或IS NOT NULL:

    使用IS NULL 或IS NOT NULL同样会限制索引的使用。因为NULL值并没有被定义。在SQL语句中使用NULL会有很多的麻烦。因此建议开发人员在建表时,把需要索引的列设成NOT NULL。如果被索引的列在某些行中存在NULL值,就不会使用这个索引(除非索引是一个位图索引,关于位图索引在稍后在详细讨论)。

    4.3 使用函数:

    如果不使用基于函数的索引,那么在SQL语句的WHERE子句中对存在索引的列使用函数时,会使优化器忽略掉这些索引。

    下面的查询不会使用索引(只要它不是基于函数的索引):

    select empno,ename,deptno
    from  emp
    where trunc(hiredate)='01-MAY-81';

    把上面的语句改成下面的语句,这样就可以通过索引进行查找。

    select empno,ename,deptno
    from  emp
    where hiredate<(to_date('01-MAY-81')+0.9999);

    4.4 比较不匹配的数据类型:

    比较不匹配的数据类型也是比较难于发现的性能问题之一。

    注意下面查询的例子,account_number是一个VARCHAR2类型,在account_number字段上有索引。下面的语句将执行全表扫描。

    select bank_name,address,city,state,zip
    from  banks
    where account_number = 990354;

    Oracle可以自动把where子句变成to_number(account_number)=990354,这样就限制了索引的使用,改成下面的查询就可以使用索引:

    select bank_name,address,city,state,zip
    from  banks
    where account_number ='990354';

    特别注意:不匹配的数据类型之间比较会让Oracle自动限制索引的使用,即便对这个查询执行Explain Plan也不能让您明白为什么做了一次“全表扫描”。

    5、选择性:

    使用USER_INDEXES视图,该视图中显示了一个distinct_keys列。比较一下唯一键的数量和表中的行数,就可以判断索引的选择性。选择性越高,索引返回的数据就越少。

    6、群集因子(Clustering Factor):

    Clustering Factor位于USER_INDEXES视图中。该列反映了数据相对于已索引的列是否显得有序。如果Clustering Factor列的值接近于索引中的树叶块(leaf block)的数目,表中的数据就越有序。如果它的值接近于表中的行数,则表中的数据就不是很有序。

    7、二元高度(Binary height):

    索引的二元高度对把ROWID返回给用户进程时所要求的I/O量起到关键作用。在对一个索引进行分析后,可以通过查询DBA_INDEXES的B-level列查看它的二元高度。二元高度主要随着表的大小以及被索引的列中值的范围的狭窄程度而变化。索引上如果有大量被删除的行,它的二元高度也会增加。更新索引列也类似于删除操作,因为它增加了已删除键的数目。重建索引可能会降低二元高度。

    8、快速全局扫描:

    在Oracle7.3后就可以使用快速全局扫描(Fast Full Scan)这个选项。这个选项允许Oracle执行一个全局索引扫描操作。快速全局扫描读取B-树索引上所有树叶块。初始化文件中的 DB_FILE_MULTIBLOCK_READ_COUNT参数可以控制同时被读取的块的数目。

    9、跳跃式扫描:

    从Oracle9i开始,索引跳跃式扫描特性可以允许优化器使用组合索引,即便索引的前导列没有出现在WHERE子句中。索引跳跃式扫描比全索引扫描要快的多。下面的程序清单显示出性能的差别:

    create index skip1 on emp5(job,empno);
    index created.

    select count(*)
    from emp5
    where empno=7900;

    Elapsed:00:00:03.13

    Execution Plan
    0 SELECT STATEMENT Optimizer
    =CHOOSE(Cost=4 Card=1 Bytes=5)
    1 0SORT(AGGREGATE)
    2 1 INDEX(FAST FULL SCAN)
    OF 'SKIP1'(NON-UNIQUE)

    Statistics

    6826 consistent gets
    6819 physical  reads

    select /*+ index(emp5 skip1)*/ count(*)
    from emp5
    where empno=7900;

    Elapsed:00:00:00.56

    Execution Plan
    0 SELECT STATEMENT Optimizer
    =CHOOSE(Cost=6 Card=1 Bytes=5)
    1 0SORT(AGGREGATE)
    2 1 INDEX(SKIP SCAN) OF
    'SKIP1'(NON-UNIQUE)

    Statistics

    21 consistent gets
    17 physical  reads

    10、索引的类型:

    B-树索引 位图索引 HASH索引 索引编排表

    反转键索引 基于函数的索引 分区索引 本地和全局索引

  • 索引概述

    Oracle索引分析与比较

    26.1 概述
    索引在各种关系型数据库系统中都是举足轻重的组成部分,其对于提高检索数据的速度起至关重要的作用。在Oracle中,索引基本分为以下几种:B*Tree索引,反向索引,降序索引,位图索引,函数索引,interMedia全文索引等。本文主要就前6种索引进行分析,由于interMedia全文索引涉及的内容可以单独写一篇文章,所以不在此对其做分析。
    首先给出各种索引的简要解释:
    b*tree index:几乎所有的关系型数据库中都有b*tree类型索引,也是被最多使用的。其树结构与二叉树比较类似,根据rid快速定位所访问的行。
    反向索引:反转了b*tree索引码中的字节,是索引条目分配更均匀,多用于并行服务器环境下,用于减少索引叶的竞争。
    降序索引:8i中新出现的索引类型,针对逆向排序的查询。
    位图索引:使用位图来管理与数据行的对应关系,多用于OLAP系统。
    函数索引:这种索引中保存了数据列基于function返回的值,在select * from table where function(column)=value这种类型的语句中起作用。


    26.2 各种索引的结构分析

    26.2.1 B*Tree索引B*Tree索引是最常见的索引结构,默认建立的索引就是这种类型的索引。B*Tree索引在检索高基数数据列(高基数数据列是指该列有很多不同的值)时提供了最好的性能。当取出的行数占总行数比例较小时B-Tree索引比全表检索提供了更有效的方法。但当检查的范围超过表的10%时就不能提高取回数据的性能。B-Tree索引是基于二叉树的,由分支块(branch block)和叶块(leaf block)组成。在树结构中,位于最底层底块被称为叶块,包含每个被索引列的值和行所对应的rowid。在叶节点的上面是分支块,用来导航结构,包含了索引列(关键字)范围和另一索引块的地址,如图26-1所示。

    图26-1
    假设我们要找索引中值为80的行,从索引树的最上层入口开始,定位到大于等于50,然后往左找,找到第2个分支块,定位为75-100,最后再定位到叶块上,找到80所对应的rowid,然后根据rowid去读取数据块获取数据。如果查询条件是范围选择的,比如where column >20 and column <80,那么会先定位到第一个包含20的叶块,然后横向查找其他的叶块,直到找到包含80的块为止,不用每次都从入口进去再重新定位。


    26.2.2 反向索引
    反向索引是B*Tree索引的一个分支,它的设计是为了运用在某些特定的环境下的。Oracle推出它的主要目的就是为了降低在并行服务器(Oracle Parallel Server)环境下索引叶块的争用。当B*Tree索引中有一列是由递增的序列号产生的话,那么这些索引信息基本上分布在同一个叶块,当用户修改或访问相似的列时,索引块很容易产生争用。反向索引中的索引码将会被分布到各个索引块中,减少了争用。反向索引反转了索引码中每列的字节,通过dump()函数我们可以清楚得看见它做了什么。举个例子:1,2,3三个连续的数,用dump()函数看它们在Oracle内部的表示方法。
    SQL> select 'number',dump(1,16) from dual
    2 union all select 'number',dump(2,16) from dual
    3 union all select 'number',dump(3,16) from dual;

    'NUMBE DUMP(1,16)
    ------ -----------------
    number Typ=2 Len=2: c1,2 (1)
    number Typ=2 Len=2: c1,3 (2)
    number Typ=2 Len=2: c1,4 (3)
    再对比一下反向以后的情况:
    SQL> select 'number',dump(reverse(1),16) from dual
    2 union all select 'number',dump(reverse(2),16) from dual
    3 union all select 'number',dump(reverse(3),16) from dual;

    'NUMBE DUMP(REVERSE(1),1
    ------ -----------------
    number Typ=2 Len=2: 2,c1 (1)
    number Typ=2 Len=2: 3,c1 (2)
    number Typ=2 Len=2: 4,c1 (3)
    我们发现索引码的结构整个颠倒过来了,这样1,2,3个索引码基本上不会出现在同一个叶块里,所以减少了争用。不过反向索引又一个缺点就是不能在所有使用常规索引的地方使用。在范围搜索中其不能被使用,例如,where column>value,因为在索引的叶块中索引码没有分类,所以不能通过搜索相邻叶块完成区域扫描。


    26.2.3 降序索引
    降序索引是8i里面新出现的一种索引,是B*Tree的另一个衍生物,它的变化就是列在索引中的储存方式从升序变成了降序,在某些场合下降序索引将会起作用。举个例子,我们来查询一张表并进行排序:
    SQL> select * from test where a between 1 and 100 order by a desc,b asc;
    已选择100行。
    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=100 Bytes=400)
    1 0 SORT(ORDER BY)(Cost=2 Card=100 Bytes=400)
    2 1 INDEX (RANGE SCAN) OF 'IND_BT' (NON-UNIQUE) (Cost=2 Card=100 Bytes=400)
    这里优化器首先选择了一个索引范围扫描,然后还有一个排序的步骤。如果使用了降序索引,排序的过程会被取消。
    SQL> create index test.ind_desc on test.testrev(a desc,b asc);
    索引已创建。
    SQL> analyze index test.ind_desc compute statistics;
    索引已分析
    再来看下执行路径:
    SQL> select * from test where a between 1 and 100 order by a desc,b asc;
    已选择100行。
    Execution Plan(SQL执行计划,稍后会讲解如何使用)。
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=100 Bytes=400)
    1 0 INDEX (RANGE SCAN) OF 'IND_DESC' (NON-UNIQUE) (Cost=2 Card=100 Bytes=400)
    我们看到排序过程消失了,这是因为创建降序索引时Oracle已经把数据都按降序排好了。
    另外一个需要注意的地方是要设置init.ora里面的compatible参数为8.1.0或以上,否则创建时desc关键字将被忽略。


    26.2.4 位图索引
    位图索引主要用于决策支持系统或静态数据,不支持行级锁定。位图索引最好用于低cardinality列(即列的唯一值除以行数为一个很小的值,接近零),例如又一个“性别”列,列值有“Male”,“Female”,“Null”等3种,但一共有300万条记录,那么3/3000000约等于0,这种情况下最适合用位图索引。
    位图索引可以是简单的(单列)也可以是连接的(多列),但在实践中绝大多数是简单的。在这些列上多位图索引可以与AND或 OR操作符结合使用。位图索引使用位图作为键值,对于表中的每一数据行位图包含了TRUE(1)、FALSE(0)、或NULL值。位图索引的位图存放在 B-Tree结构的页节点中。B-Tree结构使查找位图非常方便和快速。另外,位图以一种压缩格式存放,因此占用的磁盘空间比B-Tree索引要小得多。位图索引的格式如表26-1所示。
    表26-1 位图索引的格式

    值 1 2 3 4 5 6 7 8 9 10
    Male 1 0 0 0 0 0 0 0 1 1
    Female 0 1 1 1 0 0 1 1 0 0
    Null 0 0 0 0 1 1 0 0 0 0

    如果搜索where gender=’Male’,要统计性别是”Male”的列行数的话,Oracle很快就能从位图中找到共3行即第1,9,10行是符合条件的;如果要搜索where gender=’Male’ or gender=’Female’的列的行数的话,也很容易从位图中找到共8行即1,2,3,4,7,8,9,10行是符合条件的。如果要搜索表的值的话,那么Oracle会用内部的转换函数将位图中的相关信息转换成rowid来访问数据块。


    26.2.5 函数索引
    基于函数的索引也是8i以来的新产物,它有索引计算列的能力,它易于使用并且提供计算好的值,在不修改应用程序的逻辑上提高了查询性能。使用基于函数的索引有几个先决条件:
    (1)必须拥有QUERY REWRITE(本模式下)或GLOBAL QUERY REWRITE(其他模式下)权限。
    (2)必须使用基于成本的优化器,基于规则的优化器将被忽略。
    (3)必须设置以下两个系统参数:
    QUERY_REWRITE_ENABLED=TRUE
    QUERY_REWRITE_INTEGRITY=TRUSTED
    可以通过alter system set,alter session set在系统级或线程级设置,也可以通过在init.ora添加实现。
    这里举一个基于函数的索引的例子:
    SQL> create index test.ind_fun on test.testindex(upper(a));
    索引已创建。
    SQL> insert into testindex values('a',2);
    已创建 1 行。
    SQL> commit;
    提交完成。
    SQL> select /*+ RULE*/* FROM test.testindex where upper(a)='A';
    A B
    -- ----------
    a 2
    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=HINT: RULE
    1 0 TABLE ACCESS (FULL) OF 'TESTINDEX'
    (优化器选择了全表扫描)
    --------------------------------------------------------------------
    SQL> select * FROM test.testindex where upper(a)='A';
    A B
    -- ----------
    a 2
    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=5)
    1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TESTINDEX' (Cost=2 Card=
    1 Bytes=5)
    2 1 INDEX (RANGE SCAN) OF 'IND_FUN' (NON-UNIQUE) (Cost=1 Car
    d=1)(使用了ind_fun索引)


    26.3 各种索引的创建方法
    (1)*Tree索引。
    Create index indexname on tablename(columnname[columnname...])
    (2)反向索引。
    Create index indexname on tablename(columnname[columnname...]) reverse
    (3)降序索引。
    Create index indexname on tablename(columnname DESC[columnname...])
    (4)位图索引。
    Create BITMAP index indexname on tablename(columnname[columnname...])
    (5)函数索引。
    Create index indexname on tablename(functionname(columnname))
    注意:创建索引后分析要索引才能起作用。
    analyze index indexname compute statistics;


    26.4 各种索引使用场合及建议(1)B*Tree索引。
    常规索引,多用于oltp系统,快速定位行,应建立于高cardinality列(即列的唯一值除以行数为一个很大的值,存在很少的相同值)。
    (2)反向索引。
    B*Tree的衍生产物,应用于特殊场合,在ops环境加序列增加的列上建立,不适合做区域扫描。
    (3)降序索引。
    B*Tree的衍生产物,应用于有降序排列的搜索语句中,索引中储存了降序排列的索引码,提供了快速的降序搜索。
    (4)位图索引。
    位图方式管理的索引,适用于OLAP(在线分析)和DSS(决策处理)系统,应建立于低cardinality列,适合集中读取,不适合插入和修改,提供比B*Tree索引更节省的空间。
    (5)函数索引。
    B*Tree的衍生产物,应用于查询语句条件列上包含函数的情况,索引中储存了经过函数计算的索引码值。可以在不修改应用程序的基础上能提高查询效率。


    26.5 附表(索引什么时候不工作)
    首先要声明两个知识点:
    (1)RBO&CBO。
    Oracle 有两种执行优化器,一种是RBO(Rule Based Optimizer)基于规则的优化器,这种优化器是基于sql语句写法选择执行路径的;另一种是CBO(Cost Based Optimizer)基于规则的优化器,这种优化器是Oracle根据统计分析信息来选择执行路径,如果表和索引没有进行分析,Oracle将会使用 RBO代替CBO;如果表和索引很久未分析,CBO也有可能选择错误执行路径,不过CBO是Oracle发展的方向,自8i版本来已经逐渐取代RBO.
    (2)AUTOTRACE。
    要看索引是否被使用我们要借助Oracle的一个叫做AUTOTRACE功能,它显示了sql语句的执行路径,我们能看到Oracle内部是怎么执行sql的,这是一个非常好的辅助工具,在sql调优里广泛被运用。我们来看一下怎么运用AUTOTRACE:
    ① 由于AUTOTRACE自动为用户指定了Execution Plan,因此该用户使用AUTOTRACE前必须已经建立了PLAN_TABLE。如果没有的话,请运行utlxplan.sql脚本(它在$ORACLE_HOME/rdbms/admin目录中)。
    ② AUTOTRACE可以通过运行plustrce.sql脚本(它在$ORACLE_HOME/sqlplus/admin目录中)来设置,用sys用户登陆然后运行plustrce.sql后会建立一个PLUSTRACE角色,然后给相关用户授予PLUSTRACE角色,然后这些用户就可以使用 AUTOTRACE功能了。
    ③ AUTOTRACE的默认使用方法是set autotrace on,但是这方法不总是适合各种场合,特别当返回行数很多的时候。Set autotrace traceonly提供了只查看统计信息而不查询数据的功能。
    SQL> set autotrace on
    SQL> select * from test;
    A
    ----------
    1
    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=CHOOSE
    1 0 TABLE ACCESS (FULL) OF 'TEST'
    Statistics
    ----------------------------------------------------------
    0 recursive calls
    0 db block gets
    0 consistent gets
    0 physical reads
    0 redo size
    0 bytes sent via SQL*Net to client
    0 bytes received via SQL*Net from client
    0 SQL*Net roundtrips to/from client
    0 sorts (memory)
    0 sorts (disk)
    rows processed


    SQL> set autotrace traceonly
    SQL> select * from test.test;

    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=CHOOSE
    1 0 TABLE ACCESS (FULL) OF 'TEST'

    Statistics
    ----------------------------------------------------------
    0 recursive calls
    0 db block gets
    0 consistent gets
    0 physical reads
    0 redo size
    0 bytes sent via SQL*Net to client
    0 bytes received via SQL*Net from client
    0 SQL*Net roundtrips to/from client
    0 sorts (memory)
    0 sorts (disk)
    rows processed
    Hints是Oracle提供的一个辅助用法,按字面理解就是‘提示’的意思,确实它起得作用也是提示优化器按它所提供的关键字来选择执行路径,特别适用于sql调整的时候。使用方法如下:
    {DELETE|INSERT|SELECT|UPDATE} /*+ hint [text] [hint[text]]... */
    具体可参考Oracle SQL Reference。
    有了前面这些知识点,接下来让我们来看一下什么时候索引是不起作用的。以下列出几种情况。


    (1)类型不匹配时。
    SQL> create table test.testindex (a varchar(2),b number);
    表已创建。
    SQL> create index ind_cola on test.testindex(a);
    索引已创建。
    SQL> insert into test.testindex values('1',1);
    已创建 1 行。
    SQL> commit;
    提交完成。
    SQL> analyze table test.testindex compute statistics for all indexes;
    表已分析。
    SQL> set autotrace on;

    SQL> select /*+RULE */* FROM test.testindex where a='1';(使用基于rule的优化器,数据类型匹配的情况下)
    A B
    -- ----------
    1 1
    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=HINT: RULE
    1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TESTINDEX'
    2 1 INDEX (RANGE SCAN) OF 'IND_COLA' (NON-UNIQUE)(使用了索引ind_cola)
    ――――――――――――――――――――――――――――――――――
    SQL> select /*+RULE */* FROM test.testindex where a=1;(数据类型不匹配的情况)
    A B
    -- ----------
    1 1
    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=HINT: RULE
    1 0 TABLE ACCESS (FULL) OF 'TESTINDEX'(优化器选择了全表扫描)


    (2)条件列包含函数但没有创建函数索引。
    SQL> select /*+ RULE */* FROM test.testindex where upper(a)= 'A';(使用了函数upper()在列a上);
    A B
    -- ----------
    a 2
    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=HINT: RULE
    1 0 TABLE ACCESS (FULL) OF 'TESTINDEX'(优化器选择全表扫描)
    ----------------------------------------------------------
    创建基于函数的索引
    SQL> create index test.ind_fun on test.testindex(upper(a));
    索引已创建。
    SQL> insert into testindex values('a',2);
    已创建1行。
    SQL> commit;
    提交完成。
    SQL> select /*+ RULE*/* FROM test.testindex where upper(a)='A';
    A B
    -- ----------
    a 2
    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=HINT: RULE
    1 0 TABLE ACCESS (FULL) OF 'TESTINDEX'
    (在RULE优化器下忽略了函数索引选择了全表扫描)
    -----------------------------------------------------------
    SQL> select * FROM test.testindex where upper(a)
    ='A';
    A B
    -- ----------
    a 2
    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=5)
    1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TESTINDEX' (Cost=2 Card=
    1 Bytes=5)
    2 1 INDEX (RANGE SCAN) OF 'IND_FUN' (NON-UNIQUE) (Cost=1 Car
    d=1)(CBO优化器使用了ind_fun索引)


    (3)复合索引中的前导列没有被作为查询条件。
    创建一个复合索引
    SQL> create index ind_com on test.testindex(a,b);
    索引已创建。
    SQL> select /*+ RULE*/* from test.testindex where a='1';
    A B
    -- ----------
    1 2
    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=HINT: RULE
    1 0 INDEX (RANGE SCAN) OF 'IND_COM' (NON-UNIQUE)(条件列表包含前导列时使用索引ind_com)
    SQL> select /*+ RULE*/* from test.testindex where b=1;
    未选定行
    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=HINT: RULE
    1 0 TABLE ACCESS (FULL) OF 'TESTINDEX'(条件列表不包括前导列是选择全表扫描)
    -----------------------------------------------------------


    (4)CBO模式下选择的行数比例过大,优化器采取了全表扫描。
    SQL> select * from test.testindex where a='1';
    A B
    -- ----------
    1 2
    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=5)
    1 0 TABLE ACCESS (FULL) OF 'TESTINDEX' (Cost=1 Card=1 Bytes=5)
    (表一共2行,选择比例为50%,所以优化器选择了全表扫描)
    ――――――――――――――――――――――――――――――――――
    下面增加表行数
    SQL> declare i number;
    2 begin
    3 for i in 1 .. 100 loop
    4 insert into test.testindex values (to_char(i),i);
    5 end loop;
    6 end;
    7 /
    PL/SQL 过程已成功完成。
    SQL> commit;
    提交完成。
    SQL> select count(*) from test.testindex;
    COUNT(*)
    ----------
    102
    SQL> select * from test.testindex where a='1';
    A B
    ---- ----------
    1 1
    1 2
    Execution Plan
    SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=5)
    1 0 INDEX (RANGE SCAN) OF 'IND_COM' (NON-UNIQUE) (Cost=1 Card=1 Bytes=5)
    (表一共102行,选择比例为2/102=2%,所以优化器选择了索引扫描)


    (5)CBO模式下表很久没分析,表的增长明显,优化器采取了全表扫描。SQL> select * from test.testindex where a like '1%';
    A B
    ---- ----------
    1 2
    1 1
    10 10
    11 11
    12 12
    13 13
    14 14
    15 15
    16 16
    17 17
    18 18
    19 19
    100 100
    已选择13行。
    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=13 Bytes=52)
    1 0 TABLE ACCESS (FULL) OF 'TESTINDEX' (Cost=1 Card=13 Bytes=52)
    (表一共102行,选择比例为13/102>10%,优化器选择了全表扫描)
    ――――――――――――――――――――――――――――――――――
    增加表行数
    SQL> declare i number;
    2 begin
    3 for i in 200 .. 1000 loop
    4 insert into test.testindex values (to_char(i),i);
    5 end loop;
    6 end;
    7 /
    PL/SQL 过程已成功完成。
    SQL> commit;
    提交完成。
    SQL> select count(*) from test.testindex;
    COUNT(*)
    ----------
    903
    SQL> select * from test.testindex where a like '1%';
    A B
    ---- ----------
    1 2
    1 1
    10 10
    11 11
    12 12
    13 13
    14 14
    15 15
    16 16
    17 17
    18 18
    19 19
    100 100
    1000 1000
    已选择14行。
    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=13 Bytes=52)
    1 0 TABLE ACCESS (FULL) OF 'TESTINDEX' (Cost=1 Card=13 Bytes=52)
    (表一共903行,选择比例为14/903<5%,优化器选择了全表扫描,选择路径是错误的)
    ―――――――――――――――――――――――――――――
    给表做分析

    SQL> analyze table test.testindex compute statistics for table for all indexed c
    olumns for all indexes;
    表已分析。
    SQL> select * from test.testindex where a like '1%';
    A B
    ---- ----------
    1 2
    1 1
    10 10
    100 100
    1000 1000
    11 11
    12 12
    13 13
    14 14
    15 15
    16 16
    17 17
    18 18
    19 19
    已选择14行。
    Execution Plan
    ----------------------------------------------------------
    0 SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=24 Bytes=120)
    1 0 TABLE ACCESS (BY INDEX ROWID) OF 'TESTINDEX' (Cost=4 Card=
    24 Bytes=120)
    2 1 INDEX (RANGE SCAN) OF 'IND_COLA' (NON-UNIQUE) (Cost=2 Ca
    rd=24)
    (经过分析后优化器选择了正确的路径,使用了ind_cola索引)
    ――――――――――――――――――――――――――――――――――
    小结
    这篇文章介绍了oracle数据库中的各种索引,通过分析它们的结构,我们知道了它们的特点和应用范围,同时介绍了如何去避免索引失效,希望对大家有所帮助。

    Posted Jan 15 2009, 02:26 PM by slash with no comments
    Filed under:
  • 理解Oracle的Rowid含义

     

    8以下ROWID组成(也叫受限Rowid)为:FFFF.BBBBBBBB.RRRR,占用6个字节(10bit file#+22bit+16bit),但是,为了扩充的需要
    ,如数据文件的扩充,现在的Rowid改为:OOOOOOFFFBBBBBBRRR,占用10个字节(32bit+10bit rfile#+22bit+16bit)。其中,
    O是对象IDF是文件IDB是块IDR是行ID。由于rowid的组成从file#变成了rfile#,所以数据文件数的限制也从整个库不能超过1023
    个变成了每个表空间不能超过1023个数据文件。
    注意:这里的O,代表的是data_object_id,是与段物理存储位置相关的一个信息,所以data_object_id + rfile#就能最终定位到该rowid
    在那个确定的物理数据文件。
    如果我们查询一个表的ROWID,就可以获得object的信息,文件信息,块信息与行信息等等,如根据其中块的信息,可以知道该表确切占用
    了多少个块,每行在哪个块上,哪个数据文件上。
    用例子说明一下Rowid的组成:
    SQL> select rowid from emp where rownum = 1;
    AAAAeNAADAAAAWZAAA
    分解一下,可以看到
    Data Object number = AAAAeN
    File = AAD
    Block = AAAAWZ
    ROW = AAA
    另外,我们需要注意的是,ROWID64进制的,分布关系如下
    A-Z <==> 0 - 25 (26)
    a-z <==> 26 - 51 (26)
    0-9 <==> 52 - 61 (10)
    +/ <==> 62 - 63 (2)
    拿其中的Data Object number= AAAAeN为例子,
    N64进制中的13,位置为0
    13 * (64 ^ 0) = 13
    E64进制中的30,位置为1
    30 * (64 ^ 1) = 1920
    A64进制中的 0
    所以 A * (64 ^ 2) = 0
    A * (64 ^ 3) = 0
    A * (64 ^ 4) = 0
    A * (64 ^ 5) = 0
    则有AAAAeN = 0 + 0 + 0 + 0 + 1920 + 13 = 1933,表示该行存在的对象,对应的对象号为1933
    而且,我们也可以利用oracle提供的包,dbms_rowid来做到这一点:
    代码:
    select dbms_rowid.rowid_object('AAAAeNAADAAAAWZAAA') data_object_id#,
           dbms_rowid.rowid_relative_fno('AAAAeNAADAAAAWZAAA') rfile#,
           dbms_rowid.rowid_block_number('AAAAeNAADAAAAWZAAA') block#,
           dbms_rowid.rowid_row_number('AAAAeNAADAAAAWZAAA') row# from dual;
    DATA_OBJECT_ID#     RFILE#     BLOCK#       ROW#

    --------------- ---------- ---------- ----------

               1933          3       1433          0
    关于更多dbms_rowid的用法,可以参考包的说明或者是oracle手册。
    Posted Dec 28 2008, 11:17 AM by slash with 1 comment(s)
    Filed under:
  • AWR Report

     一、WHY——为什么会出现ASH和AWR?
    1.     10g之前

    用户的连接将产生会话,当前会话记录保存在v$session中;处于等待状态的会话会被复制一份放在v$session_wait中。当该连接断开后,其原来的连接信息在v$session和v$session_wait中就会被删除。这是10g之前的状况。
    2.     v$session_wait_history与ASH

    若是一个普通的会话(我是指没有大量地耗费资源),则对于性能调整来说无足轻重。但若该会话在活动时大量占用了资源(比如:CPU,内存,I/O等),该 会话信息的丢失,将无法评测当时的系统瓶颈究竟是什么。令DBA高兴的是,oracle10g中保留下了v$session_wait中的这些信息。

    在10g中新出现了一个视图:v$session_wait_history。这个视图保存了每个活动session在v$session_wait中最 近10次的等待事件。但这对于一段时期内的数据库性能状况的监测是远远不够的,为了解决这个问题,在10g中还新添加了一个视 图:v$active_session_history。这就是ASH(active session history)。

    典型的情况下,为了诊断当前数据库的状态,需要最近的五到十分钟的详细信息。然而,由于记录session的活动信息是很费时间和空间的,ASH采用的策 略是:保存处于等待状态的活动session的信息,每秒从v$session_wait中采样一次,并将采样信息保存在内存中。
    3.     AWR

    注意,ASH的采样数据是保存在内存中。而分配给ASH的内存空间是有限的,当所分配空间占满后,旧的记录就会被覆盖掉;而且数据库重启后,所有的这些 ASH信息都会消失。这样,对于长期检测oracle的性能是不可能的。在Oracle10g中,提供了永久保留ASH信息的方法,这就是 AWR(auto workload repository)。

    由于全部保存ASH中的信息是非常耗费时间和空间的,AWR采用的策略是:每小时对v$active_session_history进行采样一次,并将 信息保存到磁盘中,并且保留7天,7天后旧的记录才会被覆盖。这些采样信息被保存在视图wrh$_active_session_history中。而这 个采样频率(1小时)和保留时间(7天)是可以根据实际情况进行调整的,这就给DBA们提供了更加有效的系统监测工具。

    AWR永久地保存系统的性能诊断信息,由SYS用户拥有。一段时间后,你可能想清除掉这些信息;有时候为了性能诊断,你可能需要自己定义采样频率来获取系 统快照信息。Oracle 10g在包dbms_workload_repository中提供了很多过程,通过这些过程,你可以管理快照并设定基线(baselines)。
    4.     小结

    这样,我们就知道了ASH和AWR产生的原因和功能。ASH保存了系统最新的处于等待的会话记录,可以用来诊断数据库的当前状态;而AWR中的信息最长可能有1小时的延迟,所以其采样信息并不能用于诊断数据库的当前状态,但可以用来作为一段时期内数据库性能调整的参考。

    对于这些视图间的继承关系,eygle给出了一个关系图:

    图1 各个视图的层次

    其中视图dba_hist_active_sess_history是wrh$_active_session_history和其他几个视图的联合展现,通常通过这个视图进行历史数据的访问。
    二、WHAT——什么是AWR?

    现在我们稍微详细地了解一下刚才所说内容。
    1.     ash占用的内存大小

    ASH的采集信息保存在内存中,在旧的信息被采样到AWR中后,可被新采集的信息覆盖,重启oracle后该信息被清除。分配给ASH的内存大小可以查询到:

    SQL> select pool, name, bytes/1024/1024 From v$sgastat where name like '%ASH %';

    POOL          NAME          BYTES/1024/1024
    ------------- ------------- ---------------
    shared pool   ASH buffers                 2

    2.     AWR更正

    为了便于描述和理解,在第一部分中,我们说AWR就是保存ASH中的信息。

    其实,AWR记录的信息不仅是ASH,还可以收集到数据库运行的各方面统计信息和等待信息,用以诊断分析。

    AWR的采样方式是,以固定的时间间隔为其所有重要的统计信息和负载信息执行一次采样,并将采样信息保存在AWR中。

    可以这样说:ASH中的信息被保存到了AWR中的视图wrh$_active_session_history中。ASH是AWR的真子集。
    3.     mmon进程与mmnl进程

    快照由一个称为 MMON 的新的后台进程(及其从进程)以及MMNL后台进程自动地每隔固定时间采样一次。我们先来看一下10g的概念指南中对这两个新增加的后台进程的介绍:

    *        MMON进程负责执行多种和管理相关(manageability-related)的后台任务,例如:

        * 当某个测量值(metrics)超过了预设的限定值(threshold value)后提交警告
        * 创建新的 MMON 隶属进程(MMON slave process)来进行快照(snapshot)
        * 捕获最近修改过的 SQL 对象的统计信息

    *        MMNL进程负责执行轻量级的且频率较高的和可管理性相关的后台任务,例如捕获会话历史信息,测量值计算等。

    AWR的采样工作由MMON进程每个1小时执行一次,ASH信息同样会被采样写出到AWR负载库中。虽然ASH buffer被设计为保留1小时的信息,但很多时候这个内存是不够的,当ASH buffer写满后,另外一个后台进程MMNL将会主动将ASH信息写出。
    4.     SYSAUX表空间

    这些采样数据都存储在SYSAUX表空间中,并且以WRM$_* 和 WRH$_*的格式命名。前一种类型存储元数据信息(如检查的数据库和采集的快照),后一种类型保存实际采集的统计数据。

    SQL> select table_name from dba_tables where table_name like 'WRM$%';



    TABLE_NAME

    -----------------------

    WRM$_WR_CONTROL

    WRM$_SNAP_ERROR

    WRM$_SNAPSHOT

    WRM$_DATABASE_INSTANCE

    WRM$_BASELINE

    当SYSAUX表空间满后,AWR将自动覆盖掉旧的信息,并在警告日志中记录一条相关信息:

    ORA-1688: unable to extend table SYS.WRH$_ACTIVE_SESSION_HISTORY partition WRH$_ACTIVE_3533490838_1522 by 128 in                 tablespace SYSAUX
    5.     采样频率和保留时间

    可以通过查询视图dba_hist_wr_control或(wrm$_wr_control)来查询AWR的采样频率和保留时间。默认为每1小时采样一次,采样信息保留时间为7天。

    SQL> select * from dba_hist_wr_control;



    DBID SNAP_INTERVAL RETENTION   TOPNSQL
    ---- ------------- ----------- ----------
    1148 +00000 00:1   +00007 00:0 DEFAULT



    SQL> select DBID, SNAP_INTERVAL, SNAPINT_NUM, RETENTION from wrm$_wr_control;

          DBID SNAP_INTERVAL      SNAPINT_NUM RETENTION
    ---------- ------------------ ----------- --------------------
    1160732652 +00000 01:00:00.0         3600 +00007 00:00:00.0


    6.     采样数据量

    由于数据量巨大,把所有ASH数据写到磁盘上是不可接受的。一般是在写到磁盘的时候过滤这个数据,写出的数据占采样数据的10%,写出时通过direct-path insert完成,尽量减少日志生成,从而最小化数据库性能的影响。
    7.     初始化参数statistics_level

    AWR的行为受到参数STATISTICS_LEVEL的影响。这个参数有三个值:

    *        BASIC:awr统计的计算和衍生值关闭.只收集少量的数据库统计信息.

    *        TYPICAL:默认值.只有部分的统计收集.他们代表需要的典型监控oracle数据库的行为.

    *        ALL : 所有可能的统计都被捕捉. 并且有操作系统的一些信息.这个级别的捕捉应该在很少的情况下,比如你要更多的sql诊断信息的时候才使用.
    三、HOW——如何使用AWR?

    AWR由ORACLE自动产生,但是也可以通过DBMS_WORKLOAD_REPOSITORY包来手工创建、删除和修改。可以使用desc命令查看该包中的过程。下面只介绍几个常用的:
    1.     手工创建一个快照

    SQL> select count(*) from wrh$_active_session_history;



    COUNT(*)

    ----------

    317



    SQL> begin

    2 dbms_workload_repository.create_snapshot();

    3 end;

    4 /



    PL/SQL 过程已成功完成。



    SQL> select count(*) from wrh$_active_session_history;



    COUNT(*)

    ----------

           320


    2.     手工删除指定范围的快照

    SQL> select * from wrh$_active_session_history where snap_id = 96;



       SNAP_ID       DBID INSTANCE_NUMBER SAMPLE_ID SAMPLE_TIME

    ---------- ---------- --------------- ---------- ----------------------------

            96 1160732652               1     236930 06-10月-07 11.26.04.562 上午

            96 1160732652               1     236930 06-10月-07 11.26.04.562 上午

            96 1160732652               1     236930 06-10月-07 11.26.04.562 上午



    SQL> begin

    2 dbms_workload_repository.drop_snapshot_range(low_snap_id => 96, high_snap_id => 96, dbid => 1160732652);

    3 end;

    4 /



    PL/SQL 过程已成功完成。



    SQL> select * from wrh$_active_session_history where snap_id = 96;



    未选定行


    3.     修改采集时间和统计信息保留时间

    PROCEDURE MODIFY_SNAPSHOT_SETTINGS

    参数名称                       类型                    输入/输出默认值?

    ------------------------------ ----------------------- ------ --------

    RETENTION                      NUMBER                  IN     DEFAULT

    INTERVAL                       NUMBER                  IN     DEFAULT

    TOPNSQL                        NUMBER                  IN     DEFAULT

    DBID                           NUMBER                  IN     DEFAULT

    通过修改retention参数可以修改awr信息的保留期限。默认的是七天,最小的值是一天。如果把retention设置为零,自动清除就关闭了.如 果awr发现sysaux空间不够,它通过删除那些最老部分的快照来重新使用这些空间.同时,也会给dba发一条警告,告诉sysaux空间不够了(在警 告日志中).

    通过修改interval参数可以修改awr信息的采样频率。最小的值是10分钟,默认的是60分钟.典型的值是10,20,30,60,120等等。把 interval设为0则关闭自动捕捉快照.如将收集间隔时间改为30 分钟一次。并且保留5天时间(注:单位都是为分钟):

    SQL> select *from dba_hist_wr_control;



          DBID SNAP_INTERVAL      RETENTION          TOPNSQL

    ---------- ------------------ -------------------------- -----------

    1160732652 +00000 01:00:00.0 +00007 00:00:00.0          DEFAULT



    SQL> exec dbms_workload_repository.modify_snapshot_settings(interval=>30, retention=>5*24*60);



    PL/SQL 过程已成功完成。



    SQL> SELECT *from dba_hist_wr_control;



          DBID SNAP_INTERVAL       RETENTION         TOPNSQL

    ---------- ------------------- ------------------------- -----------

    1160732652 +00000 00:30:00.0   +00005 00:00:00.0         DEFAULT


    SQL>

    4.     设置基线

    基线(baseline)是一种机制,这样你可以在重要时间的快照信息集做标记。一个基线定义在一对快照之间,快照通过他们的快照序列号识别.每个基线有且只有一对快照。

    一次典型的性能调整实践从采集量度的基准线集合、作出改动、然后采集另一个基准线集合开始。可以比较这两个集合来检查所作的改动的效果。在 AWR 中,对现有的已采集的快照可以执行相同类型的比较。

    假定一个名称为 apply_interest 的高度资源密集的进程在下午 1:00 到 3:00 之间运行,对应快照 ID 95 到 98。我们可以为这些快照定义一个名称为 apply_interest_1 的基准线:

    SQL> select *From dba_hist_baseline;



    未选定行



    SQL> select * from wrm$_baseline;



    未选定行



    SQL> exec dbms_workload_repository.create_baseline(95, 98, 'apply_interest_1');



    PL/SQL 过程已成功完成。



    这一操作将快照从 95 到 98 编号,作为上面指定的基准线的一部分。查看现有的基准线:

    SQL> select *from dba_hist_baseline;



          DBID BASELINE_ID BASELINE_NAME     START_SNAP_ID START_SNAP_TIME               END_SNAP_ID END_SNAP_TIME

    ---------- ----------- ------------------------- ------------- ------------------------------------- ----------- ------------

    1160732652           1 apply_interest_1             95 06-10月-07 11.00.05.375 上午           98 06-10月-07 01.44.58.062 下午



    SQL> select *from wrm$_baseline;



          DBID BASELINE_ID BASELINE_NAME        START_SNAP_ID END_SNAP_ID

    ---------- ----------- ---------------------------- ------------- -----------

    1160732652           1 apply_interest_1                95          98



    SQL>



    在一些调整步骤之后,我们可以创建另一个基准线 — 假设名称为 apply_interest_2,然后只为那些与这两条基准线相关的快照比较量度。

    SQL> exec dbms_workload_repository.create_baseline(92, 94, 'apply_interest_2');



    PL/SQL 过程已成功完成。

    像这样把快照分隔在仅仅几个集合中有助于研究调整对于性能量度的影响。您可以在分析之后使用 drop_baseline() 来删除基准线;快照将保留(也可级联删除)。此外,当清除例程开始删除旧的快照时,与基准线相关的快照不会被清除,从而允许进行进一步的分析。
    5.     删除基线

    如果要删除一个基准线:

    SQL> exec dbms_workload_repository.drop_baseline(baseline_name=>'apply_interest_1', cascade=>false);



    PL/SQL 过程已成功完成。



    SQL> select *from wrh$_active_session_history where snap_id in (95,96,97,98);



       SNAP_ID       DBID INSTANCE_NUMBER SAMPLE_ID SAMPLE_TIME

    ---------- ---------- --------------- ---------- -------------------------------

            95 1160732652               1     235360 06-10月-07 10.56.29.872 上午

            95 1160732652               1     235230 06-10月-07 10.54.19.857 上午

            95 1160732652               1     233130 06-10月-07 10.19.19.478 上午

            95 1160732652               1     232830 06-10月-07 10.14.18.859 上午

            95 1160732652               1     232250 06-10月-07 10.04.38.481 上午

            97 1160732652               1     238600 06-10月-07 12.33.08.420 下午

            97 1160732652               1     238600 06-10月-07 12.33.08.420 下午

            97 1160732652               1     238600 06-10月-07 12.33.08.420 下午

            97 1160732652               1     238600 06-10月-07 12.33.08.420 下午

            97 1160732652               1     238600 06-10月-07 12.33.08.420 下午

            97 1160732652               1     238600 06-10月-07 12.33.08.420 下午



       SNAP_ID       DBID INSTANCE_NUMBER SAMPLE_ID SAMPLE_TIME

    ---------- ---------- --------------- ---------- -------------------------------

            97 1160732652               1     238420 06-10月-07 11.50.55.686 上午

            97 1160732652               1     238230 06-10月-07 11.47.45.687 上午

            98 1160732652               1     239140 06-10月-07 01.42.00.976 下午

            98 1160732652               1     239140 06-10月-07 01.42.00.976 下午

            98 1160732652               1     239140 06-10月-07 01.42.00.976 下午

            98 1160732652               1     239140 06-10月-07 01.42.00.976 下午

            98 1160732652               1     239140 06-10月-07 01.42.00.976 下午

            98 1160732652               1     239130 06-10月-07 01.27.04.161 下午

            98 1160732652               1     239130 06-10月-07 01.27.04.161 下午

            98 1160732652               1     239130 06-10月-07 01.27.04.161 下午



    已选择21行。



    SQL> exec dbms_workload_repository.drop_baseline(baseline_name=>'apply_interest_2', cascade=>true);



    PL/SQL 过程已成功完成。



    SQL> select *from wrh$_active_session_history where snap_id in (92,93,94);

    未选定行



    SQL>


    6.     生成报表

    awr有个报表生成机制,可以对存储在workload资料库的统计产生汇总报表。这个分析对一段时间的统计做的。这个报表生成机制很像statspack。

    可以使用脚本awrrpt.sql或awrrpti.sql来查看AWR报告(非常类似statspack中的spreport.sql),这两个脚本都 在目录$ORACLE_HOME/rdbms/admin中。Awrrpt.sql脚本可以显示指定快照id范围的诊断信息,报告可以保存为文本文件或 HTML文件;awrrpti.sql脚本与awrrpt.sql类似,唯一的不同就是在awrrpti.sql脚本中,你可以指定数据库ID和实例 ID(作为参数)。报告包括如下诊断信息:

    [1] Report summary
    [1] Wait events statistics
    [1] SQL statistics
    [1] Instance activity statistics
    [1] I/O statistics
    [1] Buffer pool statistics
    [1] Advisory statistics
    [1] Wait statistics
    [1] Undo statistics
    [1] Latch statistics
    Segment statistics
    [1] Dictionary cache statistics
    [1] Library cache statistics
    [1] SGA statistics
    [1] Resource limit statistics
    [1] init.ora parameters

    通过运行$ORACLE_HOME/rdbms/admin目录中的awrrpt.sql脚本,AWR的功能可以立即通过它从采集的统计数据和量度中生成 的报表得到最好的说明。这个脚本显示所有的现有AWR快照并请求两个特定的快照作为时间间隔边界。它产生两种类型的输出:文本格式(类似于 Statspack报表的文本格式但来自于AWR信息库)和默认的HTML格式(拥有到部分和子部分的所有超链接),从而提供了非常用户友好的报表。

    运行这个脚本必须要select any dictionary权限.这个脚本提示你输入选项如怎么和在哪里生成这个报表:

    *首先,你需要指明你要生成html还是text格式的
    *要选择快照的天数:输入天数,和你最近的快照,可选的,你可以使用dba_hist_snapshot表来看你要用哪个snap_id.
    *开始snap_id和终止snap_id,这个快照对定义你的报表产生的时间间隔.
    *文件名称,报告写的用户指定的文件.

    现在运行该脚本以查看报表,从而对AWR的报表功能有一个直观的了解。

    SQL> @D:\oracle\product\10.2.0\db_1\RDBMS\ADMIN\awrrpt.sql



    Current Instance

    ~~~~~~~~~~~~~~~~



       DB Id    DB Name      Inst Num Instance

    ----------- ------------ -------- ------------

    1160732652 ORCL                1 orcl





    Specify the Report Type

    ~~~~~~~~~~~~~~~~~~~~~~~

    Would you like an HTML report, or a plain text report?

    Enter 'html' for an HTML report, or 'text' for plain text

    Defaults to 'html'

    输入 report_type 的值:



    Type Specified:                  html





    Instances in this Workload Repository schema

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



       DB Id     Inst Num DB Name      Instance     Host

    ------------ -------- ------------ ------------ ------------

    * 1160732652        1 ORCL         orcl         YUECHAOTIAN



    Using 1160732652 for database Id

    Using          1 for instance number





    Specify the number of days of snapshots to choose from

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Entering the number of days (n) will result in the most recent

    (n) days of snapshots being listed. Pressing <return> without

    specifying a number lists all completed snapshots.





    输入 num_days 的值: 4



    Listing the last 4 days of Completed Snapshots



                                                            Snap

    Instance     DB Name        Snap Id    Snap Started    Level

    ------------ ------------ --------- ------------------ -----

    orcl         ORCL                81 04 10月 2007 07:24     1



                                     83 05 10月 2007 13:34     1

                                     84 05 10月 2007 16:19     1

                                     85 05 10月 2007 17:00     1

                                     86 05 10月 2007 18:00     1

                                     87 05 10月 2007 19:10     1

                                     88 05 10月 2007 20:00     1

                                     89 05 10月 2007 21:00     1

                                     90 05 10月 2007 22:00     1

                                     91 05 10月 2007 23:00     1



                                     95 06 10月 2007 11:00     1

                                     97 06 10月 2007 13:27     1

                                     98 06 10月 2007 13:44     1

                                     99 06 10月 2007 14:00     1

                                    100 06 10月 2007 14:30     1

                                    101 06 10月 2007 15:00     1

                                    102 06 10月 2007 15:30     1







    Specify the Begin and End Snapshot Ids

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    输入 begin_snap 的值: 100

    Begin Snapshot Id specified: 100



    输入 end_snap 的值: 102

    End   Snapshot Id specified: 102







    Specify the Report Name

    ~~~~~~~~~~~~~~~~~~~~~~~

    The default report file name is awrrpt_1_100_102.html. To use this name,

    press <return> to continue, otherwise enter an alternative.



    输入 report_name 的值:



    Using the report name awrrpt_1_100_102.html



    <HTML><HEAD><TITLE>AWR Report</TITLE>

    ……(省略结果)

    End of Report

    </BODY></HTML>

    Report written to awrrpt_1_100_102.html

    SQL>

    将产生的HTML脚本粘贴出来,用IE打开看看:一个指定时间段的完整的数据库性能报告就展现在我们面前了。下面是部分截图:

    图2 AWR报告截图
    7.     statspack和awr

    在过去,你可以手工的使用statspack来获得历史数据.你也可以继续在oracle10g中使用statspack,但是如果你要使用workload资料库,那你需要更改你的应用程序代码。statspack用户应该转到10g的workload 资料库。

    这里不支持数据从statspack移植到workload资料库.同样,也没有视图来模拟statspack.

    在rac环境,每个快照包含集群的所有的节点.每个节点的快照数据有相同的snap_id,但靠实例id来区分。一般地,rac中的快照是在同一时间捕捉的。
    你也可以使用db control来进行手工的快照。手工的快照支持系统产生的自动快照。手工的快照可以自定义在你要捕捉的系统行为的两个时间点跟自动的不一致的时候,从而拥有更大的灵活性。

    Posted Dec 17 2008, 03:11 PM by slash with no comments
    Filed under:
  • Excel读入dataset数据丢失的问题

     摘要:研究了ISAM驱动限制,分析了在.NET中采用Microsoft.Jet.OLEDB读取Excel文件产生数据丢失的原因,给出了解决此问题的方法。
        
      1 引言
      在应用程序的设计中,经常需要读取Excel数据或将Excel数据导入转换到其他数据载体中,例如将Excel数据通过应用程序导入SQL Sever等数据库中以备使用。笔者在开发“汽车产业链ASP协同商务平台”中遇到了类似需求。某汽车整车生产企业需要将其车辆发车信息发布到汽车产业链平台上去,其数据为内部ERP系统生成的Excel数据表,用户首先将该数据表上传至汽车产业链平台,平台将此Excel数据读取导入到平台内部的SQL Sever数据库中,以供其它应用使用。汽车产业链平台的开发使用的开发工具为VS.NET,使用的语言是C#,在开发的过程中发现使用 Microsoft.Jet.OLEDB.4.0读取数据会出现当某一字段内分别含有文本和数字的混合数据时,某一类型的数据会产生丢失。本文就对此问题产生的根源进行了分析并给出了相应的解决方法。
      
      2 问题描述
      Excel是Microsoft公司的电子表格处理软件,在现代办公及企业信息化的应用中使用非常广泛,正因如此,在程序设计中我们经常要通过访问Excel文件来获得数据,但Excel文件不是标准数据库[1]。
      ASP.NET也是Microsoft公司的产品,作为.NET FrameWork框架中的一个重要组成部分,其主要用于Web设计。我们在.NET中访问读取Excel数据时一般采用 Microsoft.Jet.OLEDB.4.0[2]。现以读取一个Excel文件auto.xls中sheet1工作表为例,工作表的内容如表1所示。
      表1 sheet1表的数据内容
      现将该表的数据内容读取并显示到到DataGrid中,简化的代码如下:
      String ConnStr = " Provider = Microsoft.Jet.OLEDB.4.0; DataSource=c:/auto.xls;Extended Properties='Excel 8.0;HDR=YES';";
      OleDbConnection Conn=new OleDbConnection(ConnStr);
      Conn.Open();
      string SQL="select * from [sheet1$]";
      OleDbDataAdapter da=new OleDbDataAdapter(SQL,ConnStr);
      DataSet ds=new DataSet();
      da.Fill(ds);
      DataGrid1.DataSource=ds;
      DataGrid1.DataBind();
      Conn.Close();
      但是运行以上代码的结果并不是期望的,它将显示为表2所示的内容。可以发现第一个字段中为“1042”的两个数据项变为空。
      表2 DataGrid1所显示的数据内容
      有程序设计人员将以上代码OleDbConnection连接字符串中的Extended Properties一项作了如下改动,Extended Properties='Excel 8.0;HDR=NO;IMEX=1’,认为可以解决此问题。由于在开发“汽车产业链协同商务平台”中碰到过类似问题,作了大量的测试后发现,添加 IMEX=1后并未实质上解决此问题。表现为:如果某字段前8条记录中全部为纯数字的话,那么在该字段随后的记录中含有字母或汉字的项将仍然变为空,但是如果该字段前8条记录中有一条不为纯数字,将能得到预期想要的结果。
      
      3 问题分析
      产生这种问题的根源与 Excel ISAM[3](Indexed Sequential Access Method,即索引顺序存取方法)驱动程序的限制有关,Excel ISAM 驱动程序通过检查前几行中实际值确定一个 Excel 列的类型,然后选择能够代表其样本中大部分值的数据类型[4]。也即Excel ISAM查找某列前几行(默认情况下是8行),把占多的类型作为其处理类型。例如如果数字占多,那么其它含有字母等文本的数据项就会置空;相反如果文本居多,纯数字的数据项就会被置空。
      现具体分析在第1节程序代码Extended Properties项中的HDR和IMEX所代表的含义。HDR用来设置是否将Excel表中第一行作为字段名,“YES”代表是,“NO”代表不是即也为数据内容;IMEX是用来告诉驱动程序使用Excel文件的模式,其值有0、1、2三种,分别代表导出、导入、混合模式。当我们设置IMEX=1时将强制混合数据转换为文本,但仅仅这种设置并不可靠,IMEX=1只确保在某列前8行数据至少有一个是文本项的时候才起作用,它只是把查找前8行数据中数据类型占优选择的行为作了略微的改变。例如某列前8行数据全为纯数字,那么它仍然以数字类型作为该列的数据类型,随后行里的含有文本的数据仍然变空。
      另一个改进的措施是IMEX=1与注册表值TypeGuessRows配合使用,TypeGuessRows 值决定了ISAM 驱动程序从前几条数据采样确定数据类型,默认为“8”。可以通过修改“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft \Jet\4.0\Engines\Excel”下的该注册表值来更改采样行数。但是这种改进还是没有根本上解决问题,即使我们把IMEX设为“1”, TypeGuessRows设得再大,例如1000,假设数据表有1001行,某列前1000行全为纯数字,该列的第1001行又是一个文本,ISAM驱动的这种机制还是让这列的数据变成空。
      
      4 解决方法
      从以上的分析中可以得知,当某列数据中含有混合类型时,在.NET中使用Microsoft.Jet.OLEDB.4.0来读取Excel文件造成数据丢失是不可避免的,要解决这个问题只能考虑采用其它数据读取方法。
      在.NET中读取Excel文件的另外一种方法是回到使用传统COM组件,这种方法在很多技术文章或论文中都有涉及,本文不作赘述。需要指出的是,使用 COM组件来读取Excel文件数据的效率较低,在作释放的时候有可能碰到不可预知的错误,特别开发Web应用的程序应该慎重使用。  

    本文提出另外一种利用读取CSV纯文本格式解决此问题的方法。
      (1)在读取Excel的.xls类型的文本数据之前,先将其转换为.csv格式,在Excel中直接另存为这种格式就可以达到转换的目的。CSV文件又称为逗号分隔的文件,是一种纯文本文件,它以“,”分隔数据列,本文表1的数据表用CSV格式存储后用纯文本编辑器打开的表现形式如表3所示。
      表3 采用CSV格式保存的表1数据
      需要指出的是,CSV文件也可以用Ole DB或ODBC的方式读取,但是如果采用这些方式读取其数据又会回到丢失数据的老路上,ISAM机制同样会发挥作用。
      (2)采用普通的读取文本文件的方法打开文件,读取第一行,用“,”作为分隔符获得各字段名,在DataTable中创建对应的各字段,字段的类型可以统一创建成“String”。
      
    本文原文
      (3)逐行读取数据行, 用“,”作为分隔符获得某行各列的数据并填入DataTable相应的字段中。
      实现的简化代码如下:
      String line;
      String [] split = null;
      DataTable table=new DataTable("auto");
      DataRow row=null;
      StreamReader sr=new StreamReader("c:/auto.csv",System.Text.Encoding.Default);
      //创建与数据源对应的数据列
      line = sr.ReadLine();
      split=line.Split(',');
      foreach(String colname in split){
      table.Columns.Add(colname,System.Type.GetType("System.String")); }
      //将数据填入数据表
      int j=0;
      while((line=sr.ReadLine())!=null){
       j=0;
       row = table.NewRow();
       split=line.Split(',');
       foreach(String colname in split){
       row[j]=colname;
       j++;}
       table.Rows.Add(row);}
       sr.Close();
      //显示数据
      dataGrid1.DataSource=table.DefaultView;
      dataGrid1.DataBind();
      
      5 结语
      在应用程序的设计中,需要访问Excel数据的情况非常普遍,本文以在.NET中对访问含有混合类型数据的Excel表格拟采取的方法进行探讨。当然,如果不存在混合类型的数据使用Microsoft.Jet.OLEDB为较佳方案。对于不是使用.NET开发的情况,本论文的分析和所提供的方法亦可参考。

    Posted Dec 16 2008, 10:42 AM by slash with no comments
    Filed under:
  • 解决SPC用户在Database中过多Session一例

    由于在Oracle Database 专用服务器中,一个Session会对应一个服务器进程,如果Session过多,占用的资源就会越大,所以我们应该控制极可能少的连接的Session及在数据处理完成后及时将连接关闭.

    之前查看SPC在Database中一直占有比较多的Session,多达几十个,状态都为INACTIVE

    SQL>select * from v$session where username='SPC';

    or

    SQL>select count(*) from v$session where username='SPC';

     

     如此多的Session让我怀疑是不是有些连接是不是没有关闭.那能不能找出这么Session都在执行什么语句呢?可以的.

    SQL> select sql_text from v$sqlarea a ,v$session b where a.address=b.sql_address and b.username='SPC'

     按照查出来的语句去程序中搜索,确实存在连接没有关闭的情况,调整程序...(省略n步)

     

    另外还有一种sql,在程序中始终找不到.

    例如 SELECT ac.constraint_name key_name,
               acc.column_name key_col, 1
                   FROM all_cons_columns acc, all_constraints ac
                   WHERE acc.owner = ac.owner
                     AND acc.constraint_name = ac.constraint_name
                     AND acc.table_name = ac.table_name
                     AND ac.constraint_type = 'P'
                     AND ac.owner = user
                     AND ac.table_name = :tablename
                   ORDER BY acc.constraint_name

     

    经询问总监,觉得此sql是在DbDataReader.GetSchemaTable()  自动生成的. 但是DbDataReader就没有关闭,导致了session没有关闭.

     

    经过调整, SPC用户基本都占用很少的Session了.

     

     在一个用户并发 不是很多的情况下,如果Session过多,就应该检查一下是否有连接未关闭的情况...

    Posted Dec 08 2008, 02:52 PM by slash with no comments
    Filed under:
  • Ado.Net读取Excel

     

    经常需要在数据库与Execl之间互导数据。net时代,ADO.NET可以使用使用Microsoft.Jet.OleDb访问访问Excel,网上已经有很多类似的资源,最典型也是最简单的可能如下:(asp.net环境)

     // 连接字符串            
            string xlsPath 
    = Server.MapPath("~/app_data/somefile.xls"); // 绝对物理路径
            string connStr = "Provider=Microsoft.Jet.OLEDB.4.0;" +
                            
    "Extended Properties=Excel 8.0;" + 
                            
    "data source=" + xlsPath;
            // 查询语句
            string sql 
    = "SELECT * FROM [Sheet1$]";

            DataSet ds 
    = new DataSet();
            OleDbDataAdapter da = new OleDbDataAdapter(sql, connStr);
            da.Fill(ds);    // 填充DataSet        
            
            // 在这里对DataSet中的数据进行操作        

            // 输出,绑定数据
            GridView1.DataSource 
    = ds.Tables[0]; 
            GridView1.DataBind();

    很简单吧?!一切就像操作数据库一样,只是需要注意的是:
    1。数据提供程序使用Jet,同时需要指定Extended Properties 关键字设置 Excel 特定的属性,不同版本的Excel对应不同的属性值:
    用于 Extended Properties 值的有效 Excel 版本。 
    对于 Microsoft Excel 
    8.0 (97)、9.0 (2000) 和 10.0 (2002) 工作簿,请使用 Excel 8.0。 

    对于 Microsoft Excel 
    5.0 和 7.0 (95) 工作簿,请使用 Excel 5.0。 

    对于 Microsoft Excel 
    4.0 工作簿,请使用 Excel 4.0。 

    对于 Microsoft Excel 
    3.0 工作簿,请使用 Excel 3.0。 

    ref:http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/dv_vbcode/html/vbtskcodeexamplereadingexceldataintodataset.asp

    2。数据源路径使用物理绝对路径(同Access)

    3。如何引用表名?
    对 Excel 工作簿中表(或范围)的有效引用。 
    若要引用完全使用的工作表的范围,请指定后面跟有美元符号的工作表名称。例如: 

    select * from 
    [Sheet1$]
    若要引用工作表上的特定地址范围,请指定后面跟有美元符号和该范围的工作表名称。例如: 

    select * from 
    [Sheet1$A1:B10]
    若要引用指定的范围,请使用该范围的名称。例如: 

    select * from 
    [MyNamedRange]

    ref:http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/dv_vbcode/html/vbtskcodeexamplereadingexceldataintodataset.asp
    说明:
    可以引用Excel 工作簿中的三种对象:
    • 整张工作表:[Sheet1$]  ,Sheet1 就是工作表的名称
    • 工作表上的命名单元格区域:[MyNamedRange] (不需要指定工作表,因为整个xls中命名区域只能唯一)
    XLS命名方法:选中单元格范围》插入》名称》定义
    • 工作表上的未命名单元格区域 :[Sheet1$A1:B10]
    (在关系数据库提供的各种对象中(表、视图、存储过程等),Excel 数据源仅提供相当于表的对象,它由指定工作簿中的工作表和定义的命名区域组成。命名区域被视为“表”,而工作表被视为“系统表”)

    注意:
    •必须使用[](方括号),否将报:
    FROM 子句语法错误
    •必须跟$(美元符号),否则报:
    Microsoft Jet 数据库引擎找不到对象'Sheet2'。请确定对象是否存在,并正确地写出它的名称和路径。
    •如果工作表名称不对,或者不存在,将报:
    'Sheet2$' 不是一个有效名称。请确认它不包含无效的字符或标点,且名称不太长。
    •在 如何在 Visual Basic 或 VBA 中使用 ADO 来处理 Excel 数据   中提到可以使用
    ~  和 '(波浪线和单引号)代替[],使用ADO。NET测试没有成功,报:
    FROM 子句语法错误
    •当引用工作表明名([Sheet1$])时,数据提供程序认为数据表从指定工作表上最左上方的非空单元格开始。比如,工作表从第 3 行,C 列开始,第3行,C列之前以及第1、2行全为空,则只会显示从第3行,C列开始的数据;以最后表最大范围内的非空单元结束;
    •因此,如需要精确读取范围,应该使用命名区域 [NamedRange],或者指定地址:[Sheet1$A1:C10]

    4。如何引用列名?
    •根据默认连接字符串中,数据提供程序会将有效区域内的第一行作为列名,如果此行某单元格为空则用F1、F2表示,其中序数,跟单元格的位置一致,从1开始;
    •如果希望第一行作为数据显示,而非列名,可以在连接串的 Extended Properties 属性指定:HDR=NO
    默认值为:HDR=NO 格式如下:

            string connStr = "Provider=Microsoft.Jet.OLEDB.4.0;" +
                            
    "Extended Properties=""Excel 8.0;HDR=NO"";" + 
                            
    "data source=" + xlsPath;

    注意: Excel 8.0;HDR=NO  需要使用双引号(这里的反斜扛,是C#中的转义)

    ref: ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.VisualStudio.v80.chs/WD_ADONET/html/745c5f95-2f02-4674-b378-6d51a7ec2490.htm 中 《连接Excel》节(说明:在我自己的MSDN中,它的例子使用了两个双引号是错的,测试没有通过,原文这样说的:

    注意,Extended Properties 所需的双引号必须还要加双引号。



    在这种情况下,所有的列名都是以F开头,然后跟索引,从F1开始,F2,F3。。。。。。。

    5。为什么有效单元格数据不显示出来?
    出现这种情况的可能原因是,默认连接中,数据提供程序根据前面单元格推断后续单元个的数据类型。
    可以通过 Extended Properties 中指定 IMEX=1

    “IMEX=1;”通知驱动程序始终将“互混”数据列作为文本读取

    ref:同4

    PS:在baidu这个问题的时候,有网友说,将每个单元都加上引号,这固然是格方案,但是工作量何其大啊,又不零活,庆幸自己找到”治本药方“
     
    more ref:
    如何在 Visual Basic 或 VBA 中使用 ADO 来处理 Excel 数据
    http://dotnet.aspx.cc/ShowDetail.aspx?id=C673E2CD-3F1E-4919-8CE0-D69B894A0599

    注意:
    在网上有很多同志说是加上HDR或IMEX这两个属性值后会报“找不到可安装的ISAM ”的错误。这时解决方法很简单:
    就是把连接串写成如下形式:

    Provider=Microsoft.Jet.Oledb.4.0;Data Source={0};Extended Properties='Excel 8.0;HDR=1; IMEX=1;' (注意红色标注的单引号)

    而不要写成这样的形式:

    Provider=Microsoft.Jet.Oledb.4.0;Data Source={0};Extended Properties=Excel 8.0;HDR=1; IMEX=1;



    应用程序经常需要与Excel进行数据交互,在上一篇文章ADO.NET 如何读取 Excel (上)阐 述了基于ADO.NET 读取Excel的基本方法与技巧。今天这里要介绍是如何动态的读取Excel数据,这里的动态指的是事先不知道Excel文件的是什么样的结构,或者无法 预测,比如一张.xls文件有多少张sheet,而且每张sheet的结构可能都不一样等等。
    其实我们可以通过获取Excel的“架构信息”来动态的构造查询语句。这里的“架构信息”与数据库领域的“数据库架构信息”意义相同(也称“元数据”), 对于整个数据库,这些“元数据”通常包括数据库或可通过数据库中的数据源、表和视图得到的目录以及所存在的约束等;而对于数据库中的表,架构信息包括主 键、列和自动编号字段等。
    ADO.NET 如何读取 Excel (上)提到

    在关系数据库提供的各种对象中(表、视图、存储过程等),Excel 数据源仅提供相当于表的对象,它由指定工作簿中的工作表和定义的命名区域组成。命名区域被视为“表”,而工作表被视为“系统表”)


    这里我们将Excel也当作一个“数据库”来对待,然后利用OleDbConnection.GetOleDbSchemaTable 方法
    要获取所需的架构信息,该方法获取的架构信息与ANSI SQl-92是兼容的:

    注意:对于那些不熟悉 OLE DB 架构行集的人而言,它们基本上是由 ANSI SQL-92  定义的数据库构造的标准化架构。每个架构行集具有为指定构造提供定义元数据的一组列(称作 .NET 文档中的“限制列”)。这样,如果请求架构信息(例 如,列的架构信息或排序规则的架构信息),则您会明确知道可以得到哪种类型的数据。如果希望了解更多信息,请访问 Appendix B: Schema Rowsets。

    ref:http://www.microsoft.com/china/msdn/library/office/office/odatanet2.mspx?mfr=true

    以下是读取Excel文件内“表”定义元数据,并显示出来的的程序片断:

            // 读取Excel数据,填充DataSet
            
    // 连接字符串            
            string xlsPath = Server.MapPath("~/app_data/somefile.xls");
            
    string connStr = "Provider=Microsoft.Jet.OLEDB.4.0;" +
                            
    "Extended Properties=""Excel 8.0;HDR=No;IMEX=1"";" + // 指定扩展属性为 Microsoft Excel 8.0 (97) 9.0 (2000) 10.0 (2002),并且第一行作为数据返回,且以文本方式读取
                            "data source=" + xlsPath;
            
    string sql_F = "SELECT * FROM [{0}]";

            OleDbConnection conn 
    = null;
            OleDbDataAdapter da 
    = null;
            DataTable tblSchema 
    = null;
            IList
    <string> tblNames = null;

            
    // 初始化连接,并打开
            conn = new OleDbConnection(connStr);
            conn.Open();

            
    // 获取数据源的表定义元数据                        
            
    //tblSchema = conn.GetSchema("Tables");
            tblSchema = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] nullnullnull"TABLE" });

            GridView1.DataSource 
    = tblSchema;
            GridView1.DataBind();

            
    // 关闭连接
            conn.Close();

    GetOleDbSchemaTable 方法的详细说明可以参考:
    http://msdn2.microsoft.com/zh-CN/library/system.data.oledb.oledbconnection.getoledbschematable.aspx

    接着是一段利用“架构信息”动态读取Excel内部定义的表单或者命名区域的程序片断:

            // 读取Excel数据,填充DataSet
            
    // 连接字符串            
            string xlsPath = Server.MapPath("~/app_data/somefile.xls");
            
    string connStr = "Provider=Microsoft.Jet.OLEDB.4.0;" +
                            
    "Extended Properties=""Excel 8.0;HDR=No;IMEX=1"";" + // 指定扩展属性为 Microsoft Excel 8.0 (97) 9.0 (2000) 10.0 (2002),并且第一行作为数据返回,且以文本方式读取
                            "data source=" + xlsPath;
            
    string sql_F = "SELECT * FROM [{0}]";

            OleDbConnection conn 
    = null;
            OleDbDataAdapter da 
    = null;
            DataTable tblSchema 
    = null;
            IList
    <string> tblNames = null;

            
    // 初始化连接,并打开
            conn = new OleDbConnection(connStr);
            conn.Open();

            
    // 获取数据源的表定义元数据                        
            
    //tblSchema = conn.GetSchema("Tables");
            tblSchema = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] nullnullnull"TABLE" });

            
    //GridView1.DataSource = tblSchema;
            
    //GridView1.DataBind();

            
    // 关闭连接
            
    //conn.Close();

            tblNames 
    = new List<string>();
            
    foreach (DataRow row in tblSchema.Rows) {
                tblNames.Add((
    string)row["TABLE_NAME"]); // 读取表名
            }


            
    // 初始化适配器
            da = new OleDbDataAdapter();
            
    // 准备数据,导入DataSet
            DataSet ds = new DataSet();

            
    foreach (string tblName in tblNames) {
                da.SelectCommand 
    = new OleDbCommand(String.Format(sql_F, tblName), conn);
                
    try {
                    da.Fill(ds, tblName);
                }

                
    catch {
                    
    // 关闭连接
                    if (conn.State == ConnectionState.Open) {
                        conn.Close();
                    }

                    
    throw;
                }

            }


            
    // 关闭连接
            if (conn.State == ConnectionState.Open) {
                conn.Close();
            }


            
    // 对导入DataSet的每张sheet进行处理        
            
    // 这里仅做显示
            GridView1.DataSource = ds.Tables[0];
            GridView1.DataBind();

            GridView2.DataSource 
    = ds.Tables[1];
            GridView2.DataBind();

            
    // more codes
            
    // .

    这里我们就不需要对SELEC 语句进行“硬编码”,可以根据需要动态的构造FROM 字句的“表名”。

    不仅可以,获取表明,还可以获取每张表内的字段名、字段类型等信息:

    tblSchema = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Columns, new object[] nullnullnullnull });


    在ADO.nET 1.x 时候只有OleDb提供了GetOleDbSchemaTable 方法,而SqlClient或者OrcaleClient没有对应的方法,因为对应数据库已经提供了类似功能的存储过程或者系统表供应用程序访问,比如对 于Sql Server:

    SELECT *
    FROM Northwind.INFORMATION_SCHEMA.COLUMNS
    WHERE TABLE_NAME = N'Customers'


    而在ADO.NET 2.0中每个xxxConnenction都实现了基类System.Data.Common.DbConnection的 GetSchemal 方法
    来获取数据源的架构信息。

  • sqlserver连接oracle

     一:sqlserver连接oracle  
      1.配置windows的ODBC数据源:    
      开始菜单—》设置—》管理工具—》数据源(ODBC)—》进入配置用户DSN或者系统DSN均可以:  
      添加—》选择Microsoft   ODBC   for   oracle—》自定义数据源名称(最好跟tns中连接串同名!)—》服务器名称  
      (必填!填写tns文件中的连接串名称)—》完成。    
       
      2.配置sqlserver2000中的连接服务器:    
      企业管理器—》安全性—》连接服务器—》右键新建连接服务器—》定义连接名称;  
      选其他数据源;   指定程序名称为:Microsoft   OLE   DB   Provider   for   Oracle;   产品名称可不填;    
      数据源指定刚才ODBC中定义好的数据源名称;   提供程序字符串按以下格式填写:User   ID=username;Password=userpasswd  
      (或者按如下格式:UID=username;PWD=userpasswd),  
      这里的用户名和密码对应所要连接的oracle数据库中的用户名和密码   —》   安全性标签页里:  
      设置用此安全上下文进行,并录入oracle的数据库用户名和密码—》服务器选项标签页可默认—》确定。    
       
      3.完成了。使用方法  
      在sqlserver企业管理器—》安全性—》连接服务器打开刚建好的连接服务器—》点击表,  
      即可在右边窗口看到该oracle数据库用户拥有的的所有表名,但在这里还并不能查看表的记录,这个需要在sqserver的查询分析器中用具体sql实现!  
      访问表时,使用格式为:   [连接服务器名]..[ORACLE用户].[表名]。必须为大写。更详细具体的使用这里不再赘述。  
      ------------------------------------------------------------------------------------------  
      二:oracle连接sqlserver  
      1、安转透明网关  
      在自定义安转内  
      D:\oracle\ora92\tg4msql\admin\inittg4msql.ora       tg4msql  
      2、在D:\oracle\ora92\network\admin\listener.ora内添加  
        (SID_DESC   =  
                  (GLOBAL_DBNAME   =   tg4msql)    
                    (PROGRAM   =   tg4msql)  
                  (ORACLE_HOME   =   D:\oracle\ora92)  
                  (SID_NAME   =   tg4msql)  
              )  
      3、配置tns  
      tnsnames.ora  
      -----------------  
      例子1  
      cdma   =  
          (DESCRIPTION   =  
              (ADDRESS_LIST   =  
                  (ADDRESS   =   (PROTOCOL   =   TCP)(HOST   =   zhyg)(PORT   =   1521))  
              )  
              (CONNECT_DATA   =  
                  (SID   =   tg4msql)  
                  (SERVER   =   DEDICATED)  
              )  
              (HS=OK)  
          )  
      例子2  
      du   =  
          (DESCRIPTION   =  
              (ADDRESS_LIST   =  
                  (ADDRESS   =   (PROTOCOL   =   TCP)(HOST   =   sha38)(PORT   =   1521))  
              )  
              (CONNECT_DATA   =  
                  (SID   =   jf)  
                  (SERVER   =   DEDICATED)  
              )  
              (HS=OK)  
          )  
      --------  
      3、创建数据链路      
      CREATE   PUBLIC   DATABASE   LINK   DU   CONNECT   TO   SA   IDENTIFIED   BY   SA   USING   'DU'  
      ok可以使用了  
      select   *   from   sysobjects@cdma  

  • 解析oracle的ROWNUM

    对于rownum来说它是oracle系统顺序分配为从查询返回的行的编号,返回的第一行分配的是1,第二行是2,依此类推,这个伪字段可以用于限制查询返回的总行数,而且rownum不能以任何表的名称作为前缀。
     举例说明:
    例如表:student(学生)表,表结构为:
    ID       char(6)      --学号
    name    VARCHAR2(10)   --姓名
    create table student (ID char(6), name VARCHAR2(100));
    insert into sale values('200001',‘张一’);
    insert into sale values('200002',‘王二’);
    insert into sale values('200003',‘李三’);
    insert into sale values('200004',‘赵四’);
    commit;

    (1) rownum 对于等于某值的查询条件
    如 果希望找到学生表中第一条学生的信息,可以使用rownum=1作为条件。但是想找到学生表中第二条学生的信息,使用rownum=2结果查不到数据。因 为rownum都是从1开始,但是1以上的自然数在rownum做等于判断是时认为都是false条件,所以无法查到rownum = n(n>1的自然数)。
    SQL> select rownum,id,name from student where rownum=1;(可以用在限制返回记录条数的地方,保证不出错,如:隐式游标)
    SQL> select rownum,id,name from student where rownum=1;
        ROWNUM ID     NAME
    ---------- ------ ---------------------------------------------------
             1 200001 张一
    SQL> select rownum,id,name from student where rownum =2;
        ROWNUM ID     NAME
    ---------- ------ ---------------------------------------------------

    (2)rownum对于大于某值的查询条件
       如果想找到从第二行记录以后的记录,当使用rownum>2是查不出记录的,原因是由于rownum是一个总是从1开始的伪列,Oracle 认为rownum> n(n>1的自然数)这种条件依旧不成立,所以查不到记录
    SQL> select rownum,id,name from student where rownum >2;
    ROWNUM ID     NAME
    ---------- ------ ---------------------------------------------------
    那如何才能找到第二行以后的记录呀。可以使用以下的子查询方法来解决。注意子查询中的rownum必须要有别名,否则还是不会查出记录来,这是因为rownum不是某个表的列,如果不起别名的话,无法知道rownum是子查询的列还是主查询的列。
    SQL>select * from(select rownum no ,id,name from student) where no>2;
            NO ID     NAME
    ---------- ------ ---------------------------------------------------
             3 200003 李三
             4 200004 赵四
    SQL> select * from(select rownum,id,name from student)where rownum>2;
        ROWNUM ID     NAME
    ---------- ------ ---------------------------------------------------

    (3)rownum对于小于某值的查询条件
    如果想找到第三条记录以前的记录,当使用rownum<3是能得到两条记录的。显然rownum对于rownum<n((n>1的自然数)的条件认为是成立的,所以可以找到记录。
    SQL> select rownum,id,name from student where rownum <3;
        ROWNUM ID     NAME
    ---------- ------ ---------------------------------------------------
            1 200001 张一
            2 200002 王二
    综 上几种情况,可能有时候需要查询rownum在某区间的数据,那怎么办呀从上可以看出rownum对小于某值的查询条件是人为true的,rownum对 于大于某值的查询条件直接认为是false的,但是可以间接的让它转为认为是true的。那就必须使用子查询。例如要查询rownum在第二行到第三行之 间的数据,包括第二行和第三行数据,那么我们只能写以下语句,先让它返回小于等于三的记录行,然后在主查询中判断新的rownum的别名列大于等于二的记 录行。但是这样的操作会在大数据集中影响速度。
    SQL> select * from (select rownum no,id,name from student where rownum<=3 ) where no >=2;
            NO ID     NAME
    ---------- ------ ---------------------------------------------------
             2 200002 王二
             3 200003 李三

    (4)rownum和排序
    Oracle中的rownum的是在取数据的时候产生的序号,所以想对指定排序的数据去指定的rowmun行数据就必须注意了。
    SQL> select rownum ,id,name from student order by name;
        ROWNUM ID     NAME
    ---------- ------ ---------------------------------------------------
             3 200003 李三
             2 200002 王二
             1 200001 张一
             4 200004 赵四
    可以看出,rownum并不是按照name列来生成的序号。系统是按照记录插入时的顺序给记录排的号,rowid也是顺序分配的。为了解决这个问题,必须使用子查询
    SQL> select rownum ,id,name from (select * from student order by name);
        ROWNUM ID     NAME
    ---------- ------ ---------------------------------------------------
             1 200003 李三
             2 200002 王二
             3 200001 张一
             4 200004 赵四
    这样就成了按name排序,并且用rownum标出正确序号(有小到大)
    笔者在工作中有一上百万条记录的表,在jsp页面中需对该表进行分页显示, 便考虑用rownum来作,下面是具体方法(每页
    显示20条):
      “select * from tabname where rownum<20 order by name" 但却发现oracle却不能按自己的意愿来执行,而是先随便
    取20条记录,然后再 order by,后经咨询oracle,说rownum确实就这样,想用的话,只能用子查询 来实现先排序,后
    rownum,方法如下:
      "select * from (select * from tabname order by name) where  rownum<20",但这样一来,效率会较低很多。
      后经笔者试验,只需在order by 的字段上加主键或索引即可让oracle先按 该字段排序,然后再rownum;方法不变:
       “select * from tabname where rownum<20 order by name"

    取得某列中第N大的行

    select column_name from
    (select table_name.*,dense_rank() over (order by column desc) rank from table_name)
    where rank = &N;
     假如要返回前5条记录:

      select * from tablename where rownum<6;(或是rownum <= 5 或是rownum != 6)
    假如要返回第5-9条记录:

    select * from tablename
    where …
    and rownum<10
    minus
    select * from tablename
    where …
    and rownum<5
    order by name
    选出结果后用name排序显示结果。(先选再排序)

    注意:只能用以上符号(<、<=、!=)。

    select * from tablename where rownum != 10;返回的是前9条记录。
    不能用:>,>=,=,Between...and。由于rownum是一个总是从1开始的伪列,Oracle 认为这种条件 不成立,查不到记录.

    另外,这个方法更快:

    select * from (
    select rownum r,a from yourtable
    where rownum <= 20
    order by name )
    where r > 10
    这样取出第11-20条记录!(先选再排序再选)

    要先排序再选则须用select嵌套:内层排序外层选。
    rownum是随着结果集生成的,一旦生成,就不会变化了;同时,生成的结果是依次递加的,没有1就永远不会有2!
    rownum 是在 查询集合产生的过程中产生的伪列,并且如果where条件中存在 rownum 条件的话,则:

    1: 假如 判定条件是常量,则:
    只能 rownum = 1, <= 大于1 的自然数, = 大于1 的数是没有结果的, 大于一个数也是没有结果的
    即 当出现一个 rownum 不满足条件的时候则 查询结束   this is stop key!

    2: 当判定值不是常量的时候
    若条件是 = var , 则只有当 var 为1 的时候才满足条件,这个时候不存在 stop key ,必须进行 full scan ,对每个满足其他where条件的数据进行判定
    选出一行后才能去选rownum=2的行…… 

    rowid 与 rownum 虽都被称为伪列,但它们的存在方式是不一样的,rowid 可以说是物理存在的,表示记录在表空间中的唯一位置ID,在DB中唯一。只要记录没被搬动过,rowid是不变的。rowid 相对于表来说又像表中的一般列,所以以 rowid 为条件就不会有 rownum那些情况发生。
    另外还要注意:rownum不能以任何基表的名称作为前缀。

    rowid和rownum不同,一般说来每一行数据对应的rowid是固定而且唯一的,在这一行数据存入数据库的 时候就确定了。可以利用rowid来查询记录,而且通过rowid查询记录是查询速度最快的查询方法。(这个我没有试过,另外要记住一个长度在18位,而 且没有太明显规律的字符串是一个很困难的事情,所以我个人认为利用rowid查询记录的实用性不是很大)rowid只有在表发生移动(比如表空间变化,数 据导入/导出以后),才会发生变化。

  • SimpleTool 之 SimpleSnapper

     简单实用的截图工具,敬请关注...

    请先安装.net framework 2.0 ...

    Posted Oct 13 2008, 01:44 PM by slash with no comments
    Filed under:
  • Win32 API讲座

    第一课∶认识API
    一、什么是API
    首先,有必要向大家讲一讲,什么是API。所谓API本来是为C和C++程序员写的。API
    说来说去,就是一种函数,他们包含在一个附加名为DLL的动态连接库文件中。用标准的定义来讲,API就是Windows的32位应用程序编程接口,是一系列很复杂的函数,消息和结构,它使编程人员可以用不同类型的编程语言编制出的运行在Windows95和Windows NT
    操作系统上的应用程序。可以说,如果你曾经学过VC,那么API对你来说不是什么问题。但是如果你没有学过VC,或者你对Windows95的结构体系不熟悉,那么可以说,学习API
    将是一件很辛苦的事情。
    如果你打开WINDOWS的SYSTEM文件夹,你可以发现其中有很多附加名为DLL的文件。一个DLL中包含的API函数并不只是一个,数十个,甚至是数百个。我们能都掌握它嘛?回答是否定的∶不可能掌握。但实际上,我们真的没必要都掌握,只要重点掌握Windos系统本身自带的API函数就可以了。但,在其中还应当抛开掉同VB本身自有的函数重复的函数。如,VB的etAttr命令可以获得文件属性,SetAttr可以设置文件属性。对API来讲也有对应的函数GetFileAttributes和SetFileAttributes,性能都差不多。如此地一算,剩下来的也就5、600
    个。是的,也不少。但,我可以敢跟你说,只要你熟悉地掌握100个,那么你的编程水平比现在高出至少要两倍。尽管人们说VB和WINDOWS具有密切的关系,但我认为,API更接近WINDOWS。如果你学会了API,首要的收获便是对WINDOWS体系结构的认识。这个收获是来自不易的。
    如果你不依靠API会怎么样?我可以跟你说,绝大多是高级编程书本(当然这不是书的名程叫高级而高级的,而是在一开始的《本书内容》中指明《本书的阅读对象是具有一定VB
    基础的读者》的那些书),首先提的问题一般大都是从API开始。因此可以说,你不学API,你大概将停留在初级水平,无法往上攀登。唯一的途径也许就是向别人求救∶我快死了,快来救救我呀,这个怎么办,那个怎么办?烦不烦呢?当然,现在网上好人太多(包括我在内,嘻嘻),但,你应当明白,通过此途径,你的手中出不了好的作品。这是因为缺乏这些知识你的脑子里根本行不成一种总体的设计构思。
    二、API文本游览器。
    很多API函数都是很长很长的。想看什么样子吗?如下就是作为例子的API DdeClientTransaction函数∶
    Declare Function DdeClientTransaction Lib "user32" (pData As Byte, ByVal cbData As Long, ByVal hConv As Long, ByVal hszItem As Long, ByVal wFmt As Long, ByVal wType As Long, ByVal dwTimeout As Long, pdwResult As Long) As Long
    哇!这么长?如果你从来没有接触过API,我想你肯定被吓住了。你也许考虑,该不该继续学下去。不过不要担心,幸运的是Microsoft的设计家们为我们提供了有用的工具,这便是API
    文本查看器。
    通过API文本查看器,我们可以方便地查找程序所需要的函数声明、结构类型和常数,然后将它复制到剪贴板,最后再粘贴到VB程序的代码段中。在大多数情况下,只要我们确定了程序所需要的函数、结构和常数这三个方面后,就可以通过对API文本游览器的以上操作将他们加入到程序段中,从而程序中可以使用这些函数了。这些是学习API最基本的常识问题,它远远占不到API的庞大的体系内容。今后我们把精力浪费(这绝不是浪费)在哪里呢?那就是∶
    什么时候使用什么函数,什么时候使用什么结构类型,什么时候使用什么常数。
    三、API函数声明。
    让我们回想一下。在VB中,如何声明函数呢?我想,如果你正在看此文,那么你绝对能够回答得出这个问题。以下便是你应该很熟悉的函数声明∶
    Function SetFocus (ByVal hwnd As Long) As Long
    即,这行代码定义了名为SetFocus的函数,此函数具有一个Long型数据类型的参数,并按值传递(ByVal),函数执行后将返回一个Long型数据。
    API函数的声明也很类似,如,API中的SetFocus 函数是这样写的∶
    Declare Function SetFocus Lib "user32" Alias "SetFocus" (ByVal hwnd As Long) As Long
    有点复杂了一些。是的,是复杂了点。但我可以告诉你,除了这些多出来的部分,其他部分还是和你以前学到的东西是一样的。函数在程序中的调用也是一样。如:
    Dim dl As Long
    dl&=SetFoucs(Form1.Hwnd)
    但,一点是清楚的。它不象你自己写的程序那样能够看到里面的运行机理,也不像VB
    自带的函数那样,能够从VB的联机帮助中查到其用法。唯一的方法就是去学、查VB以外的资料。
    Declare 语句用于在模块级别中声明对动态链接库 (DLL) 中外部过程的引用。对此,你只要记住任何API函数声明都必须写这个语句就可以了。
    Iib 指明包含所声明过程或函数的动态链接库或代码资源。也就是说,它说明的是,函数或过程从何而来的问题。
    如在上例中,SetFocus Lib "user32"说明 函数 SetFocus 来自 user32.dll文件。主要的dll动态连接库文件有∶
    user32.dllWindows管理。生成和管理应用程序的用户接口。
    GDI32.dll图形设备接口。产生Windows设备的图形输出
    Kernel32.dll系统服务。访问操作系统的计算机资源。
    注意,当DLL文件不在Windows或System文件夹中的时候,必须在函数中说明其出处(
    路径)。如,SetFocus Lib "c:Mydlluser32"
    函数声明中的Alias 是可选的。表示将被调用的过程在动态链接库 (DLL) 中还有另外的名称(别名)。如,Alias "SetFocus" ,说明SetFocus函数在User32.dll中的另外一个名称是,
    SetFocus。怎么两个名都一样呢?当然,也可以是不同的。在很多情况下,Alias说明的函数名,即别名最后一个字符经常是字符A,如SetWindowsText函数的另一个名称是
    SetWindowsTextA,表示为Alias "SetWindowsTextA"。这个A只不过是设计家们的习惯的命名约定,表示函数属于ANSI版本。
    那么,别名究竟有什么用途呢?从理论上讲,别名提供了用另一个名子调用API的函数方法。如果你指明了别名,那么 尽管我们按Declare语句后面的函数来调用该函数,但在函数的实际调用上是以别名作为首要选择的。如,以下两个函数(Function,ABCD)声明都是有效的,他们调用的是同一个 SetFocus函数∶
    Declare Function SetFocus Lib "user32" "SetFocus" (ByVal hwnd As Long) As Long
    Declare ABCD SetFocus Lib "user32" Alias "SetFocus" (ByVal hwnd As Long) As Long
    需要注意的是,选用Alias的时候,应注意别名的大小写;如果不选用Alias 时的时候,函数名必须注意大小写,而且不能改动。当然,在很多情况下,由于函数声明是直接从API
    文本游览器中拷贝过来的,所以这种错误的发生机会是很少的,但您有必要知道这一点。
    最后提醒你一句,API声明(包括结构、常数)必须放在窗体或模块的"通用(General Declarations)段。
    (杭州元帅注:在窗体里的API声明必须 Private Declare Function ......)
    四、数据类型与"类型安全"
    API函数中使用的数据类型基本上和VB中的一样。但作为WIN32的API函数中,不存在Integer
    数据类型。另外一点是在API函数中看不到Boolean数据类型。 Variant数据类型在API函数中是以Any的形式出现,如Data As Any。尽管其含义是允许任意参数类型作为一个该API函数的参数传递,但这样做存在一定的缺点。其原因是,这将会使得对目标参数的所有类型检查都会被关闭。这自然会给各种类型的参数调用带来了产生错误的机会。
    为了强制执行严格的类型检查,并避免上面提到的问题,一个办法是在函数里使用上面提到到Alias技术。如对API函数 GetDIBits 可进行另外一种声明方法。如下∶
    GetDIBits函数的原型∶
    Public Declare Function GetDIBits Lib "gdi32" Alias "GetDIBits" (ByVal aHDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpBI As BITMAPINFO, ByVal wUsage As Long) As Long
    GetDIBits函数的改型∶
    Public Declare Function GetDIBitsLong Lib "gdi32" Alias "GetDIBits" (ByVal aHDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Long, lpBI As BITMAPINFO, ByVal wUsage As Long) As Long
    通过本课程前面所学到的知识,我们已经可以得知原型 GetDIBits函数也好,改型 GetDIBitsLong函数也好,实际将调用的都是Alias所指定的 GetDIBits原函数。但你应当看到,两者的区别在于,我们在改型的函数中强制指定lpBits参数为Long形。这样就会使得函数调用中发生的错误机率减少到了最小。这种方法叫做"安全类型"声明。
    API函数中经常看到的数据类型有∶Long,String,Byte,Any....(也就这些吧。)
    五、常数
    对于API常数来讲,没有什么太特别的学问。请看VB中的以下代码∶
    Msg = MsgBox("您好", vbOKCancel)
    我们知道, vbOKCancel这个常数的值等于1。对上面的代码我们完全可以这样写,而不会影响代码的功能∶
    Msg = MsgBox("您好", 1)
    但你大概不太愿意选择后一种,因为这会使得看懂代码费劲起来。这种方法也被API采取了。只是API常数必须在事情之前做好初始化声明VB本身是看不懂的。其内容仍然来自与API
    文本游览器。具体形式如下等等∶
    Public Const ABM_ACTIVATE = &H6
    Public Const RIGHT_CTRL_PRESSED = &H4
    Public Const RPC_E_SERVER_DIED = &H80010007
    Private Const RPC_S_CALL_FAILED_DNE = 1727&
    在常数的初始化中,有些程序使用Global,如Global Const ABM_ACTIVATE = &H6,但我认为Public完全可以代替它。过去我也用过Global,但现在不大用了。一会儿用这个,一会儿用那个,各程序之间不能保持一致性了,起码看起来别扭。
    六、结构
    结构是C和C++语言中的说法。在VB中一般称为自定义数据类型。想必很多朋友都已经认识它。在API领域里,我更喜欢把它叫做结构,因为API各种结构类型根本不是我定义(
    自定义)的。
    在VB中,API结构同样由TYPE.......END TYPE语句来定义。如,在API中,点(Point)结构的定义方法如下:
    Public Type POINTAPI
    X As Long '点在X坐标(横坐标)上的坐标值
    Y As Long '点在Y坐标(纵坐标)上的坐标值
    End Type
    又如,API中矩形(Rect)结构的定义如下∶
    Public Type RECT
    Left As Long '矩形左上角的X坐标
    Top As Long '矩形左上角的Y坐标
    Right As Long '矩形右下角的X坐标
    Bottom As Long '矩形右下角的Y坐标
    End Type
    这些内容同样可以从API文本游览器中拷贝过来。这些结构中的变量名可随意改动,而不会影响结构本身。也就是说,这些成员变量都是虚拟的。如,POINTAPI结构可改为如下∶
    Public Type POINTAPI
    MyX As Long '点在X坐标(横坐标)上的坐标值
    MyY As Long '点在Y坐标(纵坐标)上的坐标值
    End Type
    不过,一般来讲,是没有这种必要的。结构本身是一种数据类型,因此,使用时必须声明具体变量为该结构型,才能在程序中真正使用到该结构。结构的声明方法和其他数据的声明方法一样,如,以下语句把变MyPoint声明为POINTAPI结构类型∶
    MyPoint As POINTAPI
    引用结构中的成员变量也十分简单,在结构名后面加上一个".",然后紧接着写要引用的成员变量即可。这很象VB中的引用一个对象的某个属性。如,假如我们把上面已经声明的MyPoint结构中的X变量的值赋给变量Temp&
    则代码如下∶
    Temp&=MyPoint.X
    但,特别注意的是,你千万不要认为上例中的MyPoint是一个值。它不是值,而是地址(
    指针)。值和地址是完全不同的概念。结构要求按引用传递给WINDOWS函数,即所有API
    函数中,结构都是按ByRef传递的(在Declare语句 中ByRef是默认型)。对于结构的传递,你不要试图采用ByVal,你将一无所获。由于结构名实际上就是指向这个结构的指针(这个结构的首地址),所以,你也就传送特定的结构名就可以了(参见小结,我用红色字体来突出了这种传递方式)。
    由于结构传送的是指针,所以函数将直接对结构进行读写操作。这种特性很适合于把函数执行的结果装载在结构之中。
    七、小结
    以下的程序是为了总结本课中学到的内容而给出的。启动VB,新建一个项目,添加一个命令按钮,并把下面的代码拷贝到代码段中,运行它。
    Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
    Private Type POINTAPI '定义点(Point)结构
    X As Long '点在X坐标(横坐标)上的坐标值
    Y As Long '点在Y坐标(纵坐标)上的坐标值
    End Type
    Sub PrintCursorPos( )
    Dim dl AS Long
    Dim MyPoint As POINTAPI
    dl&= GetCursorPos(MyPoint) '调用函数,获取屏幕鼠标坐标
    Debug.Print "X=" & Str(MyPoint.X) & " and " & "Y=" & Str(MyPoint.Y)
    End Sub
    Private Sub Command1_Click()
    PrintCursorPos
    End Sub
    输出结果为(每次运行都可能得到不同的结果,这得由函数调用时鼠标指针在屏幕中所处的位置而决定)∶
    X= 240 and Y= 151
    程序中,GetCursorPos函数用来获取鼠标指针在屏幕上的位置。
    以上例子中,你可以发现,以参数传递的MyPpint结构的内容在函数调用后发生了实质性变化。这是由于结构是按ByRef传递的原因。
     
    第二课∶句柄、矩形和画点函数


    一、句柄


    今天开始,我向大家讲有关API的是实质性内容。我们就从"句柄"开始。
    只要你来到了API的世界,经常碰到的问题之一就是句柄。那么究竟什么是句柄呢?
    如果你从来都没有听说过"句柄"这个词,可能首先觉得句柄当中有很多内容。其实不然,所谓句柄实际上是一个数据,是一个Long (整长型)的数据。在API中,它经常是以一个参数的形式传递给各种API函数。如:
    Public Declare Function GetWindow& Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long)


    其中,hwnd就是句柄。在VB里,句柄是一种属性,您打开VB中的对象游览器看一看Form
    窗体或者PictureBox控件等究竟有没有hwnd属性。是有的。VB中的解释是这样的∶
    Microsoft Windows 运行环境,通过给应用程序中的每个窗体和控件分配一个句柄(或 hWnd)来标识它们。hWnd 属性用于Windows API调用。许多 Windows 运行环境函数需要活动窗口的 hWnd 作为参数。


    如果想更透彻一点地认识句柄,我可以告诉大家,句柄是一种指向指针的指针。我们知道,所谓指针是一种内存地址。应用程序启动后,组成这个程序的各对象是住留在内的。如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访问对象。但是,如果您真的这样认为,那么您就大错特错了。我们知道,Windows是一个以虚拟内存为基础的操作系统。在这种系统环境下,Windows内存管理器经常在内存中来回移动对象,依此来满足各种应用程序的内存需要。对象被移动意味着它的地址变化了。如果地址总是如此变化,我们该到哪里去找该对象呢?


    为了解决这个问题,Windows操作系统为各应用程序腾出一些内存储地址,用来专门登记各应用对象在内存中的地址变化,而这个地址(存储单元的位置)本身是不变的。Windows
    内存管理器在移动对象在内存中的位置后,把对象新的地址告知这个句柄地址来保存。这样我们只需记住这个句柄地址就可以间接地知道对象具体在内存中的哪个位置。这个地址是在对象装载(Load)时由系统分配给的,当系统卸载时(Unload)又释放给系统。
    句柄地址(稳定)→记载着对象在内存中的地址────→对象在内存中的地址(不稳定)→
    实际对象


    但是,必须注意的是程序每次从新启动,系统不能保证分配给这个程序的句柄还是原来的那个句柄,而且绝大多数情况的确不一样的。假如我们把进入电影院看电影看成是一个应用程序的启动运行,那么系统给应用程序分配的句柄总是不一样,这和每次电影院售给我们的门票总是不同的一个座位是一样的道理。
    在VB中获得一个对象的句柄十分简单,如要获取Form1窗体的句柄,就可以这样写∶
    Form1.Hwnd


    对象的句柄还可以通过API函数来获得,如∶


    GetActiveWindow返回位于最顶部的具有输入焦点的窗口句柄
    GetFocus获得当前线程里补获鼠标输入的窗口句柄
    GetForegroundWindow从位于前台的线程里返回活动窗口的句柄
    GetCursor取得当前指针的句柄
    GetDesktopWindow获取整个桌面的句柄
    GetWindow获得一个窗口的句柄,该窗口与某源窗口有特定的关系
    《以上函数说明均可在WinAPI.hlp文件中找到。》


    本教程提供了演示例程──play1.vbp,正是为了说明这些函数的具体用法的。
    程序运行后,用鼠标做一些任何你想做的事情,并观察各项目数据的变化。
    通过本程序,注意观察以下几点∶
    1,线程内与线程外。(VB不支持多线程)。其他应用程序对此程序来说都是线程外。
    2,在windows95操作系统下,各个窗体(包括一些控件,如文本框,图片框等,
    MICROSORT对它们均统称为窗体)拥有各自的鼠标指针。这和win16下各应用程序使用同样一个指针是截然不同的。


    3,每次从新启动,各窗体的句柄都有所变化。Text5 的装载和卸载过程中,句柄始终是在变化着的。这说明了上面提的影院售门票中存在的现象是真实的。
    获得对象句柄的函数还有很多,以后碰到它们时再介绍给大家。


    二、驾驶句柄


    只要弄清了什么是句柄,尤其是窗口句柄,那么操纵一个对象就变得自如一多了。比如,可以通过GetWindowText API函数,我们可以轻松地获得某特定窗口的标题。
    GetWindowText 在VB中的声明如下:
    Private Declare Function GetWindowText& Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long)
    于是,我们可以通过以下一段代码来获得Form1的窗口标题。(新建一个项目,添加一个命令按钮,把以下的代码拷贝过去,还有上面的函数声明。你就可以运行了)


    Private Sub Command1_Click()
    Dim dl As Long
    Dim FormCaption As String
    FormCaption = Space(128)
    dl& = GetWindowText(Form1.hwnd, FormCaption, 128)
    Print FormCaption
    End Sub
    注∶其中128是指字符串变量FormCaption的长度(又称缓冲区大小)


    这有必要吗?为了获得Form1窗体的标题,何必写这么多代码呢?难道这就是API。是的,的确在VB中用 Print Form1.Caption 一行代码就可以抵挡住以上代码了。但是,假如我们启动我们设计的应用程序后,想要在用鼠标点一下别的应用程序的时候,让我们的应用程序显示出那个窗体的标题,那又该怎么办呢?比方说,我们另外启动的是Micorsoft Word,
    用鼠标点击Word时让程序显示出"您选择了Microsoft Word"字样。显然只靠VB是办不到的,还得靠API这老手。


    当然,您已经具备了这种能力,可以办到这件事情。让我们一起来。
    关键的问题是如何获得Word程序的句柄。首先要认清的是,对VB的应用程序来说,Word
    是属于进程外应用程序。正和您已经想到的那样,我们可以使用前一个示范程序用到过的
    GetForegroundWindow API函数获取它的句柄。因为,当我们用鼠标点击Word时,Word
    将成为前台活动窗体。接着呢?当然是 GetWindowText函数显示它的标题就可以了。我们可以采用Timer控件来完成这一切。剩下的事情就不用我多说了。本教程附带的
    Program2.vbp程序是,为那些懒得由自己动手写这几行代码的人准备的,但愿您不是。如果你还没有写过API应用程序,可以说这是一个好的机会,还是动一动你的手吧,会有好处的。


    程序正常运行了没有?哈,这下感觉到API的魅力了吧? 还想不想继续学下去呀!


    三、矩形和点


    关于矩形和点,我们在上一个课堂中简单提了一下。在这里,就做一下详细的介绍。
    先从简单点(Point)结构的开始。点的结构如下∶
    Type POINTAPI
    x as Long
    y as Long
    End Type
    在英语里,点叫Point,那么为什么不叫POINT,而叫POINTAPI呢?原来,VB中有Point
    方法,这只是为了不必要的冲突或重名。Point用于描述一个位置,当然是一个点的位置了。在屏幕坐标中,x指的是从屏幕左边界到指定点的距离,y指的是从屏幕顶边界到指定点的距离。初次之外,没有太多的学问了,还是那句老话──牢记按引用传递。


    矩形的结构和点结构差不到哪儿去,只不过是用两个点来描述的。它的结构定义如下
    Public Type RECT


    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
    End Type
    以下图描述了RECT结构各字段的含义。

    可以看出,一个矩形区域是通过矩形的左上角的一个点和右下角的一个点,共两个点来描述的。其中,left和top字段描述了巨型的第一个角的位置,right和bottom字段描述了矩形的第二个角即右下角的位置。
    在VB中,描述一个窗体或控件所处的矩形位置时,经常用Left,top以及Width,height来描述的。其中,Width是一个巨型区域的宽度,Height是高度。在此,您应当看到,RECT结构中并不是这样。如,想要获得宽度,必须从right中减掉left。


    API中有若干个函数用来处理矩形数据结构,如,下表所列∶


    函 数 功 能
    EquaRect 判断两个矩形是否相同,如果相同则返回True(非0)
    InflateRect 增加或减少矩形的尺寸
    InterSectRect获得两个矩形相重叠的部分,即一个新的矩形
    IsRectEmpty 如矩形为空,则返回TRUE
    OffsetRect 按规定的偏移量移动一个矩形的位置
    ptInRect 判断一个指定点是否位于给出的矩形的内部
    SetRect 设置巨型的参数字段值
    SetRectEmpty将所有字段都设为0,从而将矩形置空
    SubtractRect 将一个矩形从另一个巨型里减去。"即切掉"
    UnionRect 获得同时包含两个矩形区域的最小矩形。
    CopyRect将一个矩形的内容复制给另一个
    这几个函数,都是很好理解和实际应用的。Program3.vbp示范程序是,为帮助大家了解这些函数的具体使用方法而设计的。此程序应用了以上函数列表中的多数函数。在阅读程序原代码时,请注意理解vbNotXorPen绘图模式的应用。
     
    第三课∶窗口函数


    一、 关于窗口函数


    在上一堂课里,我们已经提出了"句柄"的概念,并为此进行了较深度的讨论。现在来想,我要补充的是,句柄并非是仅仅是窗口才有的。似乎所有的WINDOWS对象都具有句柄。如,GDI对象中的画笔、刷子等,不久即将要学习的设备场景等也有自己的句柄,等等。但,和一些控件不同,这些对象并不属于窗口。
    什么是窗口呢?有一句非常有趣的话∶如果它位于屏幕,那么它肯定是在一个窗口里;如果它不在于屏幕,它仍然可能在一个窗口里。窗体也是窗口;滚动条、列表框,文本框,甚至是桌面上的快捷图表也是窗口。更有趣的是,就连作为背景的桌面也是窗口。


    很多控件基本上都提供了hWnd属性,但没有提供的也有。对于这些控件可以用SetFocus
    方法,将输入焦点设向控件,然后用API函数GetFocus取得当前具有焦点的那个窗口的句柄。当然,这一过程应当写在GoFocus事件中。在我碰到过的问题中有一个有趣的事情是,
    VB提供的IE控件的hWnd属性不管用。这个问题我一般都采用上述方法来解决的。
    很多窗口函数都能对系统的任何窗体进行操作。这意味了VB程序可以直接操纵正在运行中的其他窗体。大家知道,如果对VB设计出的程序未做特殊的处理,那么我们可以启动多个该应用程序实例。我们可以利用API窗口函数来判断一个窗体的先例是否在运行当中,从而可以做到如果有先例则停止启动。很多应用程序就是这个样子的。比如四通利方中文平台,在已经启动的情况下再此启动,程序会告诉用户"四通利方已经在运行",并停止启动。


    窗口函数主要可分为四个类型(也许说为"这是为了这次讲课分类出来的"更适合一些)∶
    1、窗口分级函数;
    2、窗口位置与大小函数;
    3、窗口信息函数;
    4、其他窗口函数。
    以下我们就一一讲述。但由于窗口函数比较多,在这里就选择性的进行讨论。关于窗口函数有多少,具体的用法如何,您可以注意"小雁侠"的VB API站点的技术文档,或者本站
    程序下载栏目中的WinAPI帮助文。由于帮助文其内容来自"小雁侠"的网站,因此其内容更新比较起来会较晚一些。


    二、窗口分级函数


    系统中运行的窗口是有级别的高低之分的。谁不知道这样?这当然是废话。很多文章都是采用类似的这种废话来做导语,在这里我只不过也是学学而罢。
    一、父子关系。
    每个窗口都可能有自己的父窗口和子窗口。但,系统中运行的窗口是有限的,说明总得有个窗口是没有其父,我们把它叫做顶级窗口。一般把一个应用程序的主窗口就是顶级窗口,VB独立窗体及MDI窗体都是顶级窗口。窗口间的父子关系一般遵循以下规则∶
    1、父窗口显示时,所有包容在其中的可见的子窗口会随着父窗口的显示而显示出来。


    2、父窗口隐藏时,所有包容在其中的子窗口会随着父窗口的隐藏而隐藏。
    3、父窗口被卸载时,哈,您已经知道我想说什么了,当然是∶跟着自动卸载。
    4、父窗口移动时,跟着移动。
    二、兄弟关系及Z序列
    当然,一位父亲有好几个儿女,都是常见的事情。同样,一个父窗口可以拥有多个子窗体。比如,位于一个窗体中的各种控件之间以及MDI窗口的各子窗口之间的关系。父窗口与子窗口的显示、隐藏、卸载及移动,其先后顺序是显而易见的。那么各兄弟窗口之间的情况会是如何呢?


    显然,两个互相重叠的两个子窗体不能都同时显示出它的全貌,自然有个显示的顺序规则。这个顺序规则叫做Z序列。有个解释为,如果把屏幕坐标看层X和Y轴组成的平面(事实上正是如此),那么作为三维坐标系统Z轴可看做是垂直于屏幕的坐标轴。这样,可以认为屏幕上的所有窗口是垂直于这个Z轴的。在Z轴上,谁在前,谁在后,就产生了一个Z序列。很生动!可用WINDOWS API函数和Visual Basic Z序列方法对Z序列进行控制。


    有了以上简单的知识以后,我们就不难应用API窗口分级函数,主要有以下及个∶
    窗口分级函数


    函数名 说 明
    FindWindow 按类名或窗口名(Caption)查找一个窗口
    FindWindowEx 类似于FindWindow提供了更多的功能
    GetLastActivePopup针对指定的窗口,取回上一个活动的弹出式窗口的句柄
    GetParent 获得指定窗口父窗口的句柄
    GetTopWindow 获得指定窗口的第一个子窗口的句柄
    GetWindow 如给定一个窗口句柄,该函数能取回具有特定关系的另一个窗口 的句柄。如,第一个子窗口、父窗口或窗口列表内的上一个或下一个窗口。
    SetParent 改变任何窗口的父窗口。
    从我个人的经验来看,我最常用的是GetWindow和SetParent函数。


    三、窗口位置与大小函数


    Windows API函数基本上都是(尤其是USER32.DLL动态连接库内的函数)以屏幕像素为度量单位的。这一点很重要,必须牢记。为此,在使用API函数的时候,我们经常把窗体或图片框控件的ScaleMode属性设置为3,即vbPixels(像素)。
    理解窗口位置及大小函数的关键在于分清屏幕坐标、窗口坐标及客户坐标这三个概念。以下图展示了这三个坐标系统之间的关系。



    屏目、窗口与客户区坐标系统


    只要对这些坐标有了明确的概念,对使用窗口位置及大小函数就不难了。关于窗口的位置,有些函数返回的是上一堂课学习到的RECT结构。有关窗口位置及大小函数如下表所列∶
    窗口位置及大小函数


    函数名 说 明
    BringWindowToTop 使指定的窗口进入可见窗口列表的顶部,如它被部分或全部隐藏,则令其全部可见。同时,该窗口成为当前活动窗口。只有从前台线程调用时,才生效。
    ChildWindowFromPoint 在规定的坐标取得某子窗口的句柄(如果有的话),这儿的坐标是指相对于父窗口的客户区坐标。
    ChildWindowFromPointEx与 ChildWindowFromPoint相同,功能更强。
    ClientToScreen 判断指定点在窗口客户区内的屏幕坐标。
    GetClientRect 获得对窗口客户区进行表述的一个矩形(RECT)。这是以像素为单位判断客户区大小的一个简便的方法。
    GetWindowPlacement 获得指定窗口的一个WINDOWPLACEMENT结构。该结构说明了窗口的状态。
    GetWindowRect 用于获得一个矩形(RECT)结构,它描述了窗体在屏幕坐标系统中的位置。
    MapWindowPoints 对某窗口客户区坐标内的一个或多个点进行转换,用另一窗口的客户区坐标表示。
    MoveWindow 移动指定窗口的位置,并能改变它的大小。
    OpenIcon 将一个最小化窗口恢复为原始状态。
    ScreenToClient 针对屏幕内一个指定的点,用某个特定窗口内的客户区坐标表示它。
    SetWindowsPos 更改窗口的位置和大小,并能修改它在内部窗口列表内的位置(这个列表起着控制窗口先是顺序)。
    SetWindowPlacement 在一个WINDOWPLACEMENT结构的基础上,设置某窗口的特征。该结构描述了窗口的状态,以及它在最小化、最大化或正常显示时的位置。
    WindowFromPoint 根据屏幕上一个指定的点,判断哪个窗口正位于它的下面。
    以上函数的具体用法均可在WinAPI帮助文中找到。在这些函数当中,SetWindowsPos
    函数的使用率比较高,现在很多人都是用这个函数来实现"窗口总在前面"的效果,即通过
    HWND_TOPMOST常数把窗口置于列表顶部。如果想把From1置于列表顶部,方法如下∶
    SetWindowPos Form1.hWnd, HWND_TOPMOST, Form1.Left / Screen.TwipsPerPixelX, Fo rm1.Top Screen.TwipsPerPixelY, Form1.Width Screen.TwipsPerPixelX, Form1.Height Screen.TwipsPerPixelY, 0


    您可以把这行代码放在Paint事件中。
    另外,GetWindowRect函数、MoveWindow函数以及下一课堂即将要学到的
    GetCursorPos函数的相互配合能够实现一个拖动无标题栏的窗口。这是您必须掌握的技巧之一。感兴趣的朋友,可以到《前线》的《源码解析》栏目,下载第4号演示程序。以下是其主要的代码部分∶
    Private MyRect As RECT
    Private MyPoint As POINTAPI
    Private Movex As Long, Movey As Long


    Private Sub Image1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)


    Dim dl&
    dl& = GetWindowRect(Form1.hwnd, MyRect)
    dl& = GetCursorPos(MyPoint)
    If Button = 1 Then
    Movex = MyPoint.X - MyRect.Left
    Movey = MyPoint.Y - MyRect.Top
    End If
    End Sub


    Private Sub Image1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    Dim dl&
    dl& = GetCursorPos(MyPoint)
    If Button = 1 Then
    dl& = MoveWindow(Form1.hwnd, MyPoint.X - Movex, MyPoint.Y - Movey, _


    MyRect.Right - MyRect.Left, MyRect.Bottom - MyRect.Top, -1)
    End If
    End Sub
    在MouseDown事件中,程序首先用 GetWindowRect 函数确定窗口在屏幕中的位置。再次是通过GetCursorPos函数确鼠标在屏幕中的位置。从而可通过计算获得鼠标位置与窗口左上角之间的横向与纵向距离(Movex与Movey)。
    在紧接着发生的MouseMove事件中程序不断地用GetCursorPos函数获得鼠标当前的位置,并按前面已经求得的Movex与Movey判断窗口所应处的位置,而这在MoveWindow函数调用中直接完成。MoveWindow函数将窗体移动到新的位置。


    四、窗口信息函数


    所谓窗口信息函数就是用来获取有关窗口当前状态信息的函数。这类函数主要有∶


    函 数 说 明
    GetClassInfo 取得指定窗口的类信息结构
    GetClassInfoEx 效果类似于GetClassInfo,但增加了一些功能
    GetClassLong,GetClassWord用于获取窗口类信息
    SetClassLong,SetClassWord用于设置窗口类信息
    GetClassName 获取窗口类名
    GetDesktopWindow 获取整个桌面(屏幕)的窗口句柄
    GetWindowLong,GetWindowWord 获取与窗口有关的信息
    SetWindowLong,SetWindowWord 设置与窗口有关的信息
    GetWindowText获得窗口文本。它的效果大致等价于窗体或控件的Text属性
    GetWindowTextLength获得窗口文本的长度,用字符数表示。
    IsChild 判断某窗口是否为另一窗口的子窗口或从属窗口。
    IsIconic 判断某窗口是否处于最小化状态
    IsWindow 判断指定的句柄是否为窗口句柄。
    IsWindowEnabled 判断指定的窗口是否处于活动状态。
    IsWindowVisible 判断某窗口是否可见。
    IsZoomed 判断窗口是否处于最大化状态。
    SetWindowText 设置窗口文本。大致等价于窗体或控件的Text属性。
    大部分窗口信息函数是非常好理解的,按照有关手册中进行的函数说明,按指定数据类型进行调用即可。有必要说明的是,关于类和窗口的样式位。Windows是用一个长整形的数据的位设置方式来记录类和窗口的样式的。其中,窗口样式由一个32位样式以及另一个32
    位扩展样式来构成。类样式操作由上述列表中的GetClassLong以及GetClassLong来进行,窗口样式操作由GetWindowLong 以及SetWindowLong来进行。
    由于样式位的内容较多,我无法在此给出,您可以参考有关手册。这里有必要提醒大家的是,您想改变或获取当前窗口或类的样式,绝大多数情况可以考虑样式位操作。下面,就这个问题举一个简单了例子来说明。


    下面是用BS_LEFTTEXT样式位将VB复选框或选项按钮的文本在左边和右边之间相互移动来、移动去的程序(是附带的Program1.vbp的部分内容)。程序的原理很简单。首先用
    GetWindowLong函数获得当前样式位的信息,然后通过位操作来准备新的样式位信息,最后用SetWindowLong实际地去更改。如下∶
    Dim f&, dl&
    f& = GetWindowLong(Option1.hWnd, GWL_STYLE) '获得当前样式位的信息
    If Index = 0 Then
    f& = f& Or BS_LEFTTEXT


    Else
    f& = f& And Not BS_LEFTTEXT
    End If
    dl& = SetWindowLong(Option1.hWnd, GWL_STYLE, f&) '设置新的样式位
    Option1.Refresh
    (对Or和And位操作不熟悉的朋友,请参考有关技术资料)
    在这里,对样式位不进行更详细讨论,主要有这样一个原因。用SetWindowLong函数改变一个样式位之后,不会导致窗口发生相应的变化(至少不会立即变化)。有些样式位可能在运行时候才会成功变化,而大多数都只在窗口创建时才生效。因为,用API方式创建一个窗体已经超出了本教程的范围,就算我在这里对样式位谈得再多,您可能也没有多大用处。同时,微软公司没有告诉我们哪些样式位在运行期间安全地改变,因此对具体的情况,只好靠自己进行具体试验。而从我个人的实际编程经验来看,没有特别的要求,我们不大会涉及到这些样式位操作,很多都可在VB中很方便地实现。


    本教程还附带了一个Program2.vbp的演示程序。是我本人随便编写的,没什么特别希罕之处。想看就看看好了。
    最后,想简单提一提的是,使用SetWindowLong函数的时候,改变GWL_WNDPROC
    数据是非常危险的(系统或VB经常挂死),即更改窗口函数的位置。一般,这种更改在需要进行子类处理的地方应用到。每次试运行程序,都应当习惯性地进行存盘。


    五、其他窗口函数



    API中还有以下本教程未列出的窗口函数,以供大家参考。


    函数名 说 明
    AnyPopup 判断是否存在可见的弹出式窗口
    CascadeWindows 令窗口在一个父窗口内层叠显示
    CloseWindow 对指定的窗口进行最小化处理(如果它是个钉级窗口)对弹出式及子窗口无效
    DestroyWindow 清除指定的窗口以及下属所有子窗口与包容窗口
    DrawAnimatedRects 获得窗口打开或关闭的动画效果
    EnableWindow 激活或屏蔽(禁用)指定窗口
    FlashWindow 令指定窗口的标题闪烁显示
    GetUpdateRect 判断需要更新的那个窗口的位置
    GetWindowContextHelpId取得与窗口关联在一起的帮助场景
    InvalidateRect 指定窗口内需要更新的全部或部分客户区
    IsWindowUnicode 判断一个窗口是否期望文本消息采用Unicode格式
    LockWindowUpdate 允许或禁止描绘指定的窗口
    RedrawWindow 一个功能强大的函数,用于控制全部或部分窗口重画
    ScrollWindow,ScrollWindowEx滚动显示窗口的全部或部分客户区
    SetWindowContextHelpId设置与窗口关联在一起的帮助场景
    ShowOwnedPopups 隐藏或显示从属于指定窗口的所有保容弹出窗口
    ShowWindow 用于设置窗口的状态,其中包括窗口的隐藏、显示、最小化、最大化以及激活等
    ShowWindowAsync 类似于ShowWindow,增加了对其他进程内的窗口进行操作的能力
    TileWindows 令窗口在一个父窗口内平铺显示
    UpdateWindow 立即更新窗口内需要更新的任何部分
    ValidateRect 指出全部或部分矩形已经更新,毋需再更新
    其中,FlashWindow函数非常有趣,不妨大家试一试。

  • Oracle体系结构:内存结构和进程结构

    一)内存结构和进程结构 Oracle数据库的总体结构如下图:
    Oracle体系结构:内存结构和进程结构(图一)  

    1:Oracle实例(Instance)        
      在一个服务器中,每一个运行的Oracle数据库都与一个数据库实例相联系,实例是我们
     访问数据库的手段。
      实例在操作系统中用ORACLE_SID来标识,在Oracle中用参数INSTANCE_NAME来标识,
     它们两个的值是相同的。数据库启动时,系统首先在服务器内存中分配系统全局区(SGA),
     构成了Oracle的内存结构,然后启动若干个常驻内存的操作系统进程,即组成了Oracle的
     进程结构,内存区域和后台进程合称为一个Oracle实例。
                      Oracle体系结构:内存结构和进程结构(图二)
      数据库与实例之间是1对1/n的关系,在非并行的数据库系统中每个Oracle数据库与一个
     实例相对应;在并行的数据库系统中,一个数据库会对应多个实例,同一时间用户只与一个
     实例相联系,当某一个实例出现故障时,其他实例自动服务,保证数据库正常运行。在任何
     情况下,每个实例都只可以对应一个数据库。  2:Oracle 10g动态内存治理
      内存是影响数据库性能的重要因素,Oracle8i使用静态内存治理,Oracle 10g使用动态
     内存治理。所谓静态内存治理,就是在数据库系统中,无论是否有用户连接,也无论并发用
     量大小,只要数据库服务在运行,就会分配固定大小的内存;动态内存治理答应在数据库服
     务运行时对内存的大小进行修改,读取大数据块时使用大内存,小数据块时使用小内存,读
     取标准内存块时使用标准内存设置。
      按照系统对内存使用方法的不同,Oracle数据库的内存可以分为以下几个部分:
      •系统全局区:SGA(System Global Area)
      •程序全局区:PGA(Programe Global Area)
      •排序池:(Sort Area)
      •大池:(Large Pool)
      •Java池:(Java Pool)  

    2-1:系统全局区SGA(System Global Area)
       SGA是一组为系统分配的共享的内存结构,可以包含一个数据库实例的数据或控制信
      息。假如多个用户连接到同一个数据库实例,在实例的SGA中,数据可以被多个用户共享。
      当数据库实例启动时,SGA的内存被自动分配;当数据库实例关闭时,SGA内存被回收。
      SGA是占用内存最大的一个区域,同时也是影响数据库性能的重要因素。
      SGA的有关信息可以通过下面的语句查询,sga_max_size的大小是不可以动态调整的。
       =====================================
       SQL> show parameter sga
       NAME                                 TYPE        VALUE
       ------------------------------------ ----------- --------
       lock_sga                             boolean     FALSE
       pre_page_sga                         boolean     FALSE
       sga_max_size                         big integer 164M
       sga_target                           big integer 0
       
       SQL> alter system set sga_max_size=100m;
       alter system set sga_max_size=100m
                     *
       ERROR at line 1:
       ORA-02095: specified initialization parameter cannot be modified
       ======================================  
      
    系统全局区按作用不同可以分为:
       •数据缓冲区
       •日志缓冲区
       •共享池   

    2-1-1:数据缓冲区(Database Buffer Cache)
        假如每次执行一个操作时,Oracle都必须从磁盘读取所有数据块并在改变它之后
       又必须把每一块写入磁盘,显然效率会非常低。数据缓冲区存放需要经常访问的数据,
       供所有用户使用。修改数据时,首先从数据文件中取出数据,存储在数据缓冲区中,
       修改/插入数据也存储在缓冲区中,commit或DBWR(下面有具体介绍)进程的其他条
       件引发时,数据被写入数据文件。
        数据缓冲区的大小是可以动态调整的,但是不能超过sga_max_size的限制。
       ======================================
       SQL> show parameter db_cache_size
       NAME                                 TYPE        VALUE
       ------------------------------------ ----------- -----------------
       db_cache_size                        big integer 24M
       
       SQL> alter system set db_cache_size=128m;
       alter system set db_cache_size=128m
       *
       ERROR at line 1:
       ORA-02097: parameter cannot be modified because specified value is invalid
       ORA-00384: Insufficient memory to grow cache    SQL> alter system set db_cache_size=20m;
       System altered.
       
       SQL> show parameter db_cache_size;
       NAME                                 TYPE        VALUE
       ------------------------------------ ----------- -----------------
       db_cache_size                        big integer 20M
       
       #此处我仅增加了1M都不行?
       SQL> alter system set db_cache_size=25m;
       alter system set db_cache_size=25m
       *
       ERROR at line 1:
       ORA-02097: parameter cannot be modified because specified value is invalid
       ORA-00384: Insufficient memory to grow cache    #修改显示格式,方便查看。
       SQL> column name format a40 wrap
       SQL> column value format a20 wrap
       
       #下面语句可以用来查看内存空间分配情况,注重SGA各区大小总和。
       SQL> select name,value from v$parameter where name like '%size' and value <> '0';
       
       #先将java_pool_size调小,然后再修改db_cache_size
       SQL> show parameter java_pool_size;
       NAME                                 TYPE        VALUE
       ------------------------------------ ----------- -----
       java_pool_size                       big integer 48M
       SQL> alter system set java_pool_size=20m;
       System altered.    SQL> alter system set java_pool_size=30m;
       System altered.
       #上面说明SGA中各区大小总和不能超过sga_max_size。
       =====================================
        数据缓冲区的大小对数据库的存区速度有直接影响,多用户时尤为明显。有些应
       用对速度要求很高,一般要求数据缓冲区的命中率在90%以上。
        下面给出一种计算数据缓冲区命中率的方法:
        •使用数据字典v$sysstat
        =====================================
        SQL> select name, value from v$sysstat
             2  where name in('session logical reads',
             3  'physical reads',
             4  'physical reads direct',
             5  'physical reads direct (lob)')
        NAME                              VALUE
        ------------------------------- ----------
        session logical reads               895243
        physical reads                       14992
        physical reads direct                   34
        physical reads direct (lob)              0
        ======================================
        命中率=1-(14992-34-0)/895243     可以让Oracle给出数据缓冲区大小的建议:
        ======================================
        SQL> alter system set db_cache_advice=on;#打开该功能
        System altered.     SQL> alter system set db_cache_advice=off;#关闭该功能
        System altered.
        ======================================   2-1-2:日志缓冲区(Log Buffer Cache)
        日志缓冲区用来存储数据库的修改信息。该区对数据库性能的影响很小,有关日
       志后面还会有具体的介绍。
        查询日志缓冲区大小:
       SQL> show parameter log_buffer
       NAME            TYPE          VALUE
       ----------     -----------  -------
       log_buffer     integer       262144 
      2-1-3:共享池(Share Pool)
        共享池是对SQL,PL/SQL程序进行语法分析,编译,执行的内存区域。
        它包含三个部分:(都不可单独定义大小,必须通过share pool间接定义)。
        •库缓冲区(Library Cache)包含SQL,PL/SQL语句的分析码,执行计划。
        •数据字典缓冲区(Data Dictionary Cache)表,列定义,权限。
        •用户全局区(Usr Global Area)用户MTS会话信息。
       共享池的大小可以动态修改:
        ======================================
        SQL> show parameter shared_pool_size
        NAME                                 TYPE        VALUE
        ------------------------------------ ----------- ------
        __shared_pool_size                   big integer 80M
        shared_pool_size                     big integer 80M
        
        SQL> alter system set shared_pool_size=78m 
        System altered.
        ======================================
        #上面的__shared_pool_size一行希奇?  

    2-2:程序全局区PGA(Programe Global Area)
       程序全局区是包含单个用户或服务器数据和控制信息的内存区域,它是在用户进程连
      接到Oracle并创建一个会话时由Oracle自动分配的,不可共享,主要用于用户在编程存
      储变量和数组。
        Oracle体系结构:内存结构和进程结构(图三)
       如上图:
        •Stack Space是用来存储用户会话变量和数组的存储区域;
        •User Session Data是为用户会话使用的附加存储区。
         --Session Information
         --Sort Area
         --Cursor Information   
       注重Session information(用户会话信息)在独占服务器中与在共享服务器中所处
      的内存区域是不同的。
     2-3:排序区,大池,Java池
       排序区(Sort Area)为有排序要求的SQL语句提供内存空间。系统使用专用的内存区
      域进行数据排序,这部分空间就是排序区。在Oracle数据库中,用户数据的排序可使用
      两个区域,一个是内存排序区,一个是磁盘临时段,系统优先使用内存排序区进行排序。
      假如内存不够,Orcle自动使用磁盘临时表空间进行排序。为提高数据排序的速度,建议
      尽量使用内存排序区,而不要使用临时段。
       参数sort_area_size用来设置排序区大小。(好象不能动态修改?)    大池(Large Pool)用于数据库备份工具--恢复治理器(RMAN:Recovery Manager)。
      Large Pool的大小由large_pool_size确定,可用下面语句查询和修改:
      =========================================
      SQL> show parameter large_pool_size
      NAME                  TYPE        VALUE
      -----------------   ----------- -------
      large_pool_size     big integer      8M
      SQL> alter system set large_pool_size=7m;
      System altered. 
      =========================================
       
       Java池主要用于Java语言开发,一般来说不低于20M。其大小由java_pool_size来
      确定,可以动态调整。

     2-4:Oracle自动共享内存治理(Automatic Shared Memory(SGA) Management)
      在Oracle 8i/9i中数据库治理员必须手动调整SGA各区的各个参数取值,每个区要根据
     负荷轻重分别设置,假如设置不当,比如当某个区负荷增大时,没有调整该区内存大小,则
     可能出现ORA-4031:unable to allocate ...bytes of shared memory错误。
      在Oracle 10g中,将参数STATISTICS_LEVEL设置为TYPICAL/ALL,使用SGA_TARGET指
     定SGA区总大小,数据库会根据需要在各个组件之间自动分配内存大小。
     下面是系统自动调整的区域:
      •固定SGA区及其他•共享池•数据缓冲区• Java池•大池。
      注重:假如不设置SGA_TARGET,则自动共享内存治理功能被禁止。
      ==========================================
      SQL> show parameter statistics_level
      NAME                     TYPE        VALUE
      --------------------- ----------- ------------
      statistics_level      string      TYPICAL
      
      SQL> alter system set statistics_level=all;
      System altered.   #typical和all有什么区别?
      
      SQL> alter system set statistics_level=typical;
      System altered.   SQL> show parameter sga_target
      NAME            TYPE        VALUE
      ------------- ----------- ----------
      sga_target    big integer 0   SQL> alter system set sga_target=170m;
      alter system set sga_target=170m
      *
      ERROR at line 1:
      ORA-02097: parameter cannot be modified because specified value is invalid
      ORA-00823: Specified value of sga_target greater than sga_max_size   SQL> alter system set sga_target=20m;
      System altered.   #不过后来又发现sga_target的值变成了140M? 下面是语句执行情况。
      SQL> show parameter sga_target
      NAME          TYPE        VALUE
      ------------- ----------- -------
      sga_target    big integer 140M
      
      SQL> alter system set sga_target=0;
      System altered.   SQL> show parameter sga_target
      NAME                                 TYPE        VALUE
      ------------------------------------ ----------- ------
      sga_target                           big integer 0
      #改为20M
      SQL> alter system set sga_target=20m;
      System altered.   #显示的是140M
      SQL> show parameter sga_target
      NAME                                 TYPE        VALUE
      ------------------------------------ ----------- ------
      sga_target                           big integer 140M   #不可缩减?
      SQL> alter system set sga_target=130m;
      alter system set sga_target=130m
      *
      ERROR at line 1:
      ORA-02097: parameter cannot be modified because specified value is invalid
      ORA-00827: could not shrink sga_target to specified value
      
      #不可增加
      SQL> alter system set sga_target=141m;
      alter system set sga_target=141m
      *
      ERROR at line 1:
      ORA-02097: parameter cannot be modified because specified value is invalid
      ORA-00823: Specified value of sga_target greater than sga_max_size
      
    3:Oracle实例的进程结构(Process StrUCture)
     Oracle包含三类进程:
     •用户进程(User Process)
     •服务器进程(Server Process)
     •后台进程(Background Process)
     
     3-1:用户进程和服务器进程
      当数据库用户请求连接到Oracle的服务时启动用户进程(比如启动SQLPlus时)。
      •用户进程首先必须建立一个连接。
      •用户不能直接与Oracle服务器,必须通过服务器进程交互。
      •服务器进程是用户进程与服务器交互的桥梁,它可以与Oracle Server直接交互。
      •服务器进程可以有共享和独占两种形式。
                   
    Oracle体系结构:内存结构和进程结构(图四) 
      3-2:后台进程(Backgroung Process)
      数据库的物理结构与内存结构之间的交互要通过后台进程来完成。数据库的后台进程包
     含两类,一类是必须的,一类是可选的:
      •Mandatory background processes
       --DBWn(Database Writer):数据写入
       --PMON(Process Moniter):进程监控
       --LGWR(Log Writer):日志写入
       --SMON(System Moniter):系统监控
       --RECO(Recovery):恢复
       --CKPT(Chekpoint):检查点
      •Optional background processes
       --ARCn(Archiver):归档
       --LCKn(Lock):锁
       --Dnnn(Dispatcher):调度
       --......
      可以用下面的语句查看正在运行的后台进程:
      =========================================
      SQL> select * from v$bgprocess where paddr<>'00';
      PADDR      PSERIAL# NAME  DESCRIPTION                          
      -------- ---------- ----- -------------------------------
      6B0ED064          1 PMON  process cleanup                      
      6B0ED4E4          1 MMAN  Memory Manager                       
      6B0ED964          1 DBW0  db writer process 0                  
      6B0EDDE4          1 LGWR  Redo etc.                            
      6B0EE264          1 CKPT  checkpoint                           
      6B0EE6E4          1 SMON  System Monitor Process               
      6B0EEB64          1 RECO  distributed recovery                 
      6B0EEFE4          1 CJQ0  Job Queue Coordinator                
      6B0F01E4          1 QMNC  AQ Coordinator                       
      6B0F0664          1 MMON  Manageability Monitor Process        
      6B0F0AE4          1 MMNL  Manageability Monitor Process 2
      ========================================
     3-2-1:DBWR(Database Writer,数据写入进程)
      将数据缓冲区的数据写入数据文件,是负责数据缓冲区治理的一个后台进程。
      当数据缓冲区中的一数据被修改后,就标记为dirty,DBWR进程将数据缓冲区中“脏”
     数据写入数据文件,保持数据缓冲区的”干净“。由于数据缓冲区的数据被用户修改并占
     用,空闲数据缓冲区会不断减少,当用户进程要从磁盘读取数据块到数据缓冲区却无法找
     到足够的空闲数据缓冲区时,DBWR将数据缓冲区内容写入磁盘,使用户进程总可以得到足
     够的空闲数据缓冲区。
      DBWR的作用:
      •治理数据缓冲区,以便用户进程总能够找到足够的空闲缓冲区。
      •将所有修改后的缓冲区数据写入数据文件。
      •使用LRU(最近最少使用)算法保持缓冲区数据是最近经常使用的。
      •通过延迟写来优化磁盘I/0读写。
       Oracle体系结构:内存结构和进程结构(图五)

    点击查看大图  3-2-2:LGWR(Log Writer,日志写入进程)
      将日志数据从日志缓冲区写入磁盘日志文件组。数据库在运行时,假如对数据库进行修
     改则产生日志信息,日志信息首先产生于日志缓冲区。当日志达到一定数量时,由LGWR将
     将日志数据写入到日志文件组,再经过日志切换,由归档进程(ARCH)将日志数据写入归
     档进程(前提是数据库运行在归档模式下)。数据库遵循写日志优先原则,即在写数据之前
     先写日志。
         Oracle体系结构:内存结构和进程结构(图六) 点击查看大图简单介绍
      •ARCH(Archiver,归档进程)
      Oracle数据库有两种运行模式,归档(ARCHIVELOG),非归档(NOARCHIVELOG)模式。
     以非归档模式运行时日志在切换时被直接覆盖,不产生归档日志,这是数据库默认的运行模
     式。数据库运行在归档模式时,在日志切换之前,由ARCH进程将日志信息写入磁盘,也就是
     自动备份在线日志。
      Oracle数据库的Redo文件数量是有限的,所以Oracle以循环的方式向它们中写入。它顺
     序写满每一个Redo文件,当达到最后一个时,再循环回去开始填写第一个Redo文件。假如为
     了能恢复数据库而想保存日志文件,那么在它们被重新使用之前需要对其进行备份,归档进
     程治理此工作。   •CKPT(Check Point,检查点进程)
      运行CKPT时,系统对全部数据文件及控制文件文件头的同步信号进行修改,以保证数据
     库的同步。检查点出现在以下情况:
      --在每个日志切换时产生。
      --上一个检验点之后又经过了指定时间。
      --从上一个检验点之后,当预定义数量的日志块被写入磁盘之后。
      --数据库关闭。
      --DBA强制产生。
      --当表空间设置为OFFLINE时。   •SMON(System Moniter,系统监控进程)
      SMON在实例启动时执行实例恢复,并负责清理不再使用的临时段。   •PMON(Process Monitor,进程监控)
      PMON在用户进程出现故障时进行恢复,负责清理内存区域和释放该进程所使用的资源。   •RECO(Recovery,恢复进程)
      RECO用于分布式数据库维持在分布式环境中的数据的一致性。   •LCKn(Lock,锁进程)
      在并行服务器中用于多个实例间的封锁。   •Dnnn(Dispatcher,调度进程)
      Dnnn存在于多线程服务器体系结构中,负责将用户进程连接到服务器进程,再把结果返
      回给用户进程。
    Posted Sep 19 2008, 11:49 AM by slash with 1 comment(s)
    Filed under:
  • 70,80,90

          1、
          70后:工作狂基本上都是70后的。
      80后:而我们,拒绝加班!
      90后:拒绝上班!
      2、
      70后:他们喜欢穿七匹狼或者猛龙牌子的衣服。
      80后:我们喜欢G-Star之类的。
      90后:乞丐服,越花越好,越破越好..一个洞时尚,两个洞潮流,三个洞个性...
      3、
      70后:他们唱k的时候只会乱吼──例如2002年的第一场雪,然后就拼命拉着你喝酒,不让你唱。
      80后:Mic霸一般是我们。
      90后:我们不止会唱,还会跳!
      4、
      70后:他们的话题除了工作就是股票。
      80后:我们的话题更多,有英超、魔兽……
      90后:QQ等级,QQ秀...
      5、
      70后:他们如果有笔记本,会喜欢到公众场合用。
      80后:我们才不会背那么重的东西在身上。
      90后:只要苹果笔记本,而且不止一台...
      6、
      70后:他们喜欢喝红酒,一般是长城红酒。
      80后:我们要么不喝酒,要么就喝啤酒。
      90后:韩国果汁,日本汽水...
      7、
      70后:他们无论任何时候,看到有站着的领导,都会马上给领导让座。
      80后:我们崇尚上下级平等。
      90后:天上地下,唯我独尊!
      8、
      70后:他们娶老婆的时候想娶处女。
      80后:我们觉得无所谓,只要相互感情好就可以了。
      90后:结婚需要感情吗?..需要结婚吗?..
      9、
      70后:他们觉得每个日本人、美国人、台湾人都想攻打中国。
      80后:我们喜欢日本的连续剧、台湾的综艺节目、美国的大片。
      90后:我要去日本,因为我是日系MM...
      10、
      70后:他们希望中国用核弹把上面三个国家(地区)都灭了。
      80后:我们希望和平。
      90后:和我无关!打仗衣服会降价吗?那就打呗~~
          11、
      70后:他们对服务员态度恶劣,或者言语上调戏女服务员。
      80后:我们只在点菜和结帐时会跟服务员说话。
      90后:从不和waitress说话,只会背后讨论她的衣服很土...
      12、
      70后:他们有存款。
      80后:我们负债。
      90后:我们有老爸!
      13、
      70后:他们会把房子买在番禺或者花都,然后每天早上花一个多小时乘车去上班。
      80后:我们喜欢在公司附近租房子,每天骑车或走路去上班,就为了早上多睡一会。
      90后:我们住哪里都可以,只要BF喜欢...
      14、
      70后:他们结交有背景有地位的人。
      80后:我们结交志趣相投的人。
      90后:我们结交满身文身的帅哥!
      15、
      70后:他们周末约客户去吃饭。
      80后:我们周末约同学去踢球。
      90后:一个礼拜7天周末,想做什么做什么!
      16、
      70后:他们喝酒时喜欢跟别人干杯
      80后:我们能喝多少喝多少,喝不下了,怎么也不肯再喝
      90后:我不是随便喝酒的人,我随便喝起酒来不是人~
      17、
      70后:他们的家进门要脱鞋。
      80后:我们家进门不用脱鞋。
      90后:我们上床睡觉都不脱鞋!
      18、
      70后:他们五一、国庆去旅游,然后会在各个景点门口拍下很多V字手势的照片。  
          80后:我们五一、国庆在家睡觉,或者约朋友去唱k,去旅游,我们只会拍景色。
      90后:我们天天是五一,国庆....取消五一没什么关系...
      19、
      70后:吃饭时,他们喜欢坐在老板旁边
      80后:我们最好别坐在老板旁边,那才无拘无束
      90后:我是老板!
      20、
      70后:他们跟陌生人在一起的时候喜欢找话题说。
      80后:我们不太搭理陌生人,故意找话题不累么?
      90后:你谁阿,穿这么土,死开~帅哥,交个朋友好嘛?~~

  • Altering Index

    Altering Storage Characteristics of an Index

    Alter the storage parameters of any index, including those created by the database to enforce primary and unique key integrity constraints, using the ALTER INDEX statement. For example, the following statement alters the emp_ename index:

    ALTER INDEX emp_ename
    STORAGE (PCTINCREASE 50);

    The storage parameters INITIAL and MINEXTENTS cannot be altered. All new settings for the other storage parameters affect only extents subsequently allocated for the index.

    For indexes that implement integrity constraints, you can adjust storage parameters by issuing an ALTER TABLE statement that includes the USING INDEX subclause of the ENABLE clause. For example, the following statement changes the storage options of the index created on table emp to enforce the primary key constraint:

    ALTER TABLE emp
    ENABLE PRIMARY KEY USING INDEX;
     

    Rebuilding an Existing Index

    Before rebuilding an existing index, compare the costs and benefits associated with rebuilding to those associated with coalescing indexes as described in Table 16-1.

    When you rebuild an index, you use an existing index as the data source. Creating an index in this manner enables you to change storage characteristics or move to a new tablespace. Rebuilding an index based on an existing data source removes intra-block fragmentation. Compared to dropping the index and using the CREATE INDEX statement, re-creating an existing index offers better performance.

    The following statement rebuilds the existing index emp_name:

    ALTER INDEX emp_name REBUILD;

    The REBUILD clause must immediately follow the index name, and precede any other options. It cannot be used in conjunction with the DEALLOCATE UNUSED clause.

    You have the option of rebuilding the index online. Rebuilding online enables you to update base tables at the same time that you are rebuilding. The following statement rebuilds the emp_name index online:

    ALTER INDEX emp_name REBUILD ONLINE;
     

    Monitoring Index Usage

    Oracle Database provides a means of monitoring indexes to determine whether they are being used. If an index is not being used, then it can be dropped, eliminating unnecessary statement overhead.

    To start monitoring the usage of an index, issue this statement:

    ALTER INDEX index MONITORING USAGE;

    Later, issue the following statement to stop the monitoring:

    ALTER INDEX index NOMONITORING USAGE;

    The view V$OBJECT_USAGE can be queried for the index being monitored to see if the index has been used. The view contains a USED column whose value is YES or NO, depending upon if the index has been used within the time period being monitored. The view also contains the start and stop times of the monitoring period, and a MONITORING column (YES/NO) to indicate if usage monitoring is currently active.

    Each time that you specify MONITORING USAGE, the V$OBJECT_USAGE view is reset for the specified index. The previous usage information is cleared or reset, and a new start time is recorded. When you specify NOMONITORING USAGE, no further monitoring is performed, and the end time is recorded for the monitoring period. Until the next ALTER INDEX...MONITORING USAGE statement is issued, the view information is left unchanged.

    Monitoring Space Use of Indexes

    If key values in an index are inserted, updated, and deleted frequently, the index can lose its acquired space efficiently over time. Monitor index efficiency of space usage at regular intervals by first analyzing the index structure, using the ANALYZE INDEX...VALIDATE STRUCTURE statement, and then querying the INDEX_STATS view:

    SELECT PCT_USED FROM INDEX_STATS WHERE NAME = 'index';

    The percentage of index space usage varies according to how often index keys are inserted, updated, or deleted. Develop a history of average efficiency of space usage for an index by performing the following sequence of operations several times:

    • Analyzing statistics

    • Validating the index

    • Checking PCT_USED

    • Dropping and rebuilding (or coalescing) the index

    When you find that index space usage drops below its average, you can condense the index space by dropping the index and rebuilding it, or coalescing it.

     
     

    Dropping Indexes

    To drop an index, the index must be contained in your schema, or you must have the DROP ANY INDEX system privilege.

    Some reasons for dropping an index include:

    • The index is no longer required.

    • The index is not providing anticipated performance improvements for queries issued against the associated table. For example, the table might be very small, or there might be many rows in the table but very few index entries.

    • Applications do not use the index to query the data.

    • The index has become invalid and must be dropped before being rebuilt.

    • The index has become too fragmented and must be dropped before being rebuilt.

    When you drop an index, all extents of the index segment are returned to the containing tablespace and become available for other objects in the tablespace.

    How you drop an index depends on whether you created the index explicitly with a CREATE INDEX statement, or implicitly by defining a key constraint on a table. If you created the index explicitly with the CREATE INDEX statement, then you can drop the index with the DROP INDEX statement. The following statement drops the emp_ename index:

    DROP INDEX emp_ename;

    You cannot drop only the index associated with an enabled UNIQUE key or PRIMARY KEY constraint. To drop a constraints associated index, you must disable or drop the constraint itself.

     
     

    Viewing Index Information

    The following views display information about indexes:

    View Description
    DBA_INDEXES

    ALL_INDEXES

    USER_INDEXES

    DBA view describes indexes on all tables in the database. ALL view describes indexes on all tables accessible to the user. USER view is restricted to indexes owned by the user. Some columns in these views contain statistics that are generated by the DBMS_STATS package or ANALYZE statement.
    DBA_IND_COLUMNS

    ALL_IND_COLUMNS

    USER_IND_COLUMNS

    These views describe the columns of indexes on tables. Some columns in these views contain statistics that are generated by the DBMS_STATS package or ANALYZE statement.
    DBA_IND_EXPRESSIONS

    ALL_IND_EXPRESSIONS

    USER_IND_EXPRESSIONS

    These views describe the expressions of function-based indexes on tables.
    DBA_IND_STATISTICS

    ALL_IND_STATISTICS

    USER_IND_STATISTICS

    These views contain optimizer statistics for indexes.
    INDEX_STATS Stores information from the last ANALYZE INDEX...VALIDATE STRUCTURE statement.
    INDEX_HISTOGRAM Stores information from the last ANALYZE INDEX...VALIDATE STRUCTURE statement.
    V$OBJECT_USAGE Contains index usage information produced by the ALTER INDEX...MONITORING USAGE functionality.
     
  • SQL optimize

    (1)      选择最有效率的表名顺序(只在基于规则的优化器中有效):
    ORACLE 的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 driving table)

    将被最先处理,在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。如果有3个以上的

    表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表.
    (2)      WHERE子句中的连接顺序.:
    ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以

    过滤掉最大数量记录的条件必须写在WHERE子句的末尾.
    (3)      SELECT子句中避免使用 ‘ * ‘:
    ORACLE在解析的过程中, 会将'*' 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗

    费更多的时间
    (4)      减少访问数据库的次数:
    ORACLE在内部执行了许多工作: 解析SQL语句, 估算索引的利用率, 绑定变量 , 读数据块等;
    (5)      在SQL*Plus , SQL*Forms和Pro*C中重新设置ARRAYSIZE参数, 可以增加每次数据库访问的检索数据量

    ,建议值为200
    (6)      使用DECODE函数来减少处理时间:
    使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表.
    (7)      整合简单,无关联的数据库访问:
    如果你有几个简单的数据库查询语句,你可以把它们整合到一个查询中(即使它们之间没有关系)
    (8)      删除重复记录:
    最高效的删除重复记录方法 ( 因为使用了ROWID)例子:
    DELETE  FROM  EMP E  WHERE  E.ROWID > (SELECT MIN(X.ROWID)
    FROM  EMP X  WHERE  X.EMP_NO = E.EMP_NO);
    (9)      用TRUNCATE替代DELETE:
    当删除表中的记录时,在通常情况下, 回滚段(rollback segments ) 用来存放可以被恢复的信息. 如果你没有

    COMMIT事务,ORACLE会将数据恢复到删除之前的状态(准确地说是恢复到执行删除命令之前的状况) 而当运用

    TRUNCATE时, 回滚段不再存放任何可被恢复的信息.当命令运行后,数据不能被恢复.因此很少的资源被调用,执行

    时间也会很短. (译者按: TRUNCATE只在删除全表适用,TRUNCATE是DDL不是DML)
    (10) 尽量多使用COMMIT:
    只要有可能,在程序中尽量多使用COMMIT, 这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少:
    COMMIT所释放的资源:
    a. 回滚段上用于恢复数据的信息.
    b. 被程序语句获得的锁
    c. redo log buffer 中的空间
    d. ORACLE为管理上述3种资源中的内部花费
    (11) 用Where子句替换HAVING子句:
    避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作.

    如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销. (非oracle中)on、where、having这三个都可以

    加条件的子句中,on是最先执行,where次之,having最后,因为on是先把不符合条件的记录过滤后才进行统计,

    它就可以减少中间运算要处理的数据,按理说应该速度是最快的,where也应该比having快点的,因为它过滤数据

    后才进行sum,在两个表联接时才用on的,所以在一个表的时候,就剩下where跟having比较了。在这单表查询统

    计的情况下,如果要过滤的条件没有涉及到要计算字段,那它们的结果是一样的,只是where可以使用rushmore技

    术,而having就不能,在速度上后者要慢如果要涉及到计算的字段,就表示在没计算之前,这个字段的值是不确

    定的,根据上篇写的工作流程,where的作用时间是在计算之前就完成的,而having就是在计算后才起作用的,所

    以在这种情况下,两者的结果会不同。在多表联接查询时,on比where更早起作用。系统首先根据各个表之间的联

    接条件,把多个表合成一个临时表后,再由where进行过滤,然后再计算,计算完后再由having进行过滤。由此可

    见,要想过滤条件起到正确的作用,首先要明白这个条件应该在什么时候起作用,然后再决定放在那里
    (12) 减少对表的查询:
    在含有子查询的SQL语句中,要特别注意减少对表的查询.例子:
        SELECT  TAB_NAME FROM TABLES WHERE (TAB_NAME,DB_VER) = ( SELECT
    TAB_NAME,DB_VER FROM  TAB_COLUMNS  WHERE  VERSION = 604)
    (13) 通过内部函数提高SQL效率.:
    复杂的SQL往往牺牲了执行效率. 能够掌握上面的运用函数解决问题的方法在实际工作中是非常有意义的
    (14) 使用表的别名(Alias):
    当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析的时间并

    减少那些由Column歧义引起的语法错误.
    (15) 用EXISTS替代IN、用NOT EXISTS替代NOT IN:
    在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下, 使用EXISTS(或NOT

    EXISTS)通常将提高查询的效率. 在子查询中,NOT IN子句将执行一个内部的排序和合并. 无论在哪种情况下,NOT

    IN都是最低效的 (因为它对子查询中的表执行了一个全表遍历). 为了避免使用NOT IN ,我们可以把它改写成外连

    接(Outer Joins)或NOT EXISTS.
    例子:
    (高效)SELECT * FROM  EMP (基础表)  WHERE  EMPNO > 0  AND  EXISTS (SELECT ‘X'  FROM DEPT  WHERE 

    DEPT.DEPTNO = EMP.DEPTNO  AND  LOC = ‘MELB')
    (低效)SELECT  * FROM  EMP (基础表)  WHERE  EMPNO > 0  AND  DEPTNO IN(SELECT DEPTNO  FROM  DEPT 

    WHERE  LOC = ‘MELB')
    (16) 识别'低效执行'的SQL语句:
    虽然目前各种关于SQL优化的图形化工具层出不穷,但是写出自己的SQL工具来解决问题始终是一个最好的方法:
    SELECT  EXECUTIONS , DISK_READS, BUFFER_GETS,
    ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_radio,
    ROUND(DISK_READS/EXECUTIONS,2) Reads_per_run,
    SQL_TEXT
    FROM  V$SQLAREA
    WHERE  EXECUTIONS>0
    AND  BUFFER_GETS > 0
    AND  (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0.8
    ORDER BY  4 DESC;


    (17) 用索引提高效率:
    索引是表的一个概念部分,用来提高检索数据的效率,ORACLE使用了一个复杂的自平衡B-tree结构. 通常,通过索

    引查询数据比全表扫描要快. 当ORACLE找出执行查询和Update语句的最佳路径时, ORACLE优化器将使用索引. 同

    样在联结多个表时使用索引也可以提高效率. 另一个使用索引的好处是,它提供了主键(primary key)的唯一性验

    证.。那些LONG或LONG RAW数据类型, 你可以索引几乎所有的列. 通常, 在大型表中使用索引特别有效. 当然,你

    也会发现, 在扫描小表时,使用索引同样能提高效率. 虽然使用索引能得到查询效率的提高,但是我们也必须注意

    到它的代价. 索引需要空间来存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时, 索引本身也会被

    修改. 这意味着每条记录的INSERT , DELETE , UPDATE将为此多付出4 , 5 次的磁盘I/O . 因为索引需要额外的

    存储空间和处理,那些不必要的索引反而会使查询反应时间变慢.。定期的重构索引是有必要的.:
    ALTER  INDEX <INDEXNAME> REBUILD <TABLESPACENAME>
    18) 用EXISTS替换DISTINCT:
    当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT. 一般可以考虑

    用EXIST替换, EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果. 例子:
          (低效):
    SELECT  DISTINCT  DEPT_NO,DEPT_NAME  FROM  DEPT D , EMP E
    WHERE  D.DEPT_NO = E.DEPT_NO
    (高效):
    SELECT  DEPT_NO,DEPT_NAME  FROM  DEPT D  WHERE  EXISTS ( SELECT ‘X'
    FROM  EMP E  WHERE E.DEPT_NO = D.DEPT_NO);
    (19) sql语句用大写的;因为oracle总是先解析sql语句,把小写的字母转换成大写的再执行
    (20) 在java代码中尽量少用连接符“+”连接字符串!
    (21) 避免在索引列上使用NOT 通常, 
    我们要避免在索引列上使用NOT, NOT会产生在和在索引列上使用函数相同的影响. 当ORACLE”遇到”NOT,他就会

    停止使用索引转而执行全表扫描.
    (22) 避免在索引列上使用计算.
    WHERE子句中,如果索引列是函数的一部分.优化器将不使用索引而使用全表扫描.
    举例:
    低效:
    SELECT … FROM  DEPT  WHERE SAL * 12 > 25000;
    高效:
    SELECT … FROM DEPT WHERE SAL > 25000/12;
    (23) 用>=替代>
    高效:
    SELECT * FROM  EMP  WHERE  DEPTNO >=4
    低效:
    SELECT * FROM EMP WHERE DEPTNO >3
    两者的区别在于, 前者DBMS将直接跳到第一个DEPT等于4的记录而后者将首先定位到DEPTNO=3的记录并且向前扫描

    到第一个DEPT大于3的记录.
    (24) 用UNION替换OR (适用于索引列)
    通常情况下, 用UNION替换WHERE子句中的OR将会起到较好的效果. 对索引列使用OR将造成全表扫描. 注意, 以上

    规则只针对多个索引列有效. 如果有column没有被索引, 查询效率可能会因为你没有选择OR而降低. 在下面的例

    子中, LOC_ID 和REGION上都建有索引.
    高效:
    SELECT LOC_ID , LOC_DESC , REGION
    FROM LOCATION
    WHERE LOC_ID = 10
    UNION
    SELECT LOC_ID , LOC_DESC , REGION
    FROM LOCATION
    WHERE REGION = “MELBOURNE”
    低效:
    SELECT LOC_ID , LOC_DESC , REGION
    FROM LOCATION
    WHERE LOC_ID = 10 OR REGION = “MELBOURNE”
    如果你坚持要用OR, 那就需要返回记录最少的索引列写在最前面.
    (25) 用IN来替换OR 
    这是一条简单易记的规则,但是实际的执行效果还须检验,在ORACLE8i下,两者的执行路径似乎是相同的. 
    低效:
    SELECT…. FROM LOCATION WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30
    高效
    SELECT… FROM LOCATION WHERE LOC_IN  IN (10,20,30);
    (26) 避免在索引列上使用IS NULL和IS NOT NULL
    避免在索引中使用任何可以为空的列,ORACLE将无法使用该索引.对于单列索引,如果列包含空值,索引中将不

    存在此记录. 对于复合索引,如果每个列都为空,索引中同样不存在此记录. 如果至少有一个列不为空,则记录

    存在于索引中.举例: 如果唯一性索引建立在表的A列和B列上, 并且表中存在一条记录的A,B值为(123,null) ,

    ORACLE将不接受下一条具有相同A,B值(123,null)的记录(插入). 然而如果所有的索引列都为空,ORACLE将认为

    整个键值为空而空不等于空. 因此你可以插入1000 条具有相同键值的记录,当然它们都是空! 因为空值不存在于

    索引列中,所以WHERE子句中对索引列进行空值比较将使ORACLE停用该索引.
    低效: (索引失效)
    SELECT … FROM  DEPARTMENT  WHERE  DEPT_CODE IS NOT NULL;
    高效: (索引有效)
    SELECT … FROM  DEPARTMENT  WHERE  DEPT_CODE >=0;
    (27) 总是使用索引的第一个列:
    如果索引是建立在多个列上, 只有在它的第一个列(leading column)被where子句引用时,优化器才会选择使用该

    索引. 这也是一条简单而重要的规则,当仅引用索引的第二个列时,优化器使用了全表扫描而忽略了索引
    28) 用UNION-ALL 替换UNION ( 如果有可能的话):
    当SQL 语句需要UNION两个查询结果集合时,这两个结果集合会以UNION-ALL的方式被合并, 然后在输出最终结果前

    进行排序. 如果用UNION ALL替代UNION, 这样排序就不是必要了. 效率就会因此得到提高. 需要注意的是,UNION

    ALL 将重复输出两个结果集合中相同记录. 因此各位还是要从业务需求分析使用UNION ALL的可行性. UNION 将对

    结果集合排序,这个操作会使用到SORT_AREA_SIZE这块内存. 对于这块内存的优化也是相当重要的. 下面的SQL可

    以用来查询排序的消耗量
    低效:
    SELECT  ACCT_NUM, BALANCE_AMT
    FROM  DEBIT_TRANSACTIONS
    WHERE TRAN_DATE = '31-DEC-95'
    UNION
    SELECT ACCT_NUM, BALANCE_AMT
    FROM DEBIT_TRANSACTIONS
    WHERE TRAN_DATE = '31-DEC-95'
    高效:
    SELECT ACCT_NUM, BALANCE_AMT
    FROM DEBIT_TRANSACTIONS
    WHERE TRAN_DATE = '31-DEC-95'
    UNION ALL
    SELECT ACCT_NUM, BALANCE_AMT
    FROM DEBIT_TRANSACTIONS
    WHERE TRAN_DATE = '31-DEC-95'
    (29) 用WHERE替代ORDER BY:
    ORDER BY 子句只在两种严格的条件下使用索引.
    ORDER BY中所有的列必须包含在相同的索引中并保持在索引中的排列顺序.
    ORDER BY中所有的列必须定义为非空.
    WHERE子句使用的索引和ORDER BY子句中所使用的索引不能并列.
    例如:
    表DEPT包含以下列:
    DEPT_CODE PK NOT NULL
    DEPT_DESC NOT NULL
    DEPT_TYPE NULL
    低效: (索引不被使用)
    SELECT DEPT_CODE FROM  DEPT  ORDER BY  DEPT_TYPE
    高效: (使用索引)
    SELECT DEPT_CODE  FROM  DEPT  WHERE  DEPT_TYPE > 0
    (30) 避免改变索引列的类型.:
    当比较不同数据类型的数据时, ORACLE自动对列进行简单的类型转换.
    假设 EMPNO是一个数值类型的索引列.
    SELECT …  FROM EMP  WHERE  EMPNO = ‘123'
    实际上,经过ORACLE类型转换, 语句转化为:
    SELECT …  FROM EMP  WHERE  EMPNO = TO_NUMBER(‘123')
    幸运的是,类型转换没有发生在索引列上,索引的用途没有被改变.
    现在,假设EMP_TYPE是一个字符类型的索引列.
    SELECT …  FROM EMP  WHERE EMP_TYPE = 123
    这个语句被ORACLE转换为:
    SELECT …  FROM EMP  WHERETO_NUMBER(EMP_TYPE)=123
    因为内部发生的类型转换, 这个索引将不会被用到! 为了避免ORACLE对你的SQL进行隐式的类型转换, 最好把类型

    转换用显式表现出来. 注意当字符和数值比较时, ORACLE会优先转换数值类型到字符类型
    (31) 需要当心的WHERE子句:
    某些SELECT 语句中的WHERE子句不使用索引. 这里有一些例子.
    在下面的例子里, (1)‘!=' 将不使用索引. 记住, 索引只能告诉你什么存在于表中, 而不能告诉你什么不存在于

    表中. (2) ‘ ¦ ¦'是字符连接函数. 就象其他函数那样, 停用了索引. (3) ‘+'是数学函数. 就象其他数学函数

    那样, 停用了索引. (4)相同的索引列不能互相比较,这将会启用全表扫描.
    (32) a. 如果检索数据量超过30%的表中记录数.使用索引将没有显著的效率提高.
    b. 在特定情况下, 使用索引也许会比全表扫描慢, 但这是同一个数量级上的区别. 而通常情况下,使用索引比全

    表扫描要块几倍乃至几千倍!
    (33) 避免使用耗费资源的操作:
    带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句会启动SQL引擎
    执行耗费资源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要执行两次排序. 通常, 带有

    UNION, MINUS , INTERSECT的SQL语句都可以用其他方式重写. 如果你的数据库的SORT_AREA_SIZE调配得好, 使用

    UNION , MINUS, INTERSECT也是可以考虑的, 毕竟它们的可读性很强
    (34) 优化GROUP BY:
    提高GROUP BY 语句的效率, 可以通过将不需要的记录在GROUP BY 之前过滤掉.下面两个查询返回相同结果但第二

    个明显就快了许多.
    低效:
    SELECT JOB , AVG(SAL)
    FROM EMP
    GROUP by JOB
    HAVING JOB = ‘PRESIDENT'
    OR JOB = ‘MANAGER'
    高效:
    SELECT JOB , AVG(SAL)
    FROM EMP
    WHERE JOB = ‘PRESIDENT'
    OR JOB = ‘MANAGER'
    GROUP by JOB

    Posted Aug 26 2008, 10:55 PM by slash with 2 comment(s)
    Filed under:
More Posts Next page »
Copyright SDT, 2006-2009. All rights reserved.