中文字幕一区在线,久久97超碰色,蜜臀av国内免费精品久久久夜夜http://www.aygfsteel.com/TiGERTiAN/category/2224.html----TiGERTiANzh-cnSun, 06 Mar 2011 01:59:48 GMTSun, 06 Mar 2011 01:59:48 GMT60用U盤(pán)給新MacBook Air裝Win7http://www.aygfsteel.com/TiGERTiAN/archive/2011/03/06/345810.htmlTiGERTiANTiGERTiANSat, 05 Mar 2011 19:17:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2011/03/06/345810.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/345810.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2011/03/06/345810.html#Feedback0http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/345810.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/345810.htmlhttp://reviews.cnet.com/8301-31012_7-20020513-10355804.html

買(mǎi)到蘋(píng)果新款MacBook Air后大家最想干的事是什么?體驗(yàn)一下Mac OS X?事實(shí)告訴我們有幾乎一半的人第一件想要做的事是裝一個(gè)微軟的Windows系統(tǒng),但問(wèn)題是新版的MBA已經(jīng)沒(méi)有光驅(qū)了,這可如何是好?其實(shí)只需一個(gè)4GB的U盤(pán)便能搞定了。

首先你要準(zhǔn)備的是一臺(tái)攜帶光驅(qū)的Windows 7電腦,另外需要準(zhǔn)備一張Windows 7系統(tǒng)光盤(pán),還需要一個(gè)至少4GB的U盤(pán)(閃存+讀卡器不可),再準(zhǔn)備大概1個(gè)小時(shí),恩,差不多就行了。

一,在U盤(pán)上安裝Windows 7的詳細(xì)步驟

1.先將U盤(pán)插到Windows 7電腦上,接著用管理員身份啟動(dòng)CMD命令提示符。
(單擊開(kāi)始-搜索區(qū)域鍵入“CMD”-對(duì)著CMD右鍵選擇以管理員身份運(yùn)行)

2.打開(kāi)Diskpart,這是因?yàn)槠綍r(shí)我們看到的系統(tǒng)都是字母盤(pán)符,我們需要讓其變?yōu)樽钤嫉臄?shù)字。
(在CMD中輸入“diskpart”,然后按回車(chē),切換為DISKPART>之后輸入“list disk”按回車(chē))

3.現(xiàn)在屏幕上應(yīng)該會(huì)顯示出所有電腦上正在使用的儲(chǔ)存設(shè)備,根據(jù)容量就能看出哪一個(gè)是我們插入的U盤(pán),3856 MB那個(gè)當(dāng)然就是4GB的U盤(pán),讓我們選中它吧。
(在CMD中輸入“select disk 3”,U盤(pán)所在的位置)

接下來(lái)運(yùn)行的每一個(gè)步驟都不能有錯(cuò),另外你還需要確保一件事,那就是在第3步中你已經(jīng)成功選擇了U盤(pán)所在位置,不然其他硬盤(pán)數(shù)據(jù)可就清空了哦。
(在CMD中輸入“clean”清空U盤(pán),輸入“create partition primary”創(chuàng)建主分區(qū),輸入“select partition 1”選擇分區(qū)1,輸入“active”將選中分區(qū)標(biāo)記為活動(dòng)分區(qū),輸入“format fs=ntfs”格式化U盤(pán)的分區(qū)1,輸入“assign”給所選分區(qū)分配一個(gè)驅(qū)動(dòng)器號(hào),輸入“exit”退出Diskpart)(輸入“assign”時(shí)U盤(pán)有可能斷開(kāi)重新連接,這很正常,請(qǐng)不要驚慌)

4.我們需要將Windows 7的安裝內(nèi)容裝進(jìn)U盤(pán)內(nèi),請(qǐng)將你的Windows 7光盤(pán)插入到光驅(qū)內(nèi)(本次案例光驅(qū)所在位置為E盤(pán),U盤(pán)所在位置為H盤(pán),諸君請(qǐng)自動(dòng)替換適用盤(pán)符)
(插入光盤(pán)后在CMD中輸入“e: cd boot”改為光盤(pán)啟動(dòng),輸入“bootsect.exe /nt60 h:”使其安裝至U盤(pán))

5.接下來(lái)電腦會(huì)將Windows 7的安裝內(nèi)容全部?jī)?chǔ)存到U盤(pán)內(nèi),這需要等待10分鐘左右,在那之后U盤(pán)就可以在任何電腦上安裝Windows 7了,不過(guò)首選當(dāng)然需要在BIOS中設(shè)置為U盤(pán)啟動(dòng)。好了,U盤(pán)安裝完畢,接著讓我們拿出U盤(pán),拿出等候已久的新款MacBook Air。

二,接著使用MacBook Air支持U盤(pán)啟動(dòng)

雖然新款的MacBook Air支持用U盤(pán)安裝軟件,但這并不代表其可以直接在Mac中安裝Windows 7,畢竟你可不能直接在Mac中運(yùn)行Setup.exe對(duì)吧。所以我們還需要一個(gè)軟件,那就是rEFIt引導(dǎo)工具下載地址在這。下載完之后在Mac雙擊運(yùn)行rEFIt.mpkg即可安裝。

安裝完畢后,下次你重新啟動(dòng)MacBook Air的時(shí)候應(yīng)該就會(huì)出現(xiàn)如下畫(huà)面了,這樣就能選擇是使用Mac OS X啟動(dòng)還是其他系統(tǒng)啟動(dòng)了(當(dāng)然是Windows啦)。不過(guò)首先需要按住Option鍵在設(shè)置中將路徑改為U盤(pán)所在位置。

三,在MacBook Air上安裝Windows 7

要想在MacBook Air安裝Windows 7,我們還需要一個(gè)軟件,那就是Boot Camp Assistant,該軟件已經(jīng)在Mac OS X中自帶了,請(qǐng)各位同學(xué)自己尋找。運(yùn)行它之后,會(huì)提示你需要一個(gè)光盤(pán)驅(qū)動(dòng)器,忽略這條消息,點(diǎn)擊繼續(xù)。

下一步,你需要下載Windows支持的軟件,包括Boot Camp中的各種驅(qū)動(dòng)以及遠(yuǎn)程CD/DVD支持,都保存在一個(gè)文件夾中吧,接下來(lái)你會(huì)用到它的。

接下來(lái)Boot Camp Assistant會(huì)要求你縮小現(xiàn)在的Mac OS X上的分區(qū)大小以騰出空間安裝Windows,請(qǐng)謹(jǐn)慎選擇分區(qū)大小,畢竟兩個(gè)分區(qū)的文件格式將會(huì)改變。

現(xiàn)在你可以在MacBook Air上插入剛才制作好的Windows 7啟動(dòng)U盤(pán),然后重新啟動(dòng)MBA,按住Option鍵,確保路徑選擇是U盤(pán)啟動(dòng)。USB圖標(biāo)應(yīng)該很容易辨認(rèn)。

接下來(lái)就會(huì)進(jìn)入Windows 7的安裝過(guò)程,這一步相信大家都是熟門(mén)熟路了,只有一點(diǎn)需要注意的是,剛才通過(guò) Boot Camp Assistant劃分的分區(qū)是FAT32格式的,而Windows 7不能使用FAT32的分區(qū),所以選擇的時(shí)候需要重新格式化改為NTFS格式。

這是目前為止安裝Windows最快的電腦,新款MacBook Air一共花了才7分鐘就能將Windows 7安裝完畢,是不是覺(jué)得有點(diǎn)不可思議?進(jìn)入Windows 7后還有一件事需要干,那就是將剛才下載的Boot Camp中的Windows的驅(qū)動(dòng)程序安裝一遍,接著你就能使用Windows 7了!



TiGERTiAN 2011-03-06 03:17 發(fā)表評(píng)論
]]>
[轉(zhuǎn)]細(xì)看Google的35個(gè)開(kāi)源項(xiàng)目http://www.aygfsteel.com/TiGERTiAN/archive/2010/08/27/330028.htmlTiGERTiANTiGERTiANFri, 27 Aug 2010 00:51:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2010/08/27/330028.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/330028.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2010/08/27/330028.html#Feedback0http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/330028.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/330028.html

Sun號(hào)稱開(kāi)源方面的領(lǐng)路人之一,其實(shí)Google擁有的開(kāi)源項(xiàng)目也是十分豐富的。除了開(kāi)發(fā)方面的項(xiàng)目外,也有MySQL等方面的內(nèi)容。在這其中還包括新出的Google Go!

Google是支持開(kāi)源運(yùn)動(dòng)的最大公司之一,它們現(xiàn)在總共發(fā)布有超過(guò)500個(gè)的開(kāi)源項(xiàng)目(大部分都是利用它們的API來(lái)完成),本文將列舉一些有趣的開(kāi)源項(xiàng)目,其中很可能有不少你不知道的哦。

Google開(kāi)源助Web2.0開(kāi)發(fā) 不排斥專有深入了解Google開(kāi)源框架GWT Google開(kāi)源新舉動(dòng):Linux桌面1.1版上線(.. Mozilla、Google開(kāi)源合作遭美國(guó)稅局質(zhì)疑微軟擔(dān)心Google和蘋(píng)果超越Windows 8 文本文件處理:

◆Google CRUSH (Custom Reporting Utilities for SHell)

CRUSH是為命令行或shell scripts處理特定文字?jǐn)?shù)據(jù)而制作的一系列工具,這里有指南。

C++庫(kù)和源代碼:

◆Google Breakpad

一個(gè)開(kāi)源的多平臺(tái)崩潰報(bào)告系統(tǒng)。

◆Google GFlags

Gflags是一個(gè)命令行標(biāo)記的處理庫(kù),它可以替代“getopt()”,其內(nèi)置對(duì)C++的支持比如string。指南在此。

◆Google Glog

Glog庫(kù)可執(zhí)行應(yīng)用級(jí)的登陸,提供基于C++式的登陸API,可用于Linux、BSD和Windows。指南見(jiàn)此。

◆Google PerfTools

這個(gè)工具可讓開(kāi)發(fā)創(chuàng)建更強(qiáng)大的應(yīng)用程序,特別是那些用C++模版開(kāi)發(fā)的多線程應(yīng)用程序,包括TCMalloc, heap-checker, heap-profiler 和cpu-profiler。指南見(jiàn)此還有這里。

◆Google Sparse Hash

非常節(jié)省內(nèi)存的hash-map。指南見(jiàn)此。

◆Omaha – Google Update

Omaha,也就是Google Update,它可以保證你的軟件隨時(shí)升級(jí)到最新版本,目前很多Windows下的Google軟件都是用Omaha升級(jí)的,包括Google Chrome和Google Earth,當(dāng)然你也可以用于自己的應(yīng)用程序。指南看這里還有這里。

◆Protocol Buffers

Protocol Buffers是一種可擴(kuò)展編碼序列數(shù)據(jù)的方式,Google在幾乎所有內(nèi)部RPC協(xié)議和文件格式都使用了Protocol Buffers。指南見(jiàn)此。它可以用于很多語(yǔ)言而且被一些IDE所支持,比如NetBeans。

互聯(lián)網(wǎng):

◆Google Code Pretiffy

這是一個(gè)Javascript模塊和CSS文件,它可以讓HTML頁(yè)面里的部分源碼高亮顯示,支持C/C++, Java, Python, Ruby, PHP, VisualBasic, AWK, Bash, SQL, HTML, XML, CSS, JavaScript, Makefiles和部分Perl,不支持Smalltalk和所有的CAML。例子見(jiàn)此。

◆SpriteMe – easy “CSS spirtes”

SpriteMe使你可以更輕松的創(chuàng)造CSS Sprites(俗稱雪碧……)就是把網(wǎng)站要用到的圖片都堆在一張圖片里,用CSS控制調(diào)用哪個(gè)區(qū)域。它有一個(gè)自己的官網(wǎng)在這里。

◆Redacisaurus

Reducisaurus是一個(gè)壓縮CSS和JS文件的網(wǎng)絡(luò)服務(wù),基于YUI壓縮算法,運(yùn)行于App Engine。

◆JaikuEngine

JaikuEngine是一個(gè)運(yùn)行于App Engine的微博系統(tǒng),由jaiku.com運(yùn)營(yíng)。要查看移動(dòng)客戶端的源碼可以看這里,這里還有介紹。

◆Selector Shell

Selector Shell是一個(gè)基于瀏覽器的測(cè)試工具,它可以讓你看到CSS在不同瀏覽器里的樣式,用Javascript寫(xiě)的,你可以在這里測(cè)試。

◆Google Feed Server

Google Feed Server是一個(gè)開(kāi)源Atom發(fā)布協(xié)議服務(wù),基于Apache Abdera框架,允許開(kāi)發(fā)者快速為當(dāng)前數(shù)據(jù)源(比如數(shù)據(jù)庫(kù))配置feed。指南見(jiàn)這里和這里。

◆Melange, the Spice of Creation

這個(gè)項(xiàng)目的目標(biāo)是創(chuàng)建出一個(gè)適合開(kāi)源貢獻(xiàn)流程的框架,比如Google Summer of Code TM (GSoC)項(xiàng)目。使用這個(gè)框架你就可以用Google App Engine來(lái)運(yùn)行Google Summer of Code項(xiàng)目,和其它類(lèi)似項(xiàng)目比如Google Highly Open Participation TM Contest和GHOP。指南見(jiàn)此。

◆NameBench

它可以查找最快的DNS服務(wù)器給你的電腦用,在Mac OS X、Windows和UNIX系統(tǒng)下都有命令行也有用戶界面可以幫你測(cè)試,這是Google工程師用20%自由時(shí)間寫(xiě)出來(lái)的。

◆Rat Proxy

一個(gè)半自動(dòng)化的大型被動(dòng)網(wǎng)絡(luò)應(yīng)用安全審查工具,專為精確的探測(cè)而優(yōu)化,文檔在此。

◆TopDraw

Top Draw是一個(gè)圖形生成程序,使用簡(jiǎn)單的文字腳本,基于JavaScript編程語(yǔ)言,Top Draw可以創(chuàng)造出非常復(fù)雜和有趣的圖形。支持Mac OS 10.5以上系統(tǒng),使用XCode開(kāi)發(fā)。

◆etherpad

開(kāi)源的EtherPad,這是一個(gè)基于網(wǎng)絡(luò)的實(shí)時(shí)合作文檔編輯器,這個(gè)項(xiàng)目主要是為了演示代碼而開(kāi)發(fā),幫助那些想在自己服務(wù)器部署Etherpad的人使用,這里有如何安裝的指南。EtherPad使用JavaScript、Java和Comet服務(wù)器來(lái)建造實(shí)時(shí)協(xié)作服務(wù)。

◆Chromium

Chromium是開(kāi)源版的Chrome瀏覽器,Chromium的目標(biāo)是建立一個(gè)新一代的強(qiáng)大網(wǎng)絡(luò)應(yīng)用程序,它與Chrome有很多不同之處。這里有指導(dǎo)如何在Linux上編譯Chromium。

◆V8 Google’s open source JavaScript engine

V8是Google的開(kāi)源JavaScript引擎,用C++寫(xiě)成,用于Chrome瀏覽器之上。V8使用ECMAScript的ECMA-262第三版可運(yùn)行于Windows XP、vista、Mac OS 10.5和使用IA-32或ARM處理器的Linux。V8可獨(dú)立運(yùn)行也可嵌入到任何C++程序里使用,這里有指南。

◆Chromium OS

Chromium OS是開(kāi)源版的Chrome OS操作系統(tǒng),提供快速、簡(jiǎn)單而安全的網(wǎng)絡(luò)體驗(yàn),源碼在此。

◆Android

Android是第一個(gè)免費(fèi)、開(kāi)源而且可完全自定義的移動(dòng)平臺(tái),提供完整的堆棧:一個(gè)操作系統(tǒng)、中間件和重要的一用應(yīng)用,它包含豐富的API可以讓第三方開(kāi)發(fā)者開(kāi)發(fā)出強(qiáng)大的應(yīng)用程序。

MySQL工具:

◆Google MySQL Tools

各種管理、維護(hù)和改進(jìn)MySQL數(shù)據(jù)庫(kù)性能的工具,由Google編寫(xiě),包括:

◆mypgrep.py:一個(gè)類(lèi)似pgrep的工具來(lái)管理MySQL連接

compact_innodb.py:可導(dǎo)出和重載所有表格的密集型innodb數(shù)據(jù)文件

◆Google mMAIM

mMAIM的目標(biāo)是對(duì)MySQL的監(jiān)控和分析更簡(jiǎn)單,且可以和任何環(huán)境整合使用。它可顯示主/從同步狀態(tài),一些性能狀態(tài),可以返回大量“show”命令的狀態(tài)等等。

其它:

◆Stressful Application Test (stressapptest)

Stressful Application Test試圖讓來(lái)自處理器和I/O到內(nèi)存的數(shù)據(jù)盡量隨機(jī)化,以創(chuàng)造出模擬現(xiàn)實(shí)的環(huán)境來(lái)測(cè)試現(xiàn)在的硬件設(shè)備是否穩(wěn)定,Google就在使用它,現(xiàn)在是Apache 2.0許可,這里有介紹、安裝向?qū)Ш椭改稀?/p>

◆Pop and IMAP Troubleshooter

它用于診斷并解決客戶端到郵件服務(wù)器的連接問(wèn)題。

◆OpenDuckBill

Openduckbill是一個(gè)Linux下簡(jiǎn)單的命令行備份工具,可用于監(jiān)視文件/目錄在有變化后是否標(biāo)記為備份,并傳輸這些變化到本地備份目錄、遠(yuǎn)程N(yùn)FS導(dǎo)出分卷或是用rsync命令導(dǎo)出到遠(yuǎn)程SSH服務(wù)器。見(jiàn)安裝向?qū)А?/p>

◆ZXing

ZXing(發(fā)音類(lèi)似Zebra crossing)是Java的開(kāi)源多格式1D/2D條碼圖像處理庫(kù),目的是使用內(nèi)置在手機(jī)上的攝像頭拍照并對(duì)條碼進(jìn)行解碼,而不必與服務(wù)器通訊,它被用于Android系統(tǒng)。這里有向?qū)Ш椭С值脑O(shè)備列表。

◆Tesseract OCR Engine

Tesseract OCR引擎是1995年UNLV Accuracy測(cè)試的前三名之一,在1995和2006年之間它的進(jìn)展不大,但依然是當(dāng)前精度最高的OCR引擎。這個(gè)源碼可讀取二進(jìn)制、灰階或彩色圖片并輸出文字,內(nèi)置一個(gè)TIFF閱讀器可讀取非壓縮的TIFF文件,增加libtiff后也可讀取壓縮圖片。指南和問(wèn)答。

◆Neatx – Open Source NX server

Neatx是一個(gè)開(kāi)源NX服務(wù),類(lèi)似NoMachine公司商業(yè)的NX服務(wù)。NX協(xié)議比VNX更強(qiáng)大,它們的區(qū)別主要在:

NX是X11客戶端所以不會(huì)發(fā)送位圖

NX可兼容X、VNC和Windows版的Remote Desktop

NX可緩存數(shù)據(jù)

NX安裝簡(jiǎn)單

另外一個(gè)可選的項(xiàng)目可以看看Google的FreeNx。

◆PSVM

它是這個(gè)文件的代碼,這是一個(gè)SVM的“支持所有核心”的版本,可多機(jī)并行運(yùn)行,實(shí)例見(jiàn)此。

◆Google GO

Google開(kāi)發(fā)的新編程語(yǔ)言。

◆The Google Collections Library for Java

這是一系列與Java 5以及更高版本有關(guān)的庫(kù),Google花錢(qián)給買(mǎi)過(guò)來(lái)了。

◆Google styleguIDE
每個(gè)主流的開(kāi)源項(xiàng)目都有它自己的向?qū)问剑热缫幌盗械难菔敬a。如果這些代碼都按照“Style”的形式來(lái)演示,會(huì)更友好。

http://www.oulan.com/w/2009/12/29/google-opensource-project/



TiGERTiAN 2010-08-27 08:51 發(fā)表評(píng)論
]]>
幾個(gè)HTML 5的應(yīng)用,太酷了。。。http://www.aygfsteel.com/TiGERTiAN/archive/2010/06/27/324592.htmlTiGERTiANTiGERTiANSun, 27 Jun 2010 02:14:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2010/06/27/324592.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/324592.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2010/06/27/324592.html#Feedback2http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/324592.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/324592.html

HTML5 canvas has changed the way javascript used to be. We're now able to achieve complex animation however we do need a powerful browser to interpret it. The following is 8 new HTML5 + Javascript animation effect you probably have not seen before! Play with each of them, you'll be blown away by the creativity and robustness of HTML5 canvas!

I like Tunnelers and Bomomo the most! You've to try Bomomo, it's fun!

  • Tunnelers

    Processing to sketch out the animation and form, then rewrote it from scratch in Javascript / Canvas HTML5. Simply awesome and amazing!
  • The Mesmerizer

    A simple experiment with a 16x16 grid of squares. Moving the pointer over the squares causes colours to appear and bleed into adjacent squares with an effect that is quite mesmerizing. Try clicking the corner squares or typing on the keyboard for additional effects.
  • Burn

    A simple test of local pixel-based modifications of an HTML5 canvas drawing area. Hold a mouse button down for a "slide down" effect.
  • Cheloniidae Live

    This project is pure JavaScript, though it uses a library that dynamically recompiles JavaScript functions to implement operator overloading. (This is done inside the browser, not ahead-of-time.) Pretty fun, you can alter the code and make your own drawing!
  • Canvas 3D engine

    A 3D engine on canvas, currently supporting a collection of basic prefab shapes, textures, and basic interaction. Current work includes collada (import) support, and improved texture and lighting features.
  • Bomomo

    Best give it a try yourself... just use your mouse & mouse button! All of the sudden, I have become artistic. :)
  • DDD

    Move mouse to draw, drag to rotate. This one is pretty fun, you have to try to see it.
  • Plasma Tree

    Inspired by nature - a visual presentation of a algorithm


TiGERTiAN 2010-06-27 10:14 發(fā)表評(píng)論
]]>
在MFC SDI程序中使用SDL http://www.aygfsteel.com/TiGERTiAN/archive/2010/03/27/316699.htmlTiGERTiANTiGERTiANSat, 27 Mar 2010 07:26:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2010/03/27/316699.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/316699.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2010/03/27/316699.html#Feedback0http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/316699.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/316699.html快樂(lè)蝦

http://blog.csdn.net/lights_joy/

lights@hb165.com

 

本文適用于

SDL-1.2.13

vs2008

Windows XP

 

歡迎轉(zhuǎn)載,但請(qǐng)保留作者信息


SDL的設(shè)計(jì)并沒(méi)有考慮到要和MFC相結(jié)合,但是既然它要在windows的系統(tǒng)上運(yùn)行,必然需要使用Windows提供的API。為了在MFC SDI中使用SDL,首先想到的就是替換SDL創(chuàng)建的窗口,改為使用MFC提供的窗口。

想想在Windows下要?jiǎng)?chuàng)建窗口需要使用的API必然是CreateWindow,在SDL代碼中搜,很容易發(fā)現(xiàn)了這樣一段代碼:

int DIB_CreateWindow(_THIS)

{

     char *windowid = SDL_getenv("SDL_WINDOWID");

 

     SDL_RegisterApp(NULL, 0, 0);

 

     SDL_windowid = (windowid != NULL);

     if ( SDL_windowid ) {

#if defined(_WIN32_WCE) && (_WIN32_WCE < 300)

         /* wince 2.1 does not have strtol */

         wchar_t *windowid_t = SDL_malloc((SDL_strlen(windowid) + 1) * sizeof(wchar_t));

         MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, windowid, -1, windowid_t, SDL_strlen(windowid) + 1);

         SDL_Window = (HWND)wcstol(windowid_t, NULL, 0);

         SDL_free(windowid_t);

#else

         SDL_Window = (HWND)SDL_strtoull(windowid, NULL, 0);

#endif

         if ( SDL_Window == NULL ) {

              SDL_SetError("Couldn't get user specified window");

              return(-1);

         }

 

         /* DJM: we want all event's for the user specified

              window to be handled by SDL.

          */

         userWindowProc = (WNDPROCTYPE)GetWindowLongPtr(SDL_Window, GWLP_WNDPROC);

         SetWindowLongPtr(SDL_Window, GWLP_WNDPROC, (LONG_PTR)WinMessage);

     } else {

         SDL_Window = CreateWindow(SDL_Appname, SDL_Appname,

                        (WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX),

                        CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, NULL, NULL, SDL_Instance, NULL);

         if ( SDL_Window == NULL ) {

              SDL_SetError("Couldn't create window");

              return(-1);

         }

         ShowWindow(SDL_Window, SW_HIDE);

     }

 

     /* JC 14 Mar 2006

         Flush the message loop or this can cause big problems later

         Especially if the user decides to use dialog boxes or assert()!

     */

     WIN_FlushMessageQueue();

 

     return(0);

}

注意到前面的if條件判斷使用了SDL_windowid,如果這個(gè)變量不為0,那么SDL是不會(huì)創(chuàng)建新窗口的!而這個(gè)值直接來(lái)自于SDL_WINDOWID這個(gè)環(huán)境變量!以此推斷,只要在調(diào)用SDL_Init之前設(shè)置好SDL_WINDOWID這個(gè)環(huán)境變量,那么SDL將可以使用我們提供的窗口。

下面修改SDL提供的testwin示例,使之在MFC SDI環(huán)境下運(yùn)行。

1.1    工程創(chuàng)建
直接使用VS2008的向?qū)梢粋€(gè)叫sdi_sdl的MFC工程,選擇SDI類(lèi)型。

1.2    拋棄SDLmain.lib
在SDL提供的測(cè)試用例中,都要使用SDLmain.lib,這個(gè)lib文件實(shí)現(xiàn)了Winmain和main這兩個(gè)入口函數(shù),在這兩個(gè)函數(shù)中進(jìn)行了一些SDL的初始化工作。而在MFC下,我們不需要自己寫(xiě)WinMain,因此直接拋棄SDLmain.lib,將相關(guān)的代碼轉(zhuǎn)移到Csdi_sdlView的OnCreate函數(shù)中。之所以選擇Csdi_sdlView::OnCreate是因?yàn)榇藭r(shí)窗口已經(jīng)創(chuàng)建,可以取到這個(gè)窗口的Handle,從而可以在SDL初始化之前設(shè)置好Window id。

int Csdi_sdlView::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

     if (CView::OnCreate(lpCreateStruct) == -1)

         return -1;

 

    char variable[256];

     sprintf(variable, "SDL_WINDOWID=0x%lx", this->GetSafeHwnd());

    SDL_putenv(variable);

     SDL_WinMain(AfxGetApp()->m_hInstance, NULL, AfxGetApp()->m_lpCmdLine, SW_MAXIMIZE);

     return 0;

}

1.3    SDL_WinMain
這個(gè)函數(shù)來(lái)源于SDLmain中的WinMain函數(shù),只是刪除了一些不必要的代碼:

/* This is where execution begins [windowed apps] */

int Csdi_sdlView::SDL_WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)

{

     HINSTANCE handle;

     char **argv;

     int argc;

     char *cmdline;

     char *bufp;

     size_t nLen;

 

     /* Start up DDHELP.EXE before opening any files, so DDHELP doesn't

        keep them open.  This is a hack.. hopefully it will be fixed

        someday.  DDHELP.EXE starts up the first time DDRAW.DLL is loaded.

      */

     handle = LoadLibrary(TEXT("DDRAW.DLL"));

     if ( handle != NULL ) {

         FreeLibrary(handle);

     }

 

     /* Grab the command line */

     bufp = GetCommandLine();

     nLen = SDL_strlen(bufp)+1;

     cmdline = SDL_stack_alloc(char, nLen);

     SDL_strlcpy(cmdline, bufp, nLen);

 

     /* Parse it into argv and argc */

     argc = ParseCommandLine(cmdline, NULL);

     argv = SDL_stack_alloc(char*, argc+1);

     ParseCommandLine(cmdline, argv);

 

     /* Run the main program (after a little SDL initialization) */

     SDL_PreMain(argc, argv);

 

     /* Hush little compiler, don't you cry... */

     return 0;

}

1.4    SDL_PreMain
這個(gè)函數(shù)來(lái)源于sdlmain.lib中的main函數(shù):

/* This is where execution begins [console apps] */

int Csdi_sdlView::SDL_PreMain(int argc, char *argv[])

{

     size_t n;

     char *bufp, *appname;

     int status;

 

     /* Get the class name from argv[0] */

     appname = argv[0];

     if ( (bufp=SDL_strrchr(argv[0], '\\')) != NULL ) {

         appname = bufp+1;

     } else

     if ( (bufp=SDL_strrchr(argv[0], '/')) != NULL ) {

         appname = bufp+1;

     }

 

     if ( (bufp=SDL_strrchr(appname, '.')) == NULL )

         n = SDL_strlen(appname);

     else

         n = (bufp-appname);

 

     bufp = SDL_stack_alloc(char, n+1);

     SDL_strlcpy(bufp, appname, n+1);

     appname = bufp;

 

     ///* Load SDL dynamic link library */

     //if ( SDL_Init(SDL_INIT_NOPARACHUTE) < 0 ) {

     //   ShowError("WinMain() error", SDL_GetError());

     //   return(FALSE);

     //}

 

     /* Sam:

        We still need to pass in the application handle so that

        DirectInput will initialize properly when SDL_RegisterApp()

        is called later in the video initialization.

      */

     SDL_SetModuleHandle(GetModuleHandle(NULL));

 

     /* Run the application main() code */

     status = SDL_main(argc, argv);

 

     /* Hush little compiler, don't you cry... */

     return 0;

}

1.5    SDL_main
這個(gè)函數(shù)來(lái)自于testwin示例中的main函數(shù),只是在末尾刪除了SDL_Quit這樣的退出語(yǔ)句。

int Csdi_sdlView::SDL_main(int argc, char *argv[])

{

     SDL_Surface *screen;

 

     /* Options */

     int speedy, flip, nofade;

     int delay;

     int w, h;

     int desired_bpp;

     Uint32 video_flags;

 

     /* Set default options and check command-line */

     speedy = 0;

     flip = 0;

     nofade = 0;

     delay = 1;

 

     RECT rc;

     this->GetWindowRect(&rc);

     w = rc.right;

     h = rc.bottom;

     desired_bpp = 0;

     video_flags = 0;

     if ( SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) < 0 ) {

         ShowError("Couldn't initialize SDL", SDL_GetError());

         return(1);

     }

 

     /* Initialize the display */

     screen = SDL_SetVideoMode(w, h, desired_bpp, video_flags);

     if ( screen == NULL ) {

         ShowError("Couldn't set %dx%dx%d video mode: %s\n", "");

         return (1);

     }

 

     DrawPict(screen, argv[1], speedy, flip, nofade);

//   SDL_Delay(delay*1000);

//   SDL_Quit();

     return(0);

}

至此,這個(gè)程序就可以正常運(yùn)行了,但是它的大小還不能隨主窗口的變化而變化。為此還需要響應(yīng)WM_SIZE:

void Csdi_sdlView::OnSize(UINT nType, int cx, int cy)

{

     SDL_Surface *screen;

     screen = SDL_SetVideoMode(cx, cy, 0, 0);

     DrawPict(screen, NULL, 0, 0, 0);

}

1.6    SDL_Quit
在程序退出的時(shí)候,需要調(diào)用SDL_Quit進(jìn)行一些清理的工作,原來(lái)想將這個(gè)工作放在Cview::OnDestroy中完成,但是發(fā)現(xiàn)這樣有很多的內(nèi)存泄漏。最后將其放在Csdi_sdlApp:ExitInstance中完成:

BOOL Csdi_sdlApp::ExitInstance()

{

     SDL_Quit();

     return TRUE;

}

即便是這樣,仍然有一處內(nèi)存泄漏,原因不明:

Detected memory leaks!

Dumping objects ->

{98} normal block at 0x003D37C0, 21 bytes long.

 Data: <0x40784 OWID 0x4> 30 78 34 30 37 38 34 00 4F 57 49 44 00 30 78 34

Object dump complete.

 

本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/lights_joy/archive/2009/04/04/4049123.aspx



TiGERTiAN 2010-03-27 15:26 發(fā)表評(píng)論
]]>
轉(zhuǎn):SDL在windows mobile上的使用經(jīng)驗(yàn)http://www.aygfsteel.com/TiGERTiAN/archive/2010/03/27/316698.htmlTiGERTiANTiGERTiANSat, 27 Mar 2010 07:20:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2010/03/27/316698.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/316698.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2010/03/27/316698.html#Feedback0http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/316698.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/316698.html
作者:藍(lán)梅居士

出處:http://www.zixundao.com/thread-1672-1-4.html

SDL是一種既是開(kāi)源的,也是跨平臺(tái)的多媒體開(kāi)發(fā)包,在各種平臺(tái)上應(yīng)用很廣,經(jīng)常和FFMPEG等解碼器同時(shí)使用。對(duì)于在windows mobile等缺乏通用播放器的平臺(tái)來(lái)說(shuō),是一種很好的選擇。
網(wǎng)上很多代碼,介紹SDL的用法,主要需要注意的情況有3種:
第一: 綁定播放窗口問(wèn)題:
char sdl_var[128]; 
   sprintf(sdl_var, "SDL_WINDOWID=0x%lx", m_hWnd );//主窗口句柄
   SDL_putenv(sdl_var);   char *myvalue = SDL_getenv("SDL_WINDOWID");
這端代碼,需要放在播放窗口初始化之前,否則,容易造成全屏。

第二:SDL本身響應(yīng)窗口菜單的問(wèn)題
SDL官方提供以下代碼響應(yīng)窗口菜單:
while(1) {
        SDL_WaitEvent(&event);
   {
     case 

   }

   }
但也可以我們?cè)诔绦蚶锩嬷匦鲁跏蓟@示新菜單,就可以不用這種辦法,直接用我們自己的程序響應(yīng)菜單。
第三:關(guān)于縮放:
網(wǎng)上很多代碼容易誤導(dǎo)人。
把SDL_SetVideoMode和SDL_CreateYUVOverlay的參數(shù)都設(shè)置成一樣的,這種辦法導(dǎo)致屏幕圖象縮放顯示不正常,正確的方法應(yīng)該是:SDL_SetVideoMode的參數(shù)顯示新的顯示大小,而SDL_CreateYUVOverlay(只需建立一次,SDL_SetVideoMode更換時(shí),不需要跟著更換)的參數(shù)則是圖片的原始大小 。
而在SDL_DisplayYUVOverlay中用到的SDL_Rect,大小則和SDL_SetVideoMode一樣,也就是新的顯示大小。

本文原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處和作者,謝謝。

作者:藍(lán)梅居士

出處:http://www.zixundao.com/thread-1672-1-4.html


本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/baishuren/archive/2009/10/13/4665153.aspx



TiGERTiAN 2010-03-27 15:20 發(fā)表評(píng)論
]]>
windows mobile用sdl做視頻顯示(附sdl lib for mobile) http://www.aygfsteel.com/TiGERTiAN/archive/2010/03/27/316697.htmlTiGERTiANTiGERTiANSat, 27 Mar 2010 07:19:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2010/03/27/316697.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/316697.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2010/03/27/316697.html#Feedback0http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/316697.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/316697.html使用的sdl庫(kù)及頭文件已上傳至http://download.csdn.net/source/1775400

sdl是開(kāi)源的跨平臺(tái)多媒體開(kāi)發(fā)包, 主要用在快速的視頻格式轉(zhuǎn)換和顯示,以及封裝了鍵盤(pán)/鼠標(biāo)/手機(jī)按鍵/觸屏等各個(gè)平臺(tái)的用戶交互響應(yīng),廣泛應(yīng)用在游戲,多媒體播放器等應(yīng)用中。目前支持windows,linux, wince,mac os,非正式地支持symbian os。

  一般視頻解碼器輸出圖像的是yuv420格式,而屏幕顯示大都是rgb42規(guī)格。 mobile上畫(huà)面顯示一般經(jīng)由decode>>yuv2rgb>>scale>>directDraw直接寫(xiě)屏。 采用SDL可以加速這一過(guò)程,且更方便響應(yīng)全屏縮放等用戶操作。

  下面介紹mobile上用sdl來(lái)加速顯示視頻的全過(guò)程。

  1. 把主對(duì)話框的窗口句柄傳給顯示類(lèi)。

  2. 初始化sdl,設(shè)置顯示視頻模式

 SDL_Surface     *screen;
 SDL_Rect        sdl_rect;
 SDL_Overlay     *bmp;

bool InitSdl()

{

   char sdl_var[64]; 
   sprintf(sdl_var, "SDL_WINDOWID=0x%lx", m_hWnd);//主窗口句柄
   SDL_putenv(sdl_var);

   char *myvalue = SDL_getenv("SDL_WINDOWID");

  

   atexit(SDL_Quit);

   //根據(jù)解碼后的視頻尺寸來(lái)初始化sdl

   screen = SDL_SetVideoMode(ImageWidth, ImageHeight, 0,SDL_ANYFORMAT|SDL_RESIZABLE|SDL_DOUBLEBUF|SDL_HWSURFACE);//注意用到的參數(shù)flags
 if(!screen)
 {
      TRACE(L"error SDL SetVideoMode!");
      return false;
 }

 bmp = SDL_CreateYUVOverlay(ImageWidth, ImageHeight, ,SDL_YV12_OVERLAY,screen);
 if(!bmp)
 {
      TRACE(L"error SDL CreateYUVOverlay!");
      return false;
 }

 return TRUE;

  

}

  3. 在屏幕上繪制像素

void SdlDisplayFrame()
{


 AVPicture pict;

 pict.data[0] = bmp->pixels[0];
 pict.data[1] = bmp->pixels[2];
 pict.data[2] = bmp->pixels[1];

 pict.linesize[0] = bmp->pitches[0];
 pict.linesize[1] = bmp->pitches[2];
 pict.linesize[2] = bmp->pitches[1];

 //pFrame是ffmpeg存放解碼后yuv數(shù)據(jù)的struct

 img_convert(&pict, PIX_FMT_YUV420P, (AVPicture *)pFrame, pContext->pix_fmt, iImage_Width, iImage_Height);
 

 SDL_LockSurface(screen);                
 SDL_LockYUVOverlay(bmp);


 {
  sdl_rect.x = 0;
  sdl_rect.y = 0;
  sdl_rect.w = m_width;  //pContext->width;
  sdl_rect.h = m_height;  //pContext->height;

 }

 SDL_UnlockYUVOverlay(bmp);                     
 SDL_UnlockSurface(screen);              

 SDL_DisplayYUVOverlay(bmp, &sdl_rect);//顯示圖片到屏幕


}

 

  4. 事件輪詢和按鍵響應(yīng)

void SdlEvent()
{

 SDL_Event event;

 while ( SDL_PollEvent(&event))
 {
  TRACE(L"========SDL VIDEORESIZE EVENT START");
  switch (event.type)
  {
  case SDL_VIDEORESIZE:
   // 響應(yīng)圖像縮放事件

   if (m_iImage_Width == Image_Primary_Width &&  m_iImage_Height == Image_Primary_Height)
   {
    TRACE(L"event.resize.w = %d",event.resize.w);
    TRACE(L"event.resize.h = %d",event.resize.h);
    m_width = event.resize.w;        
    m_height = event.resize.h;

    SDL_FreeYUVOverlay(bmp);
    bmp = NULL;
    SDL_FreeSurface(screen);
    screen = NULL;

    if (!IsQcif)
    {
     m_height =  m_height * 2;
     m_width = m_width * 2;
     screen = SDL_SetVideoMode(MobileFullScreenWidth,MobileFullScreenHeight,
      0,SDL_ANYFORMAT|SDL_RESIZABLE|SDL_DOUBLEBUF|SDL_HWSURFACE);
       if (screen == NULL)
     {
      TRACE(L"screen= NULL");
      return;
     }

     bmp = SDL_CreateYUVOverlay(MobileFullScreenWidth,MobileFullScreenHeight,
      SDL_YV12_OVERLAY,screen);
     if (bmp == NULL)
     {
      TRACE(L"bm1 = NULL");
      return;
     }
     IsQcif = true;
    }
    else
    {
     screen = SDL_SetVideoMode(Image_Primary_Width,Image_Primary_Height,
      0,SDL_ANYFORMAT|SDL_RESIZABLE|SDL_DOUBLEBUF|SDL_HWSURFACE);
     if (screen == NULL)
     {
      TRACE(L"screen1= NULL");
      return;
     }

     bmp = SDL_CreateYUVOverlay(Image_Primary_Width,Image_Primary_Height,
      SDL_YV12_OVERLAY,screen);
     if (bmp == NULL)
     {
      TRACE(L"bm1 = NULL");
      return;
     }

     IsQcif = false;
    }

    break;               
   }

  case SDL_MOUSEBUTTONDOWN:

    //響應(yīng)觸屏筆點(diǎn)擊事件
     break;
   }
  case SDL_QUIT:
   //響應(yīng)退出事件
   break;
  default:
   break;
  }
 }
}


本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/lius1984/archive/2009/08/07/4420786.aspx

1.http://www.libsdl.org/ SDL官方網(wǎng)站
2.http://lazyfoo.net/SDL_tutorials/index.php 非常好的一個(gè)SDL教程(英文)
3.http://www.cppblog.com/lf426/category/6107.html SDL入門(mén)教程(中文)
4.http://popul.jqcq.com/computer/2006-05/1147920342.html
5.一個(gè)演示SDL顯示YUV視頻例子 SDL_VC8_DEMO.rar


TiGERTiAN 2010-03-27 15:19 發(fā)表評(píng)論
]]>
DirectX介紹http://www.aygfsteel.com/TiGERTiAN/archive/2010/03/27/316695.htmlTiGERTiANTiGERTiANSat, 27 Mar 2010 07:15:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2010/03/27/316695.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/316695.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2010/03/27/316695.html#Feedback0http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/316695.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/316695.html DirectX是一種應(yīng)用程序接口,它可讓以windows為平臺(tái)的游戲或多媒體程序獲得更高的執(zhí)行效率,加強(qiáng)3d圖形和聲音效果,并提供設(shè)計(jì)人員一個(gè)共同的硬件驅(qū)動(dòng)標(biāo)準(zhǔn),
讓游戲開(kāi)發(fā)者不必為每一品牌的硬件來(lái)寫(xiě)不同的驅(qū)動(dòng)程序,也降低用戶安裝及設(shè)置硬件的復(fù)雜度。這樣說(shuō)是不是有點(diǎn)不太明白,其實(shí)從字面意義上說(shuō),Direct就是直接
的意思,而后邊的X則代表了很多的意思,從這一點(diǎn)上我們就可以看出 DirectX的出現(xiàn)就是為了為眾多軟件提供直接服務(wù)的。

DirectX是由很多API組成的,按照性質(zhì)分類(lèi),可以分為四大部分,顯示部分、聲音部分、輸入部分和網(wǎng)絡(luò)部分。

顯示部分擔(dān)任圖形處理的關(guān)鍵,分為Direct Draw(DDraw)和Direct 3D(D3D),前者主要負(fù)責(zé)2D圖像加速。它包括很多方面:我們播放mpg、DVD電影、看圖、玩小游戲等等都是用的DDraw,你可以把它理解成所有劃線的部分都是用的DDraw。后者則主要負(fù)責(zé)3D效果的顯示,比如CS中的場(chǎng)景和人物、FIFA中的人物等等,都是使用了DirectX的Direct 3D。

聲音部分中最主要的API是DirectSound,除了播放聲音和處理混音之外,還加強(qiáng)了3d音效,并提供了錄音功能。我們前面所舉的聲卡兼容的例子,就是利用了DirectSound來(lái)解決的。

輸入部分Direct Input可以支持很多的游戲輸入設(shè)備,它能夠讓這些設(shè)備充分發(fā)揮最佳狀態(tài)和全部功能。除了鍵盤(pán)和鼠標(biāo)之外還可以連接手柄、搖桿、模擬器等。

網(wǎng)絡(luò)部分DirectPlay主要就是為了具有網(wǎng)絡(luò)功能游戲而開(kāi)發(fā)的,提供了多種連接方式,TPC/IP,IPX,Modem,串口等等,讓玩家可以用各種連網(wǎng)方式來(lái)進(jìn)行對(duì)戰(zhàn),此外也提供網(wǎng)絡(luò)對(duì)話功能及保密措施。

 DirectX并不是一個(gè)單純的圖形API,它是由微軟公司開(kāi)發(fā)的用途廣泛的API,它包含有Direct Graphics(Direct 3D+Direct Draw)、Direct Input、Direct Play、Direct Sound、Direct Show、Direct Setup、Direct Media Objects等多個(gè)組件,它提供了一整套的多媒體接口方案。只是其在3D圖形方面的優(yōu)秀表現(xiàn),讓它的其它方面顯得暗淡無(wú)光。DirectX開(kāi)發(fā)之初是為了彌補(bǔ)Windows 3.1系統(tǒng)對(duì)圖形、聲音處理能力的不足,而今已發(fā)展成為對(duì)整個(gè)多媒體系統(tǒng)的各個(gè)方面都有決定性影響的接口。 DirectX 是一組低級(jí)“應(yīng)用程序編程接口 (API)”,可為 Windows 程序提供高性能的硬件加速多媒體支持。Windows 支持 DirectX 8.0,它能增強(qiáng)計(jì)算機(jī)的多媒體功能。使用 DirectX 可訪問(wèn)顯卡與聲卡的功能,從而使程序可提供逼真的三維 (3D) 圖形與令人如醉如癡的音樂(lè)與聲音效果。 DirectX 使程序能夠輕松確定計(jì)算機(jī)的硬件性能,然后設(shè)置與之匹配的程序參數(shù)。該程序使得多媒體軟件程序能夠在基于 Windows 的具有 DirectX 兼容硬件與驅(qū)動(dòng)程序的計(jì)算機(jī)上運(yùn)行,同時(shí)可確保多媒體程序能夠充分利用高性能硬件。 DirectX 包含一組 API,通過(guò)它能訪問(wèn)高性能硬件的高級(jí)功能,如三維圖形加速芯片和聲卡。這些 API 控制低級(jí)功能(其中包括二維 (2D) 圖形加速)、支持輸入設(shè)備(如游戲桿、鍵盤(pán)和鼠標(biāo))并控制著混音及聲音輸出。構(gòu)成 DirectX 的下列組件支持低級(jí)功能: Microsoft DirectDraw Microsoft DirectDraw API 支持快速訪問(wèn)計(jì)算機(jī)視頻適配器的加速硬件功能。它支持在所有視頻適配器上顯示圖形的標(biāo)準(zhǔn)方法,并且使用加速驅(qū)動(dòng)程序時(shí)可以更快更直接地訪問(wèn)。DirectDraw 為程序(如游戲和二維圖形程序包)以及 Windows 系統(tǒng)組件(如數(shù)字視頻編解碼器)提供了一種獨(dú)立于設(shè)備之外的方法來(lái)訪問(wèn)特定顯示設(shè)備的功能,而不要求用戶提供設(shè)備功能的其它信息。

Microsoft Direct3D Microsoft Direct3D API (Direct3D) :為大多數(shù)新視頻適配器內(nèi)置的 3-D 調(diào)色功能提供界面。Direct3D 是一種低級(jí)的 3-D API,它為軟件程序提供一種獨(dú)立于設(shè)備之外的方法以便與加速器硬件進(jìn)行有效而強(qiáng)大的通信。Direct3D 包含專用 CPU 指令集支持,從而可為新型計(jì)算機(jī)提供進(jìn)一步加速支持。

Microsoft DirectSound Microsoft DirectSound API :為程序和音頻適配器的混音、聲音播放和聲音捕獲功能之間提供了鏈接。DirectSound 為多媒體軟件程序提供低延遲混合、硬件加速以及直接訪問(wèn)聲音設(shè)備等功能。維護(hù)與現(xiàn)有設(shè)備驅(qū)動(dòng)程序的兼容性時(shí)提供該功能。

Microsoft DirectMusic Microsoft DirectMusic API :是 DirectX 的交互式音頻組件。與捕獲和播放數(shù)字聲音樣本的 DirectSound API 不同,DirectMusic 處理數(shù)字音頻以及基于消息的音樂(lè)數(shù)據(jù),這些數(shù)據(jù)是通過(guò)聲卡或其內(nèi)置的軟件合成器轉(zhuǎn)換成數(shù)字音頻的。DirectMusic API 支持以“樂(lè)器數(shù)字界面 (MIDI)”格式進(jìn)行輸入,也支持壓縮與未壓縮的數(shù)字音頻格式。DirectMusic 為軟件開(kāi)發(fā)人員提供了創(chuàng)建令人陶醉的動(dòng)態(tài)音軌的能力,以響應(yīng)軟件環(huán)境中的各種更改,而不只是用戶直接輸入更改。

Microsoft DirectInput Microsoft DirectInput API :為游戲提供高級(jí)輸入功能并能處理游戲桿以及包括鼠標(biāo)、鍵盤(pán)和強(qiáng)力反饋游戲控制器在內(nèi)的其它相關(guān)設(shè)備的輸入。

Microsoft DirectPlay Microsoft DirectPlay API: 支持通過(guò)調(diào)制解調(diào)器、Internet 或局域網(wǎng)連接游戲。DirectPlay 簡(jiǎn)化了對(duì)通信服務(wù)的訪問(wèn),并提供了一種能夠使游戲彼此通信的方法而不受協(xié)議或聯(lián)機(jī)服務(wù)的限制。DirectPlay 提供了多種游說(shuō)服務(wù),可簡(jiǎn)化多媒體播放器游戲的初始化,同時(shí)還支持可靠的通信協(xié)議以確保重要游戲數(shù)據(jù)在網(wǎng)絡(luò)上不會(huì)丟失。DirectPlay 8.0 的新功能即支持通過(guò)網(wǎng)絡(luò)進(jìn)行語(yǔ)音通信,從而可大大提高基于多媒體播放器小組的游戲的娛樂(lè)性,同時(shí)該組件還通過(guò)提供與玩游戲的其他人對(duì)話的功能而使團(tuán)體游戲更具魅力。

Microsoft DirectShow Microsoft DirectShow API: 提供了可在您的計(jì)算機(jī)與 Internet 服務(wù)器上進(jìn)行高品質(zhì)捕獲與回放多媒體文件的功能。DirectShow 支持各種音頻與視頻格式,包括“高級(jí)流式格式 (ASF)”、“音頻-視頻交錯(cuò) (AVI)”、“數(shù)字視頻 (DV)”、“動(dòng)畫(huà)專家組 (MPEG)”、“MPEG 音頻層 3 (MP3)”、 “Windows 媒體音頻/視頻 (WMA/WMV)”以及 WAV 文件。DirectShow 還具有視頻捕獲、DVD 回放、視頻編輯與混合、硬件加速視頻解碼以及調(diào)諧廣播模擬與數(shù)字電視信號(hào)等功能。



TiGERTiAN 2010-03-27 15:15 發(fā)表評(píng)論
]]>
中國(guó)移動(dòng)江蘇移動(dòng)出現(xiàn)了問(wèn)題,短信無(wú)法發(fā)送提示未標(biāo)識(shí)的用戶http://www.aygfsteel.com/TiGERTiAN/archive/2009/12/16/306156.htmlTiGERTiANTiGERTiANWed, 16 Dec 2009 07:11:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2009/12/16/306156.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/306156.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2009/12/16/306156.html#Feedback6http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/306156.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/306156.html今天一個(gè)朋友說(shuō)短信發(fā)布出去,讓我?guī)兔νㄟ^(guò)飛信發(fā)到我手機(jī)再讓我轉(zhuǎn)發(fā)到她另外一部手機(jī)上,轉(zhuǎn)發(fā)過(guò)來(lái)之后,我發(fā)現(xiàn)我也發(fā)布出去,而且提示未標(biāo)識(shí)的用戶,沒(méi)有停機(jī),靠。。。跟我朋友提示信息一樣,情況也一樣,難道是傳說(shuō)中的RPWT?后來(lái)發(fā)現(xiàn)是部分用戶出了問(wèn)題,我有的同事沒(méi)有這個(gè)問(wèn)題,靠,移動(dòng)現(xiàn)在是沒(méi)落了。。。



TiGERTiAN 2009-12-16 15:11 發(fā)表評(píng)論
]]>
Python的一些總結(jié)(不斷更新)http://www.aygfsteel.com/TiGERTiAN/archive/2009/10/31/300507.htmlTiGERTiANTiGERTiANSat, 31 Oct 2009 12:56:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2009/10/31/300507.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/300507.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2009/10/31/300507.html#Feedback0http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/300507.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/300507.html
xx=raw_input('You can input something:\n')
print xx

2.如何調(diào)用自己寫(xiě)的類(lèi)庫(kù)中的變量?
import s
print s.xx+'111111';




TiGERTiAN 2009-10-31 20:56 發(fā)表評(píng)論
]]>
如何將數(shù)據(jù)庫(kù)中的大數(shù)字轉(zhuǎn)換成不是科學(xué)計(jì)數(shù)法的字符串http://www.aygfsteel.com/TiGERTiAN/archive/2009/10/15/298349.htmlTiGERTiANTiGERTiANThu, 15 Oct 2009 03:21:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2009/10/15/298349.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/298349.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2009/10/15/298349.html#Feedback0http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/298349.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/298349.html
Select CASTCAST(num AS DECIMAL(10)) AS VARCHAR(50)) From Table1


TiGERTiAN 2009-10-15 11:21 發(fā)表評(píng)論
]]>
IBM Data Studio連接Informix 中文亂碼問(wèn)題的解決http://www.aygfsteel.com/TiGERTiAN/archive/2008/03/27/188972.htmlTiGERTiANTiGERTiANThu, 27 Mar 2008 04:18:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2008/03/27/188972.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/188972.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2008/03/27/188972.html#Feedback0http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/188972.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/188972.htmlJDBC驅(qū)動(dòng)中使用“其他”模式,填寫(xiě)好數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)驅(qū)動(dòng):com.informix.jdbc.IfxDriver,類(lèi)位置C:\Program Files\IBM\SDP70Shared\plugins\com.ibm.datatools.informix.driver_1.0.0.v200709182330\ifxjdbc.jar,在連接字符串后面加上DB_LOCALE=en_us.819;CLIENT_LOCALE=en_us.57372;NEWCODESET=GBK,8859-1,819;
就可以了



TiGERTiAN 2008-03-27 12:18 發(fā)表評(píng)論
]]>
罪惡的微軟http://www.aygfsteel.com/TiGERTiAN/archive/2007/10/28/156503.htmlTiGERTiANTiGERTiANSun, 28 Oct 2007 10:06:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2007/10/28/156503.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/156503.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2007/10/28/156503.html#Feedback2http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/156503.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/156503.html無(wú)法啟動(dòng)world wide web publishing service服務(wù) 錯(cuò)誤:127 找不到指定的程序 2007年7月16日碰到了這個(gè)錯(cuò)誤“無(wú)法啟動(dòng)world wide web publishing service服務(wù) 錯(cuò)誤:127 找不到指定的程序”world wide web publishing service無(wú)法啟動(dòng),IIS也就無(wú)法正常運(yùn)行。 原因是在微軟補(bǔ)丁KB939373,卸載掉就可以了。 刪除補(bǔ)丁方法在"添加或刪除程序"頂部選中"顯示更新",然后刪除939373的補(bǔ)丁;最后在"服務(wù)"中手動(dòng)啟動(dòng)world wide web publishing service,一切都顯靈了

今天碰到了這個(gè)問(wèn)題,罪惡的微軟,為了補(bǔ)缺所謂的安全漏洞,竟然把IIS給禁掉了。。。FUCK

TiGERTiAN 2007-10-28 18:06 發(fā)表評(píng)論
]]>
[轉(zhuǎn)]Oracle9i的常見(jiàn)登陸問(wèn)題的解決方法http://www.aygfsteel.com/TiGERTiAN/archive/2007/10/16/153218.htmlTiGERTiANTiGERTiANTue, 16 Oct 2007 04:27:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2007/10/16/153218.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/153218.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2007/10/16/153218.html#Feedback0http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/153218.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/153218.html 能否正常啟動(dòng)OEM或OMS關(guān)鍵有以下兩點(diǎn):
第一.Oracle的系統(tǒng)服務(wù)是否開(kāi)啟;
第二.登錄時(shí)用的用戶名和口令是否正確。

那么先針對(duì)第一點(diǎn)談?wù)凮racle的系統(tǒng)服務(wù)。在完全安裝的情況下,Oracle的系統(tǒng)服務(wù)共有11項(xiàng):
1.Oracle OLAP 9.0.1.0.1
2.Oracle OLAP Agent
3.OracleOraHome90Agent
4.OracleOraHome90ClientCache
5.OracleOraHome90HTTPServer
6.OracleOraHome90ManagementServer(0.5M)
7.OracleOraHome90PagingServer
8.OracleOraHome90SNMPPeerEncapsulator
9.OracleOraHome90SNMPPeerMasterAgent
10.OracleOraHome90TNSListener(5.2M)
11.OracleServiceORACLE(70M)
(注:OraHome90是可以在安裝時(shí)改變的Oracle的主目錄名稱,是安裝時(shí)的默認(rèn)值)

其中最重要的服務(wù)有3個(gè),分別是OracleOraHome90ManagementServer、OracleOraHome90TNSListener與
OracleServiceORACLE。下面就來(lái)看一下有哪些啟動(dòng)錯(cuò)誤與它們有關(guān)。

1.Oracle系統(tǒng)提示:Ora-12541:TNS:沒(méi)有監(jiān)聽(tīng)器;
2.操作系統(tǒng)提示:在本地計(jì)算機(jī)無(wú)法啟動(dòng)OMS服務(wù)
錯(cuò)誤:1053:服務(wù)并未及時(shí)響應(yīng)來(lái)控制請(qǐng)求附帶;
以上兩種錯(cuò)誤提示大都是由OracleOraHome90TNSListener監(jiān)聽(tīng)服務(wù)引起的。
解決方法:控制面版->管理工具->服務(wù)->右鍵單擊“OracleOraHome90TNSListener”,再單擊“啟動(dòng)”。

3.Oracle系統(tǒng)提示:Ora-12500:TNS:監(jiān)聽(tīng)程序無(wú)法啟動(dòng)專用服務(wù)器進(jìn)程;
該錯(cuò)誤是由OracleServiceORACLE專用服務(wù)器進(jìn)程引起的。
解決方法:控制面版->管理工具->服務(wù)->右鍵單擊“OracleServiceORACLE”,再單擊“啟動(dòng)”。

4.Oracle系統(tǒng)提示:VTK-1000:無(wú)法連接到Management Server。
請(qǐng)驗(yàn)證您已輸入Oracle Management Server的正確主機(jī)名和狀態(tài)。
該錯(cuò)誤引起的原因有兩種,一是OracleOraHome90ManagementServer還沒(méi)啟動(dòng);二是沒(méi)有輸入主機(jī)名。
解決方法:控制面版->管理工具->服務(wù)->右鍵單擊“OracleOraHome90ManagementServer”,再單擊“啟動(dòng)”,
或是輸入您這臺(tái)計(jì)算機(jī)的完整名稱。


接著針對(duì)第二點(diǎn)談?wù)劦卿洉r(shí)用的用戶名和口令。

在安裝結(jié)束后,系統(tǒng)提供了兩個(gè)默認(rèn)的數(shù)據(jù)庫(kù)系統(tǒng)管理員,其用戶名和口令分別是SYS/change_on_install和SYSTEM/manager,同時(shí)系統(tǒng)還提供了登錄OMS的用戶名和口令:sysman/oem_temp。這里容易出現(xiàn)錯(cuò)誤的是在登錄OMS是用SYS或SYSTEM作為用戶名進(jìn)行登錄,那么Oracle系統(tǒng)就回有“登錄身份證明不正確”的提示。

小結(jié):這三個(gè)服務(wù)的啟動(dòng)或關(guān)閉還有先后的順序。一般來(lái)講,啟動(dòng)時(shí)必須先啟動(dòng)OracleOraHome90TNSListener再啟動(dòng)OracleOraHome90ManagementServer或OracleServiceORACLE,在啟動(dòng)OracleOraHome90ManagementServer時(shí),同時(shí)也啟動(dòng)了OracleServiceORACLE。而關(guān)閉時(shí)必須先關(guān)閉OracleOraHome90ManagementServer再關(guān)閉OracleOraHome90TNSListener或OracleServiceORACLE,關(guān)閉OracleOraHome90ManagementServer時(shí),若有提示輸入用戶名和口令,請(qǐng)輸入sysman的用戶名和口令,以確保成功的執(zhí)行。有些其他提示如:資源已被占用,I/O重復(fù),端口已被使用等等之類(lèi)的話,那最好與系統(tǒng)管理員聯(lián)系,再尋求解決辦法。


TiGERTiAN 2007-10-16 12:27 發(fā)表評(píng)論
]]>
Google宕機(jī)了?http://www.aygfsteel.com/TiGERTiAN/archive/2007/10/15/152915.htmlTiGERTiANTiGERTiANMon, 15 Oct 2007 03:25:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2007/10/15/152915.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/152915.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2007/10/15/152915.html#Feedback8http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/152915.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/152915.html
今天剛上網(wǎng)就發(fā)現(xiàn)了這個(gè)情況,到現(xiàn)在還沒(méi)好。



TiGERTiAN 2007-10-15 11:25 發(fā)表評(píng)論
]]>
[轉(zhuǎn)]Stein算法求最大公約數(shù)http://www.aygfsteel.com/TiGERTiAN/archive/2007/07/28/132942.htmlTiGERTiANTiGERTiANSat, 28 Jul 2007 03:13:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2007/07/28/132942.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/132942.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2007/07/28/132942.html#Feedback0http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/132942.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/132942.html

Stein算法

歐幾里德算法是計(jì)算兩個(gè)數(shù)最大公約數(shù)的傳統(tǒng)算法,他無(wú)論從理論還是從效率上都是很好的。但是他有一個(gè)致命的缺陷,這個(gè)缺陷只有在大素?cái)?shù)時(shí)才會(huì)顯現(xiàn)出來(lái)。

考慮現(xiàn)在的硬件平臺(tái),一般整數(shù)最多也就是64位,對(duì)于這樣的整數(shù),計(jì)算兩個(gè)數(shù)之間的模是很簡(jiǎn)單的。對(duì)于字長(zhǎng)為32位的平臺(tái),計(jì)算兩個(gè)不超過(guò)32位的整數(shù)的模,只需要一個(gè)指令周期,而計(jì)算64位以下的整數(shù)模,也不過(guò)幾個(gè)周期而已。但是對(duì)于更大的素?cái)?shù),這樣的計(jì)算過(guò)程就不得不由用戶來(lái)設(shè)計(jì),為了計(jì)算兩個(gè)超過(guò)64位的整數(shù)的模,用戶也許不得不采用類(lèi)似于多位數(shù)除法手算過(guò)程中的試商法,這個(gè)過(guò)程不但復(fù)雜,而且消耗了很多CPU時(shí)間。對(duì)于現(xiàn)代密碼算法,要求計(jì)算128位以上的素?cái)?shù)的情況比比皆是,設(shè)計(jì)這樣的程序迫切希望能夠拋棄除法和取模。

Stein算法由J. Stein 1961年提出,這個(gè)方法也是計(jì)算兩個(gè)數(shù)的最大公約數(shù)。和歐幾里德算法 算法不同的是,Stein算法只有整數(shù)的移位和加減法,這對(duì)于程序設(shè)計(jì)者是一個(gè)福音。

為了說(shuō)明Stein算法的正確性,首先必須注意到以下結(jié)論:

  • gcd(a,a) = a,也就是一個(gè)數(shù)和他自身的公約數(shù)是其自身
  • gcd(ka,kb) = k gcd(a,b),也就是最大公約數(shù)運(yùn)算和倍乘運(yùn)算可以交換,特殊的,當(dāng)k=2時(shí),說(shuō)明兩個(gè)偶數(shù)的最大公約數(shù)必然能被2整除

有了上述規(guī)律就可以給出Stein算法如下:

  1. 如果A=0,B是最大公約數(shù),算法結(jié)束
  2. 如果B=0,A是最大公約數(shù),算法結(jié)束
  3. 設(shè)置A1 = A、B1=B和C1 = 1
  4. 如果An和Bn都是偶數(shù),則An+1 =An /2,Bn+1 =Bn /2,Cn+1 =Cn *2(注意,乘2只要把整數(shù)左移一位即可,除2只要把整數(shù)右移一位即可)
  5. 如果An是偶數(shù),Bn不是偶數(shù),則An+1 =An /2,Bn+1 =Bn ,Cn+1 =Cn (很顯然啦,2不是奇數(shù)的約數(shù))
  6. 如果Bn是偶數(shù),An不是偶數(shù),則Bn+1 =Bn /2,An+1 =An ,Cn+1 =Cn (很顯然啦,2不是奇數(shù)的約數(shù))
  7. 如果An和Bn都不是偶數(shù),則An+1 =|An -Bn|,Bn+1 =min(An,Bn),Cn+1 =Cn
  8. n++,轉(zhuǎn)4

這個(gè)算法的原理很顯然,所以就不再證明了。現(xiàn)在考察一下該算法和歐幾里德方法效率上的差別。

考慮歐幾里德算法,最?lèi)毫拥那闆r是,每次迭代a = 2b -1,這樣,迭代后,r= b-1。如果a小于2N,這樣大約需要 4N次迭代。而考慮Stein算法,每次迭代后,顯然AN+1BN+1≤ ANBN/2,最大迭代次數(shù)也不超過(guò)4N次。也就是說(shuō),迭代次數(shù)幾乎是相等的。但是,需要注意的是,對(duì)于大素?cái)?shù),試商法將使每次迭代都更復(fù)雜,因此對(duì)于大素?cái)?shù)Stein將更有優(yōu)勢(shì)。


http://blog.vckbase.com/arong/archive/2004/06/15/458.aspx

TiGERTiAN 2007-07-28 11:13 發(fā)表評(píng)論
]]>
分頁(yè)的存儲(chǔ)過(guò)程http://www.aygfsteel.com/TiGERTiAN/archive/2007/06/25/126017.htmlTiGERTiANTiGERTiANMon, 25 Jun 2007 01:12:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2007/06/25/126017.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/126017.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2007/06/25/126017.html#Feedback0http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/126017.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/126017.html閱讀全文

TiGERTiAN 2007-06-25 09:12 發(fā)表評(píng)論
]]>
【轉(zhuǎn)】SQL Outer Join 的使用http://www.aygfsteel.com/TiGERTiAN/archive/2007/05/31/121243.htmlTiGERTiANTiGERTiANThu, 31 May 2007 13:08:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2007/05/31/121243.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/121243.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2007/05/31/121243.html#Feedback0http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/121243.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/121243.html引用地址:http://database.51cto.com/art/200601/20106.htm

外聯(lián)接

外聯(lián)接可以是左向外聯(lián)接、右向外聯(lián)接或完整外部聯(lián)接。在 FROM 子句中指定外聯(lián)接時(shí),可以由下列幾組關(guān)鍵字中的一組指定:LEFT JOIN 或 LEFT OUTER JOIN。

左向外聯(lián)接的結(jié)果集包括 LEFT OUTER 子句中指定的左表的所有行,而不僅僅是聯(lián)接列所匹配的行。如果左表的某行在右表中沒(méi)有匹配行,則在相關(guān)聯(lián)的結(jié)果集行中右表的所有選擇列表列均為空值。

RIGHT JOIN 或 RIGHT OUTER JOIN。

右向外聯(lián)接是左向外聯(lián)接的反向聯(lián)接。將返回右表的所有行。如果右表的某行在左表中沒(méi)有匹配行,則將為左表返回空值。

FULL JOIN 或 FULL OUTER JOIN。

完整外部聯(lián)接返回左表和右表中的所有行。當(dāng)某行在另一個(gè)表中沒(méi)有匹配行時(shí),則另一個(gè)表的選擇列表列包含空值。如果表之間有匹配行,則整個(gè)結(jié)果集行包含基表的數(shù)據(jù)值。

僅當(dāng)至少有一個(gè)同屬于兩表的行符合聯(lián)接條件時(shí),內(nèi)聯(lián)接才返回行。內(nèi)聯(lián)接消除與另一個(gè)表中的任何行不匹配的行。而外聯(lián)接會(huì)返回 FROM 子句中提到的至少一個(gè)表或視圖的所有行,只要這些行符合任何 WHERE 或 HAVING 搜索條件。將檢索通過(guò)左向外聯(lián)接引用的左表的所有行,以及通過(guò)右向外聯(lián)接引用的右表的所有行。完整外部聯(lián)接中兩個(gè)表的所有行都將返回。

Microsoft® SQL Server™ 2000 對(duì)在 FROM 子句中指定的外聯(lián)接使用以下 SQL-92 關(guān)鍵字:

LEFT OUTER JOIN 或 LEFT JOIN

RIGHT OUTER JOIN 或 RIGHT JOIN

FULL OUTER JOIN 或 FULL JOIN

SQL Server 支持 SQL-92 外聯(lián)接語(yǔ)法,以及在 WHERE 子句中使用 *= 和 =* 運(yùn)算符指定外聯(lián)接的舊式語(yǔ)法。由于 SQL-92 語(yǔ)法不容易產(chǎn)生歧義,而舊式 Transact-SQL 外聯(lián)接有時(shí)會(huì)產(chǎn)生歧義,因此建議使用 SQL-92 語(yǔ)法。

使用左向外聯(lián)接

假設(shè)在 city 列上聯(lián)接 authors 表和 publishers 表。結(jié)果只顯示在出版商所在城市居住的作者(本例中為 Abraham Bennet 和 Cheryl Carson)。

若要在結(jié)果中包括所有的作者,而不管出版商是否住在同一個(gè)城市,請(qǐng)使用 SQL-92 左向外聯(lián)接。下面是 Transact-SQL 左向外聯(lián)接的查詢和結(jié)果:

USE pubs

SELECT a.au_fname, a.au_lname, p.pub_name

FROM authors a LEFT OUTER JOIN publishers p

ON a.city = p.city

ORDER BY p.pub_name ASC, a.au_lname ASC, a.au_fname ASC

下面是結(jié)果集:

au_fname au_lname pub_name

Reginald Blotchet-Halls NULL

Michel DeFrance NULL

Innes del Castillo NULL

Ann Dull NULL

Marjorie Green NULL

Morningstar Greene NULL

Burt Gringlesby NULL

Sheryl Hunter NULL

Livia Karsen NULL

Charlene Locksley NULL

Stearns MacFeather NULL

Heather McBadden NULL

Michael OLeary NULL

Sylvia Panteley NULL

Albert Ringer NULL

Anne Ringer NULL

Meander Smith NULL

Dean Straight NULL

Dirk Stringer NULL

Johnson White NULL

Akiko Yokomoto NULL

Abraham Bennet Algodata Infosystems

Cheryl Carson Algodata Infosystems

(23 row(s) affected)

不管是否與 publishers 表中的 city 列匹配,LEFT OUTER JOIN 均會(huì)在結(jié)果中包含 authors 表的所有行。注意:結(jié)果中所列的大多數(shù)作者都沒(méi)有相匹配的數(shù)據(jù),因此,這些行的 pub_name 列包含空值。

使用右向外聯(lián)接

假設(shè)在 city 列上聯(lián)接 authors 表和 publishers 表。結(jié)果只顯示在出版商所在城市居住的作者(本例中為 Abraham Bennet 和 Cheryl Carson)。SQL-92 右向外聯(lián)接運(yùn)算符 RIGHT OUTER JOIN 指明:不管第一個(gè)表中是否有匹配的數(shù)據(jù),結(jié)果將包含第二個(gè)表中的所有行。

若要在結(jié)果中包括所有的出版商,而不管城市中是否還有出版商居住,請(qǐng)使用 SQL-92 右向外聯(lián)接。下面是 Transact-SQL 右向外聯(lián)接的查詢和結(jié)果:

USE pubs

SELECT a.au_fname, a.au_lname, p.pub_name

FROM authors AS a RIGHT OUTER JOIN publishers AS p

ON a.city = p.city

ORDER BY p.pub_name ASC, a.au_lname ASC, a.au_fname ASC

 

下面是結(jié)果集:

au_fname au_lname pub_name 

Abraham Bennet Algodata Infosystems

Cheryl Carson Algodata Infosystems

NULL NULL Binnet & Hardley

NULL NULL Five Lakes Publishing

NULL NULL GGG&G

NULL NULL Lucerne Publishing

NULL NULL New Moon Books

NULL NULL Ramona Publishers

NULL NULL Scootney Books

(9 row(s) affected)

使用謂詞(如將聯(lián)接與常量比較)可以進(jìn)一步限制外聯(lián)接。下例包含相同的右向外聯(lián)接,但消除銷(xiāo)售量低于 50 本的書(shū)籍的書(shū)名:

USE pubs

SELECT s.stor_id, s.qty, t.title

FROM sales s RIGHT OUTER JOIN titles t

ON s.title_id = t.title_id

AND s.qty > 50

ORDER BY s.stor_id ASC

下面是結(jié)果集:

stor_id qty title 

(null) (null) But Is It User Friendly?

(null) (null) Computer Phobic AND Non-Phobic Individuals: Behavior

Variations

(null) (null) Cooking with Computers: Surreptitious Balance Sheets

(null) (null) Emotional Security: A New Algorithm

(null) (null) Fifty Years in Buckingham Palace Kitchens

7066 75 Is Anger the Enemy?

(null) (null) Life Without Fear

(null) (null) Net Etiquette

(null) (null) Onions, Leeks, and Garlic: Cooking Secrets of the

Mediterranean

(null) (null) Prolonged Data Deprivation: Four Case Studies

(null) (null) Secrets of Silicon Valley

(null) (null) Silicon Valley Gastronomic Treats

(null) (null) Straight Talk About Computers

(null) (null) Sushi, Anyone?

(null) (null) The Busy Executives Database Guide

(null) (null) The Gourmet Microwave

(null) (null) The Psychology of Computer Cooking

(null) (null) You Can Combat Computer Stress!

(18 row(s) affected)

有關(guān)謂詞的更多信息,請(qǐng)參見(jiàn) WHERE。

使用完整外部聯(lián)接

若要通過(guò)在聯(lián)接結(jié)果中包括不匹配的行保留不匹配信息,請(qǐng)使用完整外部聯(lián)接。Microsoft® SQL Server™ 2000 提供完整外部聯(lián)接運(yùn)算符 FULL OUTER JOIN,不管另一個(gè)表是否有匹配的值,此運(yùn)算符都包括兩個(gè)表中的所有行。

假設(shè)在 city 列上聯(lián)接 authors 表和 publishers 表。結(jié)果只顯示在出版商所在城市居住的作者(本例中為 Abraham Bennet 和 Cheryl Carson)。SQL-92 FULL OUTER JOIN 運(yùn)算符指明:不管表中是否有匹配的數(shù)據(jù),結(jié)果將包括兩個(gè)表中的所有行。

若要在結(jié)果中包括所有作者和出版商,而不管城市中是否有出版商或者出版商是否住在同一個(gè)城市,請(qǐng)使用完整外部聯(lián)接。下面是 Transact-SQL 完整外部聯(lián)接的查詢和結(jié)果:

USE pubs

SELECT a.au_fname, a.au_lname, p.pub_name

FROM authors a FULL OUTER JOIN publishers p

ON a.city = p.city

ORDER BY p.pub_name ASC, a.au_lname ASC, a.au_fname ASC

 

下面是結(jié)果集:

            

au_fname au_lname pub_name

Reginald Blotchet-Halls NULL

Michel DeFrance NULL

Innes del Castillo NULL

Ann Dull NULL

Marjorie Green NULL

Morningstar Greene NULL

Burt Gringlesby NULL

Sheryl Hunter NULL

Livia Karsen NULL

Charlene Locksley NULL

Stearns MacFeather NULL

Heather McBadden NULL

Michael OLeary NULL

Sylvia Panteley NULL

Albert Ringer NULL

Anne Ringer NULL

Meander Smith NULL

Dean Straight NULL

Dirk Stringer NULL

Johnson White NULL

Akiko Yokomoto NULL

Abraham Bennet Algodata Infosystems

Cheryl Carson Algodata Infosystems

NULL NULL Binnet & Hardley

NULL NULL Five Lakes Publishing

NULL NULL GGG&G

NULL NULL Lucerne Publishing

NULL NULL New Moon Books

NULL NULL Ramona Publishers

NULL NULL Scootney Books

(30 row(s) affected)



TiGERTiAN 2007-05-31 21:08 發(fā)表評(píng)論
]]>
[轉(zhuǎn)]海量數(shù)據(jù)庫(kù)的查詢優(yōu)化及分頁(yè)算法方案 http://www.aygfsteel.com/TiGERTiAN/archive/2007/05/19/118557.htmlTiGERTiANTiGERTiANSat, 19 May 2007 08:18:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2007/05/19/118557.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/118557.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2007/05/19/118557.html#Feedback0http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/118557.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/118557.html作者:Imain
原文鏈接:http://dev.csdn.net/author/Imain/d524068d280f46cca326386a7c4b1321.html

隨著“金盾工程”建設(shè)的逐步深入和公安信息化的高速發(fā)展,公安計(jì)算機(jī)應(yīng)用系統(tǒng)被廣泛應(yīng)用在各警種、各部門(mén)。與此同時(shí),應(yīng)用系統(tǒng)體系的核心、系統(tǒng)數(shù)據(jù)的存放地――數(shù)據(jù)庫(kù)也隨著實(shí)際應(yīng)用而急劇膨脹,一些大規(guī)模的系統(tǒng),如人口系統(tǒng)的數(shù)據(jù)甚至超過(guò)了1000萬(wàn)條,可謂海量。那么,如何實(shí)現(xiàn)快速地從這些超大容量的數(shù)據(jù)庫(kù)中提取數(shù)據(jù)(查詢)、分析、統(tǒng)計(jì)以及提取數(shù)據(jù)后進(jìn)行數(shù)據(jù)分頁(yè)已成為各地系統(tǒng)管理員和數(shù)據(jù)庫(kù)管理員亟待解決的難題。

在以下的文章中,我將以“辦公自動(dòng)化”系統(tǒng)為例,探討如何在有著1000萬(wàn)條數(shù)據(jù)的MS SQL SERVER數(shù)據(jù)庫(kù)中實(shí)現(xiàn)快速的數(shù)據(jù)提取和數(shù)據(jù)分頁(yè)。以下代碼說(shuō)明了我們實(shí)例中數(shù)據(jù)庫(kù)的“紅頭文件”一表的部分?jǐn)?shù)據(jù)結(jié)構(gòu):

CREATE TABLE [dbo].[TGongwen] ( --TGongwen是紅頭文件表名

[Gid] [int] IDENTITY (1, 1) NOT NULL ,
--本表的id號(hào),也是主鍵

[title] [varchar] (80) COLLATE Chinese_PRC_CI_AS NULL ,
--紅頭文件的標(biāo)題

[fariqi] [datetime] NULL ,
--發(fā)布日期

[neibuYonghu] [varchar] (70) COLLATE Chinese_PRC_CI_AS NULL ,
--發(fā)布用戶

[reader] [varchar] (900) COLLATE Chinese_PRC_CI_AS NULL ,

--需要瀏覽的用戶。每個(gè)用戶中間用分隔符“,”分開(kāi)

) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO



下面,我們來(lái)往數(shù)據(jù)庫(kù)中添加1000萬(wàn)條數(shù)據(jù):

declare @i int

set @i=1

while @i<=250000

begin

insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-2-5','通信科','通信科,辦公室,王局長(zhǎng),劉局長(zhǎng),張局長(zhǎng),admin,刑偵支隊(duì),特勤支隊(duì),交巡警支隊(duì),經(jīng)偵支隊(duì),戶政科,治安支隊(duì),外事科','這是最先的25萬(wàn)條記錄')

set @i=@i+1

end

GO



declare @i int

set @i=1

while @i<=250000

begin

insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-9-16','辦公室','辦公室,通信科,王局長(zhǎng),劉局長(zhǎng),張局長(zhǎng),admin,刑偵支隊(duì),特勤支隊(duì),交巡警支隊(duì),經(jīng)偵支隊(duì),戶政科,外事科','這是中間的25萬(wàn)條記錄')

set @i=@i+1

end

GO



declare @h int

set @h=1

while @h<=100

begin

declare @i int

set @i=2002

while @i<=2003

begin

declare @j int

set @j=0

while @j<50

begin

declare @k int

set @k=0

while @k<50

begin

insert into Tgongwen(fariqi,neibuyonghu,reader,title) values(cast(@i as varchar(4))+'-8-15 3:'+cast(@j as varchar(2))+':'+cast(@j as varchar(2)),'通信科','辦公室,通信科,王局長(zhǎng),劉局長(zhǎng),張局長(zhǎng),admin,刑偵支隊(duì),特勤支隊(duì),交巡警支隊(duì),經(jīng)偵支隊(duì),戶政科,外事科','這是最后的50萬(wàn)條記錄')

set @k=@k+1

end

set @j=@j+1

end

set @i=@i+1

end

set @h=@h+1

end

GO



declare @i int

set @i=1

while @i<=9000000

begin

insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-5-5','通信科','通信科,辦公室,王局長(zhǎng),劉局長(zhǎng),張局長(zhǎng),admin,刑偵支隊(duì),特勤支隊(duì),交巡警支隊(duì),經(jīng)偵支隊(duì),戶政科,治安支隊(duì),外事科','這是最后添加的900萬(wàn)條記錄')

set @i=@i+1000000

end

GO

通過(guò)以上語(yǔ)句,我們創(chuàng)建了25萬(wàn)條由通信科于2004年2月5日發(fā)布的記錄,25萬(wàn)條由辦公室于2004年9月6日發(fā)布的記錄,2002年和2003年各100個(gè)2500條相同日期、不同分秒的由通信科發(fā)布的記錄(共50萬(wàn)條),還有由通信科于2004年5月5日發(fā)布的900萬(wàn)條記錄,合計(jì)1000萬(wàn)條。



一、因情制宜,建立“適當(dāng)”的索引

建立“適當(dāng)”的索引是實(shí)現(xiàn)查詢優(yōu)化的首要前提。

索引(index)是除表之外另一重要的、用戶定義的存儲(chǔ)在物理介質(zhì)上的數(shù)據(jù)結(jié)構(gòu)。當(dāng)根據(jù)索引碼的值搜索數(shù)據(jù)時(shí),索引提供了對(duì)數(shù)據(jù)的快速訪問(wèn)。事實(shí)上,沒(méi)有索引,數(shù)據(jù)庫(kù)也能根據(jù)SELECT語(yǔ)句成功地檢索到結(jié)果,但隨著表變得越來(lái)越大,使用“適當(dāng)”的索引的效果就越來(lái)越明顯。注意,在這句話中,我們用了“適當(dāng)”這個(gè)詞,這是因?yàn)椋绻褂盟饕龝r(shí)不認(rèn)真考慮其實(shí)現(xiàn)過(guò)程,索引既可以提高也會(huì)破壞數(shù)據(jù)庫(kù)的工作性能。

(一)深入淺出理解索引結(jié)構(gòu)

實(shí)際上,您可以把索引理解為一種特殊的目錄。微軟的SQL SERVER提供了兩種索引:聚集索引(clustered index,也稱聚類(lèi)索引、簇集索引)和非聚集索引(nonclustered index,也稱非聚類(lèi)索引、非簇集索引)。下面,我們舉例來(lái)說(shuō)明一下聚集索引和非聚集索引的區(qū)別:

其實(shí),我們的漢語(yǔ)字典的正文本身就是一個(gè)聚集索引。比如,我們要查“安”字,就會(huì)很自然地翻開(kāi)字典的前幾頁(yè),因?yàn)?#8220;安”的拼音是“an”,而按照拼音排序漢字的字典是以英文字母“a”開(kāi)頭并以“z”結(jié)尾的,那么“安”字就自然地排在字典的前部。如果您翻完了所有以“a”開(kāi)頭的部分仍然找不到這個(gè)字,那么就說(shuō)明您的字典中沒(méi)有這個(gè)字;同樣的,如果查“張”字,那您也會(huì)將您的字典翻到最后部分,因?yàn)?#8220;張”的拼音是“zhang”。也就是說(shuō),字典的正文部分本身就是一個(gè)目錄,您不需要再去查其他目錄來(lái)找到您需要找的內(nèi)容。

我們把這種正文內(nèi)容本身就是一種按照一定規(guī)則排列的目錄稱為“聚集索引”。

如果您認(rèn)識(shí)某個(gè)字,您可以快速地從自動(dòng)中查到這個(gè)字。但您也可能會(huì)遇到您不認(rèn)識(shí)的字,不知道它的發(fā)音,這時(shí)候,您就不能按照剛才的方法找到您要查的字,而需要去根據(jù)“偏旁部首”查到您要找的字,然后根據(jù)這個(gè)字后的頁(yè)碼直接翻到某頁(yè)來(lái)找到您要找的字。但您結(jié)合“部首目錄”和“檢字表”而查到的字的排序并不是真正的正文的排序方法,比如您查“張”字,我們可以看到在查部首之后的檢字表中“張”的頁(yè)碼是672頁(yè),檢字表中“張”的上面是“馳”字,但頁(yè)碼卻是63頁(yè),“張”的下面是“弩”字,頁(yè)面是390頁(yè)。很顯然,這些字并不是真正的分別位于“張”字的上下方,現(xiàn)在您看到的連續(xù)的“馳、張、弩”三字實(shí)際上就是他們?cè)诜蔷奂饕械呐判颍亲值湔闹械淖衷诜蔷奂饕械挠成洹N覀兛梢酝ㄟ^(guò)這種方式來(lái)找到您所需要的字,但它需要兩個(gè)過(guò)程,先找到目錄中的結(jié)果,然后再翻到您所需要的頁(yè)碼。

我們把這種目錄純粹是目錄,正文純粹是正文的排序方式稱為“非聚集索引”。

通過(guò)以上例子,我們可以理解到什么是“聚集索引”和“非聚集索引”。

進(jìn)一步引申一下,我們可以很容易的理解:每個(gè)表只能有一個(gè)聚集索引,因?yàn)槟夸浿荒馨凑找环N方法進(jìn)行排序。

(二)何時(shí)使用聚集索引或非聚集索引

下面的表總結(jié)了何時(shí)使用聚集索引或非聚集索引(很重要)。

動(dòng)作描述
使用聚集索引
使用非聚集索引

列經(jīng)常被分組排序
應(yīng)
應(yīng)

返回某范圍內(nèi)的數(shù)據(jù)
應(yīng)
不應(yīng)

一個(gè)或極少不同值
不應(yīng)
不應(yīng)

小數(shù)目的不同值
應(yīng)
不應(yīng)

大數(shù)目的不同值
不應(yīng)
應(yīng)

頻繁更新的列
不應(yīng)
應(yīng)

外鍵列
應(yīng)
應(yīng)

主鍵列
應(yīng)
應(yīng)

頻繁修改索引列
不應(yīng)
應(yīng)


事實(shí)上,我們可以通過(guò)前面聚集索引和非聚集索引的定義的例子來(lái)理解上表。如:返回某范圍內(nèi)的數(shù)據(jù)一項(xiàng)。比如您的某個(gè)表有一個(gè)時(shí)間列,恰好您把聚合索引建立在了該列,這時(shí)您查詢2004年1月1日至2004年10月1日之間的全部數(shù)據(jù)時(shí),這個(gè)速度就將是很快的,因?yàn)槟倪@本字典正文是按日期進(jìn)行排序的,聚類(lèi)索引只需要找到要檢索的所有數(shù)據(jù)中的開(kāi)頭和結(jié)尾數(shù)據(jù)即可;而不像非聚集索引,必須先查到目錄中查到每一項(xiàng)數(shù)據(jù)對(duì)應(yīng)的頁(yè)碼,然后再根據(jù)頁(yè)碼查到具體內(nèi)容。

(三)結(jié)合實(shí)際,談索引使用的誤區(qū)

理論的目的是應(yīng)用。雖然我們剛才列出了何時(shí)應(yīng)使用聚集索引或非聚集索引,但在實(shí)踐中以上規(guī)則卻很容易被忽視或不能根據(jù)實(shí)際情況進(jìn)行綜合分析。下面我們將根據(jù)在實(shí)踐中遇到的實(shí)際問(wèn)題來(lái)談一下索引使用的誤區(qū),以便于大家掌握索引建立的方法。

1、主鍵就是聚集索引

這種想法筆者認(rèn)為是極端錯(cuò)誤的,是對(duì)聚集索引的一種浪費(fèi)。雖然SQL SERVER默認(rèn)是在主鍵上建立聚集索引的。

通常,我們會(huì)在每個(gè)表中都建立一個(gè)ID列,以區(qū)分每條數(shù)據(jù),并且這個(gè)ID列是自動(dòng)增大的,步長(zhǎng)一般為1。我們的這個(gè)辦公自動(dòng)化的實(shí)例中的列Gid就是如此。此時(shí),如果我們將這個(gè)列設(shè)為主鍵,SQL SERVER會(huì)將此列默認(rèn)為聚集索引。這樣做有好處,就是可以讓您的數(shù)據(jù)在數(shù)據(jù)庫(kù)中按照ID進(jìn)行物理排序,但筆者認(rèn)為這樣做意義不大。

顯而易見(jiàn),聚集索引的優(yōu)勢(shì)是很明顯的,而每個(gè)表中只能有一個(gè)聚集索引的規(guī)則,這使得聚集索引變得更加珍貴。

從我們前面談到的聚集索引的定義我們可以看出,使用聚集索引的最大好處就是能夠根據(jù)查詢要求,迅速縮小查詢范圍,避免全表掃描。在實(shí)際應(yīng)用中,因?yàn)镮D號(hào)是自動(dòng)生成的,我們并不知道每條記錄的ID號(hào),所以我們很難在實(shí)踐中用ID號(hào)來(lái)進(jìn)行查詢。這就使讓ID號(hào)這個(gè)主鍵作為聚集索引成為一種資源浪費(fèi)。其次,讓每個(gè)ID號(hào)都不同的字段作為聚集索引也不符合“大數(shù)目的不同值情況下不應(yīng)建立聚合索引”規(guī)則;當(dāng)然,這種情況只是針對(duì)用戶經(jīng)常修改記錄內(nèi)容,特別是索引項(xiàng)的時(shí)候會(huì)負(fù)作用,但對(duì)于查詢速度并沒(méi)有影響。

在辦公自動(dòng)化系統(tǒng)中,無(wú)論是系統(tǒng)首頁(yè)顯示的需要用戶簽收的文件、會(huì)議還是用戶進(jìn)行文件查詢等任何情況下進(jìn)行數(shù)據(jù)查詢都離不開(kāi)字段的是“日期”還有用戶本身的“用戶名”。

通常,辦公自動(dòng)化的首頁(yè)會(huì)顯示每個(gè)用戶尚未簽收的文件或會(huì)議。雖然我們的where語(yǔ)句可以僅僅限制當(dāng)前用戶尚未簽收的情況,但如果您的系統(tǒng)已建立了很長(zhǎng)時(shí)間,并且數(shù)據(jù)量很大,那么,每次每個(gè)用戶打開(kāi)首頁(yè)的時(shí)候都進(jìn)行一次全表掃描,這樣做意義是不大的,絕大多數(shù)的用戶1個(gè)月前的文件都已經(jīng)瀏覽過(guò)了,這樣做只能徒增數(shù)據(jù)庫(kù)的開(kāi)銷(xiāo)而已。事實(shí)上,我們完全可以讓用戶打開(kāi)系統(tǒng)首頁(yè)時(shí),數(shù)據(jù)庫(kù)僅僅查詢這個(gè)用戶近3個(gè)月來(lái)未閱覽的文件,通過(guò)“日期”這個(gè)字段來(lái)限制表掃描,提高查詢速度。如果您的辦公自動(dòng)化系統(tǒng)已經(jīng)建立的2年,那么您的首頁(yè)顯示速度理論上將是原來(lái)速度8倍,甚至更快。

在這里之所以提到“理論上”三字,是因?yàn)槿绻木奂饕€是盲目地建在ID這個(gè)主鍵上時(shí),您的查詢速度是沒(méi)有這么高的,即使您在“日期”這個(gè)字段上建立的索引(非聚合索引)。下面我們就來(lái)看一下在1000萬(wàn)條數(shù)據(jù)量的情況下各種查詢的速度表現(xiàn)(3個(gè)月內(nèi)的數(shù)據(jù)為25萬(wàn)條):

(1)僅在主鍵上建立聚集索引,并且不劃分時(shí)間段:

Select gid,fariqi,neibuyonghu,title from tgongwen

用時(shí):128470毫秒(即:128秒)

(2)在主鍵上建立聚集索引,在fariq上建立非聚集索引:

select gid,fariqi,neibuyonghu,title from Tgongwen

where fariqi> dateadd(day,-90,getdate())

用時(shí):53763毫秒(54秒)

(3)將聚合索引建立在日期列(fariqi)上:

select gid,fariqi,neibuyonghu,title from Tgongwen

where fariqi> dateadd(day,-90,getdate())

用時(shí):2423毫秒(2秒)

雖然每條語(yǔ)句提取出來(lái)的都是25萬(wàn)條數(shù)據(jù),各種情況的差異卻是巨大的,特別是將聚集索引建立在日期列時(shí)的差異。事實(shí)上,如果您的數(shù)據(jù)庫(kù)真的有1000萬(wàn)容量的話,把主鍵建立在ID列上,就像以上的第1、2種情況,在網(wǎng)頁(yè)上的表現(xiàn)就是超時(shí),根本就無(wú)法顯示。這也是我摒棄ID列作為聚集索引的一個(gè)最重要的因素。

得出以上速度的方法是:在各個(gè)select語(yǔ)句前加:declare @d datetime

set @d=getdate()

并在select語(yǔ)句后加:

select [語(yǔ)句執(zhí)行花費(fèi)時(shí)間(毫秒)]=datediff(ms,@d,getdate())

2、只要建立索引就能顯著提高查詢速度

事實(shí)上,我們可以發(fā)現(xiàn)上面的例子中,第2、3條語(yǔ)句完全相同,且建立索引的字段也相同;不同的僅是前者在fariqi字段上建立的是非聚合索引,后者在此字段上建立的是聚合索引,但查詢速度卻有著天壤之別。所以,并非是在任何字段上簡(jiǎn)單地建立索引就能提高查詢速度。

從建表的語(yǔ)句中,我們可以看到這個(gè)有著1000萬(wàn)數(shù)據(jù)的表中fariqi字段有5003個(gè)不同記錄。在此字段上建立聚合索引是再合適不過(guò)了。在現(xiàn)實(shí)中,我們每天都會(huì)發(fā)幾個(gè)文件,這幾個(gè)文件的發(fā)文日期就相同,這完全符合建立聚集索引要求的:“既不能絕大多數(shù)都相同,又不能只有極少數(shù)相同”的規(guī)則。由此看來(lái),我們建立“適當(dāng)”的聚合索引對(duì)于我們提高查詢速度是非常重要的。

3、把所有需要提高查詢速度的字段都加進(jìn)聚集索引,以提高查詢速度

上面已經(jīng)談到:在進(jìn)行數(shù)據(jù)查詢時(shí)都離不開(kāi)字段的是“日期”還有用戶本身的“用戶名”。既然這兩個(gè)字段都是如此的重要,我們可以把他們合并起來(lái),建立一個(gè)復(fù)合索引(compound index)。

很多人認(rèn)為只要把任何字段加進(jìn)聚集索引,就能提高查詢速度,也有人感到迷惑:如果把復(fù)合的聚集索引字段分開(kāi)查詢,那么查詢速度會(huì)減慢嗎?帶著這個(gè)問(wèn)題,我們來(lái)看一下以下的查詢速度(結(jié)果集都是25萬(wàn)條數(shù)據(jù)):(日期列fariqi首先排在復(fù)合聚集索引的起始列,用戶名neibuyonghu排在后列)

(1)select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi>'2004-5-5'

查詢速度:2513毫秒

(2)select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi>'2004-5-5' and neibuyonghu='辦公室'

查詢速度:2516毫秒

(3)select gid,fariqi,neibuyonghu,title from Tgongwen where neibuyonghu='辦公室'

查詢速度:60280毫秒

從以上試驗(yàn)中,我們可以看到如果僅用聚集索引的起始列作為查詢條件和同時(shí)用到復(fù)合聚集索引的全部列的查詢速度是幾乎一樣的,甚至比用上全部的復(fù)合索引列還要略快(在查詢結(jié)果集數(shù)目一樣的情況下);而如果僅用復(fù)合聚集索引的非起始列作為查詢條件的話,這個(gè)索引是不起任何作用的。當(dāng)然,語(yǔ)句1、2的查詢速度一樣是因?yàn)椴樵兊臈l目數(shù)一樣,如果復(fù)合索引的所有列都用上,而且查詢結(jié)果少的話,這樣就會(huì)形成“索引覆蓋”,因而性能可以達(dá)到最優(yōu)。同時(shí),請(qǐng)記住:無(wú)論您是否經(jīng)常使用聚合索引的其他列,但其前導(dǎo)列一定要是使用最頻繁的列。

(四)其他書(shū)上沒(méi)有的索引使用經(jīng)驗(yàn)總結(jié)

1、用聚合索引比用不是聚合索引的主鍵速度快

下面是實(shí)例語(yǔ)句:(都是提取25萬(wàn)條數(shù)據(jù))

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'

使用時(shí)間:3326毫秒

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid<=250000

使用時(shí)間:4470毫秒

這里,用聚合索引比用不是聚合索引的主鍵速度快了近1/4。

2、用聚合索引比用一般的主鍵作order by時(shí)速度快,特別是在小數(shù)據(jù)量情況下

select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by fariqi

用時(shí):12936

select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by gid

用時(shí):18843

這里,用聚合索引比用一般的主鍵作order by時(shí),速度快了3/10。事實(shí)上,如果數(shù)據(jù)量很小的話,用聚集索引作為排序列要比使用非聚集索引速度快得明顯的多;而數(shù)據(jù)量如果很大的話,如10萬(wàn)以上,則二者的速度差別不明顯。

3、使用聚合索引內(nèi)的時(shí)間段,搜索時(shí)間會(huì)按數(shù)據(jù)占整個(gè)數(shù)據(jù)表的百分比成比例減少,而無(wú)論聚合索引使用了多少個(gè)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-1-1'

用時(shí):6343毫秒(提取100萬(wàn)條)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-6-6'

用時(shí):3170毫秒(提取50萬(wàn)條)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'

用時(shí):3326毫秒(和上句的結(jié)果一模一樣。如果采集的數(shù)量一樣,那么用大于號(hào)和等于號(hào)是一樣的)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-1-1' and fariqi<'2004-6-6'

用時(shí):3280毫秒

4 、日期列不會(huì)因?yàn)橛蟹置氲妮斎攵鴾p慢查詢速度

下面的例子中,共有100萬(wàn)條數(shù)據(jù),2004年1月1日以后的數(shù)據(jù)有50萬(wàn)條,但只有兩個(gè)不同的日期,日期精確到日;之前有數(shù)據(jù)50萬(wàn)條,有5000個(gè)不同的日期,日期精確到秒。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-1-1' order by fariqi

用時(shí):6390毫秒

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi<'2004-1-1' order by fariqi

用時(shí):6453毫秒

(五)其他注意事項(xiàng)

“水可載舟,亦可覆舟”,索引也一樣。索引有助于提高檢索性能,但過(guò)多或不當(dāng)?shù)乃饕矔?huì)導(dǎo)致系統(tǒng)低效。因?yàn)橛脩粼诒碇忻考舆M(jìn)一個(gè)索引,數(shù)據(jù)庫(kù)就要做更多的工作。過(guò)多的索引甚至?xí)?dǎo)致索引碎片。

所以說(shuō),我們要建立一個(gè)“適當(dāng)”的索引體系,特別是對(duì)聚合索引的創(chuàng)建,更應(yīng)精益求精,以使您的數(shù)據(jù)庫(kù)能得到高性能的發(fā)揮。

當(dāng)然,在實(shí)踐中,作為一個(gè)盡職的數(shù)據(jù)庫(kù)管理員,您還要多測(cè)試一些方案,找出哪種方案效率最高、最為有效。



二、改善SQL語(yǔ)句

很多人不知道SQL語(yǔ)句在SQL SERVER中是如何執(zhí)行的,他們擔(dān)心自己所寫(xiě)的SQL語(yǔ)句會(huì)被SQL SERVER誤解。比如:

select * from table1 where name='zhangsan' and tID > 10000

和執(zhí)行:

select * from table1 where tID > 10000 and name='zhangsan'

一些人不知道以上兩條語(yǔ)句的執(zhí)行效率是否一樣,因?yàn)槿绻?jiǎn)單的從語(yǔ)句先后上看,這兩個(gè)語(yǔ)句的確是不一樣,如果tID是一個(gè)聚合索引,那么后一句僅僅從表的10000條以后的記錄中查找就行了;而前一句則要先從全表中查找看有幾個(gè)name='zhangsan'的,而后再根據(jù)限制條件條件tID>10000來(lái)提出查詢結(jié)果。

事實(shí)上,這樣的擔(dān)心是不必要的。SQL SERVER中有一個(gè)“查詢分析優(yōu)化器”,它可以計(jì)算出where子句中的搜索條件并確定哪個(gè)索引能縮小表掃描的搜索空間,也就是說(shuō),它能實(shí)現(xiàn)自動(dòng)優(yōu)化。

雖然查詢優(yōu)化器可以根據(jù)where子句自動(dòng)的進(jìn)行查詢優(yōu)化,但大家仍然有必要了解一下“查詢優(yōu)化器”的工作原理,如非這樣,有時(shí)查詢優(yōu)化器就會(huì)不按照您的本意進(jìn)行快速查詢。

在查詢分析階段,查詢優(yōu)化器查看查詢的每個(gè)階段并決定限制需要掃描的數(shù)據(jù)量是否有用。如果一個(gè)階段可以被用作一個(gè)掃描參數(shù)(SARG),那么就稱之為可優(yōu)化的,并且可以利用索引快速獲得所需數(shù)據(jù)。

SARG的定義:用于限制搜索的一個(gè)操作,因?yàn)樗ǔJ侵敢粋€(gè)特定的匹配,一個(gè)值得范圍內(nèi)的匹配或者兩個(gè)以上條件的AND連接。形式如下:

列名 操作符 <常數(shù) 或 變量>



<常數(shù) 或 變量> 操作符列名

列名可以出現(xiàn)在操作符的一邊,而常數(shù)或變量出現(xiàn)在操作符的另一邊。如:

Name=’張三’

價(jià)格>5000

5000<價(jià)格

Name=’張三’ and 價(jià)格>5000

如果一個(gè)表達(dá)式不能滿足SARG的形式,那它就無(wú)法限制搜索的范圍了,也就是SQL SERVER必須對(duì)每一行都判斷它是否滿足WHERE子句中的所有條件。所以一個(gè)索引對(duì)于不滿足SARG形式的表達(dá)式來(lái)說(shuō)是無(wú)用的。

介紹完SARG后,我們來(lái)總結(jié)一下使用SARG以及在實(shí)踐中遇到的和某些資料上結(jié)論不同的經(jīng)驗(yàn):

1、Like語(yǔ)句是否屬于SARG取決于所使用的通配符的類(lèi)型

如:name like ‘張%’ ,這就屬于SARG

而:name like ‘%張’ ,就不屬于SARG。

原因是通配符%在字符串的開(kāi)通使得索引無(wú)法使用。

2、or 會(huì)引起全表掃描

Name=’張三’ and 價(jià)格>5000 符號(hào)SARG,而:Name=’張三’ or 價(jià)格>5000 則不符合SARG。使用or會(huì)引起全表掃描。

3、非操作符、函數(shù)引起的不滿足SARG形式的語(yǔ)句

不滿足SARG形式的語(yǔ)句最典型的情況就是包括非操作符的語(yǔ)句,如:NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE等,另外還有函數(shù)。下面就是幾個(gè)不滿足SARG形式的例子:

ABS(價(jià)格)<5000

Name like ‘%三’

有些表達(dá)式,如:

WHERE 價(jià)格*2>5000

SQL SERVER也會(huì)認(rèn)為是SARG,SQL SERVER會(huì)將此式轉(zhuǎn)化為:

WHERE 價(jià)格>2500/2

但我們不推薦這樣使用,因?yàn)橛袝r(shí)SQL SERVER不能保證這種轉(zhuǎn)化與原始表達(dá)式是完全等價(jià)的。

4、IN 的作用相當(dāng)與OR

語(yǔ)句:

Select * from table1 where tid in (2,3)



Select * from table1 where tid=2 or tid=3

是一樣的,都會(huì)引起全表掃描,如果tid上有索引,其索引也會(huì)失效。

5、盡量少用NOT

6、exists 和 in 的執(zhí)行效率是一樣的

很多資料上都顯示說(shuō),exists要比in的執(zhí)行效率要高,同時(shí)應(yīng)盡可能的用not exists來(lái)代替not in。但事實(shí)上,我試驗(yàn)了一下,發(fā)現(xiàn)二者無(wú)論是前面帶不帶not,二者之間的執(zhí)行效率都是一樣的。因?yàn)樯婕白硬樵儯覀冊(cè)囼?yàn)這次用SQL SERVER自帶的pubs數(shù)據(jù)庫(kù)。運(yùn)行前我們可以把SQL SERVER的statistics I/O狀態(tài)打開(kāi)。

(1)select title,price from titles where title_id in (select title_id from sales where qty>30)

該句的執(zhí)行結(jié)果為:

表 'sales'。掃描計(jì)數(shù) 18,邏輯讀 56 次,物理讀 0 次,預(yù)讀 0 次。

表 'titles'。掃描計(jì)數(shù) 1,邏輯讀 2 次,物理讀 0 次,預(yù)讀 0 次。



(2)select title,price from titles where exists (select * from sales where sales.title_id=titles.title_id and qty>30)

第二句的執(zhí)行結(jié)果為:

表 'sales'。掃描計(jì)數(shù) 18,邏輯讀 56 次,物理讀 0 次,預(yù)讀 0 次。

表 'titles'。掃描計(jì)數(shù) 1,邏輯讀 2 次,物理讀 0 次,預(yù)讀 0 次。

我們從此可以看到用exists和用in的執(zhí)行效率是一樣的。

7、用函數(shù)charindex()和前面加通配符%的LIKE執(zhí)行效率一樣

前面,我們談到,如果在LIKE前面加上通配符%,那么將會(huì)引起全表掃描,所以其執(zhí)行效率是低下的。但有的資料介紹說(shuō),用函數(shù)charindex()來(lái)代替LIKE速度會(huì)有大的提升,經(jīng)我試驗(yàn),發(fā)現(xiàn)這種說(shuō)明也是錯(cuò)誤的:

select gid,title,fariqi,reader from tgongwen where charindex('刑偵支隊(duì)',reader)>0 and fariqi>'2004-5-5'

用時(shí):7秒,另外:掃描計(jì)數(shù) 4,邏輯讀 7155 次,物理讀 0 次,預(yù)讀 0 次。

select gid,title,fariqi,reader from tgongwen where reader like '%' + '刑偵支隊(duì)' + '%' and fariqi>'2004-5-5'

用時(shí):7秒,另外:掃描計(jì)數(shù) 4,邏輯讀 7155 次,物理讀 0 次,預(yù)讀 0 次。

8、union并不絕對(duì)比or的執(zhí)行效率高

我們前面已經(jīng)談到了在where子句中使用or會(huì)引起全表掃描,一般的,我所見(jiàn)過(guò)的資料都是推薦這里用union來(lái)代替or。事實(shí)證明,這種說(shuō)法對(duì)于大部分都是適用的。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16' or gid>9990000

用時(shí):68秒。掃描計(jì)數(shù) 1,邏輯讀 404008 次,物理讀 283 次,預(yù)讀 392163 次。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'

union

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid>9990000

用時(shí):9秒。掃描計(jì)數(shù) 8,邏輯讀 67489 次,物理讀 216 次,預(yù)讀 7499 次。

看來(lái),用union在通常情況下比用or的效率要高的多。

但經(jīng)過(guò)試驗(yàn),筆者發(fā)現(xiàn)如果or兩邊的查詢列是一樣的話,那么用union則反倒和用or的執(zhí)行速度差很多,雖然這里union掃描的是索引,而or掃描的是全表。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16' or fariqi='2004-2-5'

用時(shí):6423毫秒。掃描計(jì)數(shù) 2,邏輯讀 14726 次,物理讀 1 次,預(yù)讀 7176 次。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'

union

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-2-5'

用時(shí):11640毫秒。掃描計(jì)數(shù) 8,邏輯讀 14806 次,物理讀 108 次,預(yù)讀 1144 次。

9、字段提取要按照“需多少、提多少”的原則,避免“select *”

我們來(lái)做一個(gè)試驗(yàn):

select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc

用時(shí):4673毫秒

select top 10000 gid,fariqi,title from tgongwen order by gid desc

用時(shí):1376毫秒

select top 10000 gid,fariqi from tgongwen order by gid desc

用時(shí):80毫秒

由此看來(lái),我們每少提取一個(gè)字段,數(shù)據(jù)的提取速度就會(huì)有相應(yīng)的提升。提升的速度還要看您舍棄的字段的大小來(lái)判斷。

10、count(*)不比count(字段)慢

某些資料上說(shuō):用*會(huì)統(tǒng)計(jì)所有列,顯然要比一個(gè)世界的列名效率低。這種說(shuō)法其實(shí)是沒(méi)有根據(jù)的。我們來(lái)看:

select count(*) from Tgongwen

用時(shí):1500毫秒

select count(gid) from Tgongwen

用時(shí):1483毫秒

select count(fariqi) from Tgongwen

用時(shí):3140毫秒

select count(title) from Tgongwen

用時(shí):52050毫秒

從以上可以看出,如果用count(*)和用count(主鍵)的速度是相當(dāng)?shù)模鴆ount(*)卻比其他任何除主鍵以外的字段匯總速度要快,而且字段越長(zhǎng),匯總的速度就越慢。我想,如果用count(*), SQL SERVER可能會(huì)自動(dòng)查找最小字段來(lái)匯總的。當(dāng)然,如果您直接寫(xiě)count(主鍵)將會(huì)來(lái)的更直接些。

11、order by按聚集索引列排序效率最高

我們來(lái)看:(gid是主鍵,fariqi是聚合索引列)

select top 10000 gid,fariqi,reader,title from tgongwen

用時(shí):196 毫秒。 掃描計(jì)數(shù) 1,邏輯讀 289 次,物理讀 1 次,預(yù)讀 1527 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by gid asc

用時(shí):4720毫秒。 掃描計(jì)數(shù) 1,邏輯讀 41956 次,物理讀 0 次,預(yù)讀 1287 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc

用時(shí):4736毫秒。 掃描計(jì)數(shù) 1,邏輯讀 55350 次,物理讀 10 次,預(yù)讀 775 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi asc

用時(shí):173毫秒。 掃描計(jì)數(shù) 1,邏輯讀 290 次,物理讀 0 次,預(yù)讀 0 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi desc

用時(shí):156毫秒。 掃描計(jì)數(shù) 1,邏輯讀 289 次,物理讀 0 次,預(yù)讀 0 次。

從以上我們可以看出,不排序的速度以及邏輯讀次數(shù)都是和“order by 聚集索引列” 的速度是相當(dāng)?shù)模@些都比“order by 非聚集索引列”的查詢速度是快得多的。

同時(shí),按照某個(gè)字段進(jìn)行排序的時(shí)候,無(wú)論是正序還是倒序,速度是基本相當(dāng)?shù)摹?

12、高效的TOP

事實(shí)上,在查詢和提取超大容量的數(shù)據(jù)集時(shí),影響數(shù)據(jù)庫(kù)響應(yīng)時(shí)間的最大因素不是數(shù)據(jù)查找,而是物理的I/0操作。如:

select top 10 * from (

select top 10000 gid,fariqi,title from tgongwen

where neibuyonghu='辦公室'

order by gid desc) as a

order by gid asc

這條語(yǔ)句,從理論上講,整條語(yǔ)句的執(zhí)行時(shí)間應(yīng)該比子句的執(zhí)行時(shí)間長(zhǎng),但事實(shí)相反。因?yàn)椋泳鋱?zhí)行后返回的是10000條記錄,而整條語(yǔ)句僅返回10條語(yǔ)句,所以影響數(shù)據(jù)庫(kù)響應(yīng)時(shí)間最大的因素是物理I/O操作。而限制物理I/O操作此處的最有效方法之一就是使用TOP關(guān)鍵詞了。TOP關(guān)鍵詞是SQL SERVER中經(jīng)過(guò)系統(tǒng)優(yōu)化過(guò)的一個(gè)用來(lái)提取前幾條或前幾個(gè)百分比數(shù)據(jù)的詞。經(jīng)筆者在實(shí)踐中的應(yīng)用,發(fā)現(xiàn)TOP確實(shí)很好用,效率也很高。但這個(gè)詞在另外一個(gè)大型數(shù)據(jù)庫(kù)ORACLE中卻沒(méi)有,這不能說(shuō)不是一個(gè)遺憾,雖然在ORACLE中可以用其他方法(如:rownumber)來(lái)解決。在以后的關(guān)于“實(shí)現(xiàn)千萬(wàn)級(jí)數(shù)據(jù)的分頁(yè)顯示存儲(chǔ)過(guò)程”的討論中,我們就將用到TOP這個(gè)關(guān)鍵詞。



到此為止,我們上面討論了如何實(shí)現(xiàn)從大容量的數(shù)據(jù)庫(kù)中快速地查詢出您所需要的數(shù)據(jù)方法。當(dāng)然,我們介紹的這些方法都是“軟”方法,在實(shí)踐中,我們還要考慮各種“硬”因素,如:網(wǎng)絡(luò)性能、服務(wù)器的性能、操作系統(tǒng)的性能,甚至網(wǎng)卡、交換機(jī)等。



三、實(shí)現(xiàn)小數(shù)據(jù)量和海量數(shù)據(jù)的通用分頁(yè)顯示存儲(chǔ)過(guò)程

建立一個(gè)web 應(yīng)用,分頁(yè)瀏覽功能必不可少。這個(gè)問(wèn)題是數(shù)據(jù)庫(kù)處理中十分常見(jiàn)的問(wèn)題。經(jīng)典的數(shù)據(jù)分頁(yè)方法是:ADO 紀(jì)錄集分頁(yè)法,也就是利用ADO自帶的分頁(yè)功能(利用游標(biāo))來(lái)實(shí)現(xiàn)分頁(yè)。但這種分頁(yè)方法僅適用于較小數(shù)據(jù)量的情形,因?yàn)橛螛?biāo)本身有缺點(diǎn):游標(biāo)是存放在內(nèi)存中,很費(fèi)內(nèi)存。游標(biāo)一建立,就將相關(guān)的記錄鎖住,直到取消游標(biāo)。游標(biāo)提供了對(duì)特定集合中逐行掃描的手段,一般使用游標(biāo)來(lái)逐行遍歷數(shù)據(jù),根據(jù)取出數(shù)據(jù)條件的不同進(jìn)行不同的操作。而對(duì)于多表和大表中定義的游標(biāo)(大的數(shù)據(jù)集合)循環(huán)很容易使程序進(jìn)入一個(gè)漫長(zhǎng)的等待甚至死機(jī)。

更重要的是,對(duì)于非常大的數(shù)據(jù)模型而言,分頁(yè)檢索時(shí),如果按照傳統(tǒng)的每次都加載整個(gè)數(shù)據(jù)源的方法是非常浪費(fèi)資源的。現(xiàn)在流行的分頁(yè)方法一般是檢索頁(yè)面大小的塊區(qū)的數(shù)據(jù),而非檢索所有的數(shù)據(jù),然后單步執(zhí)行當(dāng)前行。

最早較好地實(shí)現(xiàn)這種根據(jù)頁(yè)面大小和頁(yè)碼來(lái)提取數(shù)據(jù)的方法大概就是“俄羅斯存儲(chǔ)過(guò)程”。這個(gè)存儲(chǔ)過(guò)程用了游標(biāo),由于游標(biāo)的局限性,所以這個(gè)方法并沒(méi)有得到大家的普遍認(rèn)可。

后來(lái),網(wǎng)上有人改造了此存儲(chǔ)過(guò)程,下面的存儲(chǔ)過(guò)程就是結(jié)合我們的辦公自動(dòng)化實(shí)例寫(xiě)的分頁(yè)存儲(chǔ)過(guò)程:

CREATE procedure pagination1

(@pagesize int, --頁(yè)面大小,如每頁(yè)存儲(chǔ)20條記錄

@pageindex int --當(dāng)前頁(yè)碼

)

as

set nocount on

begin

declare @indextable table(id int identity(1,1),nid int) --定義表變量

declare @PageLowerBound int --定義此頁(yè)的底碼

declare @PageUpperBound int --定義此頁(yè)的頂碼

set @PageLowerBound=(@pageindex-1)*@pagesize

set @PageUpperBound=@PageLowerBound+@pagesize

set rowcount @PageUpperBound

insert into @indextable(nid) select gid from TGongwen where fariqi >dateadd(day,-365,getdate()) order by fariqi desc

select O.gid,O.mid,O.title,O.fadanwei,O.fariqi from TGongwen O,@indextable t where O.gid=t.nid

and t.id>@PageLowerBound and t.id<=@PageUpperBound order by t.id

end

set nocount off

以上存儲(chǔ)過(guò)程運(yùn)用了SQL SERVER的最新技術(shù)――表變量。應(yīng)該說(shuō)這個(gè)存儲(chǔ)過(guò)程也是一個(gè)非常優(yōu)秀的分頁(yè)存儲(chǔ)過(guò)程。當(dāng)然,在這個(gè)過(guò)程中,您也可以把其中的表變量寫(xiě)成臨時(shí)表:CREATE TABLE #Temp。但很明顯,在SQL SERVER中,用臨時(shí)表是沒(méi)有用表變量快的。所以筆者剛開(kāi)始使用這個(gè)存儲(chǔ)過(guò)程時(shí),感覺(jué)非常的不錯(cuò),速度也比原來(lái)的ADO的好。但后來(lái),我又發(fā)現(xiàn)了比此方法更好的方法。

筆者曾在網(wǎng)上看到了一篇小短文《從數(shù)據(jù)表中取出第n條到第m條的記錄的方法》,全文如下:

從publish 表中取出第 n 條到第 m 條的記錄:
SELECT TOP m-n+1 *
FROM publish
WHERE (id NOT IN
    (SELECT TOP n-1 id
     FROM publish))

id 為publish 表的關(guān)鍵字

我當(dāng)時(shí)看到這篇文章的時(shí)候,真的是精神為之一振,覺(jué)得思路非常得好。等到后來(lái),我在作辦公自動(dòng)化系統(tǒng)(ASP.NET+ C#+SQL SERVER)的時(shí)候,忽然想起了這篇文章,我想如果把這個(gè)語(yǔ)句改造一下,這就可能是一個(gè)非常好的分頁(yè)存儲(chǔ)過(guò)程。于是我就滿網(wǎng)上找這篇文章,沒(méi)想到,文章還沒(méi)找到,卻找到了一篇根據(jù)此語(yǔ)句寫(xiě)的一個(gè)分頁(yè)存儲(chǔ)過(guò)程,這個(gè)存儲(chǔ)過(guò)程也是目前較為流行的一種分頁(yè)存儲(chǔ)過(guò)程,我很后悔沒(méi)有爭(zhēng)先把這段文字改造成存儲(chǔ)過(guò)程:

CREATE PROCEDURE pagination2
(
@SQL nVARCHAR(4000), --不帶排序語(yǔ)句的SQL語(yǔ)句
@Page int, --頁(yè)碼
@RecsPerPage int, --每頁(yè)容納的記錄數(shù)
@ID VARCHAR(255), --需要排序的不重復(fù)的ID號(hào)
@Sort VARCHAR(255) --排序字段及規(guī)則
)
AS

DECLARE @Str nVARCHAR(4000)

SET @Str='SELECT TOP '+CAST(@RecsPerPage AS VARCHAR(20))+' * FROM ('+@SQL+') T WHERE T.'+@ID+'NOT IN
(SELECT TOP '+CAST((@RecsPerPage*(@Page-1)) AS VARCHAR(20))+' '+@ID+' FROM ('+@SQL+') T9 ORDER BY '+@Sort+') ORDER BY '+@Sort

PRINT @Str

EXEC sp_ExecuteSql @Str
GO

其實(shí),以上語(yǔ)句可以簡(jiǎn)化為:

SELECT TOP 頁(yè)大小 *

FROM Table1

WHERE (ID NOT IN

(SELECT TOP 頁(yè)大小*頁(yè)數(shù) id

FROM 表

ORDER BY id))

ORDER BY ID

但這個(gè)存儲(chǔ)過(guò)程有一個(gè)致命的缺點(diǎn),就是它含有NOT IN字樣。雖然我可以把它改造為:

SELECT TOP 頁(yè)大小 *

FROM Table1

WHERE not exists

(select * from (select top (頁(yè)大小*頁(yè)數(shù)) * from table1 order by id) b where b.id=a.id )

order by id

即,用not exists來(lái)代替not in,但我們前面已經(jīng)談過(guò)了,二者的執(zhí)行效率實(shí)際上是沒(méi)有區(qū)別的。

既便如此,用TOP 結(jié)合NOT IN的這個(gè)方法還是比用游標(biāo)要來(lái)得快一些。

雖然用not exists并不能挽救上個(gè)存儲(chǔ)過(guò)程的效率,但使用SQL SERVER中的TOP關(guān)鍵字卻是一個(gè)非常明智的選擇。因?yàn)榉猪?yè)優(yōu)化的最終目的就是避免產(chǎn)生過(guò)大的記錄集,而我們?cè)谇懊嬉惨呀?jīng)提到了TOP的優(yōu)勢(shì),通過(guò)TOP 即可實(shí)現(xiàn)對(duì)數(shù)據(jù)量的控制。

在分頁(yè)算法中,影響我們查詢速度的關(guān)鍵因素有兩點(diǎn):TOP和NOT IN。TOP可以提高我們的查詢速度,而NOT IN會(huì)減慢我們的查詢速度,所以要提高我們整個(gè)分頁(yè)算法的速度,就要徹底改造NOT IN,同其他方法來(lái)替代它。

我們知道,幾乎任何字段,我們都可以通過(guò)max(字段)或min(字段)來(lái)提取某個(gè)字段中的最大或最小值,所以如果這個(gè)字段不重復(fù),那么就可以利用這些不重復(fù)的字段的max或min作為分水嶺,使其成為分頁(yè)算法中分開(kāi)每頁(yè)的參照物。在這里,我們可以用操作符“>”或“<”號(hào)來(lái)完成這個(gè)使命,使查詢語(yǔ)句符合SARG形式。如:

Select top 10 * from table1 where id>200

于是就有了如下分頁(yè)方案:

select top 頁(yè)大小 *

from table1

where id>

(select max (id) from

(select top ((頁(yè)碼-1)*頁(yè)大小) id from table1 order by id) as T

)

order by id

在選擇即不重復(fù)值,又容易分辨大小的列時(shí),我們通常會(huì)選擇主鍵。下表列出了筆者用有著1000萬(wàn)數(shù)據(jù)的辦公自動(dòng)化系統(tǒng)中的表,在以GID(GID是主鍵,但并不是聚集索引。)為排序列、提取gid,fariqi,title字段,分別以第1、10、100、500、1000、1萬(wàn)、10萬(wàn)、25萬(wàn)、50萬(wàn)頁(yè)為例,測(cè)試以上三種分頁(yè)方案的執(zhí)行速度:(單位:毫秒)

頁(yè) 碼
方案1
方案2
方案3

1
60
30
76

10
46
16
63

100
1076
720
130

500
540
12943
83

1000
17110
470
250

1萬(wàn)
24796
4500
140

10萬(wàn)
38326
42283
1553

25萬(wàn)
28140
128720
2330

50萬(wàn)
121686
127846
7168


從上表中,我們可以看出,三種存儲(chǔ)過(guò)程在執(zhí)行100頁(yè)以下的分頁(yè)命令時(shí),都是可以信任的,速度都很好。但第一種方案在執(zhí)行分頁(yè)1000頁(yè)以上后,速度就降了下來(lái)。第二種方案大約是在執(zhí)行分頁(yè)1萬(wàn)頁(yè)以上后速度開(kāi)始降了下來(lái)。而第三種方案卻始終沒(méi)有大的降勢(shì),后勁仍然很足。

在確定了第三種分頁(yè)方案后,我們可以據(jù)此寫(xiě)一個(gè)存儲(chǔ)過(guò)程。大家知道SQL SERVER的存儲(chǔ)過(guò)程是事先編譯好的SQL語(yǔ)句,它的執(zhí)行效率要比通過(guò)WEB頁(yè)面?zhèn)鱽?lái)的SQL語(yǔ)句的執(zhí)行效率要高。下面的存儲(chǔ)過(guò)程不僅含有分頁(yè)方案,還會(huì)根據(jù)頁(yè)面?zhèn)鱽?lái)的參數(shù)來(lái)確定是否進(jìn)行數(shù)據(jù)總數(shù)統(tǒng)計(jì)。

-- 獲取指定頁(yè)的數(shù)據(jù)

CREATE PROCEDURE pagination3

@tblName varchar(255), -- 表名

@strGetFields varchar(1000) = '*', -- 需要返回的列

@fldName varchar(255)='', -- 排序的字段名

@PageSize int = 10, -- 頁(yè)尺寸

@PageIndex int = 1, -- 頁(yè)碼

@doCount bit = 0, -- 返回記錄總數(shù), 非 0 值則返回

@OrderType bit = 0, -- 設(shè)置排序類(lèi)型, 非 0 值則降序

@strWhere varchar(1500) = '' -- 查詢條件 (注意: 不要加 where)

AS

declare @strSQL varchar(5000) -- 主語(yǔ)句

declare @strTmp varchar(110) -- 臨時(shí)變量

declare @strOrder varchar(400) -- 排序類(lèi)型



if @doCount != 0

begin

if @strWhere !=''

set @strSQL = "select count(*) as Total from [" + @tblName + "] where "+@strWhere

else

set @strSQL = "select count(*) as Total from [" + @tblName + "]"

end

--以上代碼的意思是如果@doCount傳遞過(guò)來(lái)的不是0,就執(zhí)行總數(shù)統(tǒng)計(jì)。以下的所有代碼都是@doCount為0的情況

else

begin



if @OrderType != 0

begin

set @strTmp = "<(select min"

set @strOrder = " order by [" + @fldName +"] desc"

--如果@OrderType不是0,就執(zhí)行降序,這句很重要!

end

else

begin

set @strTmp = ">(select max"

set @strOrder = " order by [" + @fldName +"] asc"

end



if @PageIndex = 1

begin

if @strWhere != ''

set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ " from [" + @tblName + "] where " + @strWhere + " " + @strOrder

else

set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ " from ["+ @tblName + "] "+ @strOrder

--如果是第一頁(yè)就執(zhí)行以上代碼,這樣會(huì)加快執(zhí)行速度

end

else

begin

--以下代碼賦予了@strSQL以真正執(zhí)行的SQL代碼

set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ " from ["

+ @tblName + "] where [" + @fldName + "]" + @strTmp + "(["+ @fldName + "]) from (select top " + str((@PageIndex-1)*@PageSize) + " ["+ @fldName + "] from [" + @tblName + "]" + @strOrder + ") as tblTmp)"+ @strOrder



if @strWhere != ''

set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ " from ["

+ @tblName + "] where [" + @fldName + "]" + @strTmp + "(["

+ @fldName + "]) from (select top " + str((@PageIndex-1)*@PageSize) + " ["

+ @fldName + "] from [" + @tblName + "] where " + @strWhere + " "

+ @strOrder + ") as tblTmp) and " + @strWhere + " " + @strOrder

end

end

exec (@strSQL)

GO

上面的這個(gè)存儲(chǔ)過(guò)程是一個(gè)通用的存儲(chǔ)過(guò)程,其注釋已寫(xiě)在其中了。

在大數(shù)據(jù)量的情況下,特別是在查詢最后幾頁(yè)的時(shí)候,查詢時(shí)間一般不會(huì)超過(guò)9秒;而用其他存儲(chǔ)過(guò)程,在實(shí)踐中就會(huì)導(dǎo)致超時(shí),所以這個(gè)存儲(chǔ)過(guò)程非常適用于大容量數(shù)據(jù)庫(kù)的查詢。

筆者希望能夠通過(guò)對(duì)以上存儲(chǔ)過(guò)程的解析,能給大家?guī)?lái)一定的啟示,并給工作帶來(lái)一定的效率提升,同時(shí)希望同行提出更優(yōu)秀的實(shí)時(shí)數(shù)據(jù)分頁(yè)算法。



四、聚集索引的重要性和如何選擇聚集索引

在上一節(jié)的標(biāo)題中,筆者寫(xiě)的是:實(shí)現(xiàn)小數(shù)據(jù)量和海量數(shù)據(jù)的通用分頁(yè)顯示存儲(chǔ)過(guò)程。這是因?yàn)樵趯⒈敬鎯?chǔ)過(guò)程應(yīng)用于“辦公自動(dòng)化”系統(tǒng)的實(shí)踐中時(shí),筆者發(fā)現(xiàn)這第三種存儲(chǔ)過(guò)程在小數(shù)據(jù)量的情況下,有如下現(xiàn)象:

1、分頁(yè)速度一般維持在1秒和3秒之間。

2、在查詢最后一頁(yè)時(shí),速度一般為5秒至8秒,哪怕分頁(yè)總數(shù)只有3頁(yè)或30萬(wàn)頁(yè)。

雖然在超大容量情況下,這個(gè)分頁(yè)的實(shí)現(xiàn)過(guò)程是很快的,但在分前幾頁(yè)時(shí),這個(gè)1-3秒的速度比起第一種甚至沒(méi)有經(jīng)過(guò)優(yōu)化的分頁(yè)方法速度還要慢,借用戶的話說(shuō)就是“還沒(méi)有ACCESS數(shù)據(jù)庫(kù)速度快”,這個(gè)認(rèn)識(shí)足以導(dǎo)致用戶放棄使用您開(kāi)發(fā)的系統(tǒng)。

筆者就此分析了一下,原來(lái)產(chǎn)生這種現(xiàn)象的癥結(jié)是如此的簡(jiǎn)單,但又如此的重要:排序的字段不是聚集索引!

本篇文章的題目是:“查詢優(yōu)化及分頁(yè)算法方案”。筆者只所以把“查詢優(yōu)化”和“分頁(yè)算法”這兩個(gè)聯(lián)系不是很大的論題放在一起,就是因?yàn)槎叨夹枰粋€(gè)非常重要的東西――聚集索引。

在前面的討論中我們已經(jīng)提到了,聚集索引有兩個(gè)最大的優(yōu)勢(shì):

1、以最快的速度縮小查詢范圍。

2、以最快的速度進(jìn)行字段排序。

第1條多用在查詢優(yōu)化時(shí),而第2條多用在進(jìn)行分頁(yè)時(shí)的數(shù)據(jù)排序。

而聚集索引在每個(gè)表內(nèi)又只能建立一個(gè),這使得聚集索引顯得更加的重要。聚集索引的挑選可以說(shuō)是實(shí)現(xiàn)“查詢優(yōu)化”和“高效分頁(yè)”的最關(guān)鍵因素。

但要既使聚集索引列既符合查詢列的需要,又符合排序列的需要,這通常是一個(gè)矛盾。

筆者前面“索引”的討論中,將fariqi,即用戶發(fā)文日期作為了聚集索引的起始列,日期的精確度為“日”。這種作法的優(yōu)點(diǎn),前面已經(jīng)提到了,在進(jìn)行劃時(shí)間段的快速查詢中,比用ID主鍵列有很大的優(yōu)勢(shì)。

但在分頁(yè)時(shí),由于這個(gè)聚集索引列存在著重復(fù)記錄,所以無(wú)法使用max或min來(lái)最為分頁(yè)的參照物,進(jìn)而無(wú)法實(shí)現(xiàn)更為高效的排序。而如果將ID主鍵列作為聚集索引,那么聚集索引除了用以排序之外,沒(méi)有任何用處,實(shí)際上是浪費(fèi)了聚集索引這個(gè)寶貴的資源。

為解決這個(gè)矛盾,筆者后來(lái)又添加了一個(gè)日期列,其默認(rèn)值為getdate()。用戶在寫(xiě)入記錄時(shí),這個(gè)列自動(dòng)寫(xiě)入當(dāng)時(shí)的時(shí)間,時(shí)間精確到毫秒。即使這樣,為了避免可能性很小的重合,還要在此列上創(chuàng)建UNIQUE約束。將此日期列作為聚集索引列。

有了這個(gè)時(shí)間型聚集索引列之后,用戶就既可以用這個(gè)列查找用戶在插入數(shù)據(jù)時(shí)的某個(gè)時(shí)間段的查詢,又可以作為唯一列來(lái)實(shí)現(xiàn)max或min,成為分頁(yè)算法的參照物。

經(jīng)過(guò)這樣的優(yōu)化,筆者發(fā)現(xiàn),無(wú)論是大數(shù)據(jù)量的情況下還是小數(shù)據(jù)量的情況下,分頁(yè)速度一般都是幾十毫秒,甚至0毫秒。而用日期段縮小范圍的查詢速度比原來(lái)也沒(méi)有任何遲鈍。

聚集索引是如此的重要和珍貴,所以筆者總結(jié)了一下,一定要將聚集索引建立在:

1、您最頻繁使用的、用以縮小查詢范圍的字段上;

2、您最頻繁使用的、需要排序的字段上。



結(jié)束語(yǔ):

本篇文章匯集了筆者近段在使用數(shù)據(jù)庫(kù)方面的心得,是在做“辦公自動(dòng)化”系統(tǒng)時(shí)實(shí)踐經(jīng)驗(yàn)的積累。希望這篇文章不僅能夠給大家的工作帶來(lái)一定的幫助,也希望能讓大家能夠體會(huì)到分析問(wèn)題的方法;最重要的是,希望這篇文章能夠拋磚引玉,掀起大家的學(xué)習(xí)和討論的興趣,以共同促進(jìn),共同為公安科技強(qiáng)警事業(yè)和金盾工程做出自己最大的努力。

最后需要說(shuō)明的是,在試驗(yàn)中,我發(fā)現(xiàn)用戶在進(jìn)行大數(shù)據(jù)量查詢的時(shí)候,對(duì)數(shù)據(jù)庫(kù)速度影響最大的不是內(nèi)存大小,而是CPU。在我的P4 2.4機(jī)器上試驗(yàn)的時(shí)候,查看“資源管理器”,CPU經(jīng)常出現(xiàn)持續(xù)到100%的現(xiàn)象,而內(nèi)存用量卻并沒(méi)有改變或者說(shuō)沒(méi)有大的改變。即使在我們的HP ML 350 G3服務(wù)器上試驗(yàn)時(shí),CPU峰值也能達(dá)到90%,一般持續(xù)在70%左右。

本文的試驗(yàn)數(shù)據(jù)都是來(lái)自我們的HP ML 350服務(wù)器。服務(wù)器配置:雙Inter Xeon 超線程 CPU 2.4G,內(nèi)存1G,操作系統(tǒng)Windows Server 2003 Enterprise Edition,數(shù)據(jù)庫(kù)SQL Server 2000 SP3。 

TiGERTiAN 2007-05-19 16:18 發(fā)表評(píng)論
]]>
什么是數(shù)據(jù)庫(kù)事務(wù)http://www.aygfsteel.com/TiGERTiAN/archive/2007/05/10/116610.htmlTiGERTiANTiGERTiANThu, 10 May 2007 14:43:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2007/05/10/116610.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/116610.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2007/05/10/116610.html#Feedback2http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/116610.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/116610.html  數(shù)據(jù)庫(kù)事務(wù)是指作為單個(gè)邏輯工作單元執(zhí)行的一系列操作。

設(shè)想網(wǎng)上購(gòu)物的一次交易,其付款過(guò)程至少包括以下幾步數(shù)據(jù)庫(kù)操作:

  · 更新客戶所購(gòu)商品的庫(kù)存信息

  · 保存客戶付款信息--可能包括與銀行系統(tǒng)的交互

  · 生成訂單并且保存到數(shù)據(jù)庫(kù)中

  · 更新用戶相關(guān)信息,例如購(gòu)物數(shù)量等等

正常的情況下,這些操作將順利進(jìn)行,最終交易成功,與交易相關(guān)的所有數(shù)據(jù)庫(kù)信息也成功地更新。但是,如果在這一系列過(guò)程中任何一個(gè)環(huán)節(jié)出了差錯(cuò),例如在更新商品庫(kù)存信息時(shí)發(fā)生異常、該顧客銀行帳戶存款不足等,都將導(dǎo)致交易失敗。一旦交易失敗,數(shù)據(jù)庫(kù)中所有信息都必須保持交易前的狀態(tài)不變,比如最后一步更新用戶信息時(shí)失敗而導(dǎo)致交易失敗,那么必須保證這筆失敗的交易不影響數(shù)據(jù)庫(kù)的狀態(tài)--庫(kù)存信息沒(méi)有被更新、用戶也沒(méi)有付款,訂單也沒(méi)有生成。否則,數(shù)據(jù)庫(kù)的信息將會(huì)一片混亂而不可預(yù)測(cè)。

數(shù)據(jù)庫(kù)事務(wù)正是用來(lái)保證這種情況下交易的平穩(wěn)性和可預(yù)測(cè)性的技術(shù)。

  數(shù)據(jù)庫(kù)事務(wù)的ACID屬性

事務(wù)處理可以確保除非事務(wù)性單元內(nèi)的所有操作都成功完成,否則不會(huì)永久更新面向數(shù)據(jù)的資源。通過(guò)將一組相關(guān)操作組合為一個(gè)要么全部成功要么全部失敗的單元,可以簡(jiǎn)化錯(cuò)誤恢復(fù)并使應(yīng)用程序更加可靠。一個(gè)邏輯工作單元要成為事務(wù),必須滿足所謂的ACID(原子性、一致性、隔離性和持久性)屬性:

  · 原子性

事務(wù)必須是原子工作單元;對(duì)于其數(shù)據(jù)修改,要么全都執(zhí)行,要么全都不執(zhí)行。通常,與某個(gè)事務(wù)關(guān)聯(lián)的操作具有共同的目標(biāo),并且是相互依賴的。如果系統(tǒng)只執(zhí)行這些操作的一個(gè)子集,則可能會(huì)破壞事務(wù)的總體目標(biāo)。原子性消除了系統(tǒng)處理操作子集的可能性。

  · 一致性

事務(wù)在完成時(shí),必須使所有的數(shù)據(jù)都保持一致?tīng)顟B(tài)。在相關(guān)數(shù)據(jù)庫(kù)中,所有規(guī)則都必須應(yīng)用于事務(wù)的修改,以保持所有數(shù)據(jù)的完整性。事務(wù)結(jié)束時(shí),所有的內(nèi)部數(shù)據(jù)結(jié)構(gòu)(如 B 樹(shù)索引或雙向鏈表)都必須是正確的。某些維護(hù)一致性的責(zé)任由應(yīng)用程序開(kāi)發(fā)人員承擔(dān),他們必須確保應(yīng)用程序已強(qiáng)制所有已知的完整性約束。例如,當(dāng)開(kāi)發(fā)用于轉(zhuǎn)帳的應(yīng)用程序時(shí),應(yīng)避免在轉(zhuǎn)帳過(guò)程中任意移動(dòng)小數(shù)點(diǎn)。

  · 隔離性

由并發(fā)事務(wù)所作的修改必須與任何其它并發(fā)事務(wù)所作的修改隔離。事務(wù)查看數(shù)據(jù)時(shí)數(shù)據(jù)所處的狀態(tài),要么是另一并發(fā)事務(wù)修改它之前的狀態(tài),要么是另一事務(wù)修改它之后的狀態(tài),事務(wù)不會(huì)查看中間狀態(tài)的數(shù)據(jù)。這稱為可串行性,因?yàn)樗軌蛑匦卵b載起始數(shù)據(jù),并且重播一系列事務(wù),以使數(shù)據(jù)結(jié)束時(shí)的狀態(tài)與原始事務(wù)執(zhí)行的狀態(tài)相同。當(dāng)事務(wù)可序列化時(shí)將獲得最高的隔離級(jí)別。在此級(jí)別上,從一組可并行執(zhí)行的事務(wù)獲得的結(jié)果與通過(guò)連續(xù)運(yùn)行每個(gè)事務(wù)所獲得的結(jié)果相同。由于高度隔離會(huì)限制可并行執(zhí)行的事務(wù)數(shù),所以一些應(yīng)用程序降低隔離級(jí)別以換取更大的吞吐量。

  · 持久性

 事務(wù)完成之后,它對(duì)于系統(tǒng)的影響是永久性的。該修改即使出現(xiàn)致命的系統(tǒng)故障也將一直保持。
DBMS的責(zé)任和我們的任務(wù)

企業(yè)級(jí)的數(shù)據(jù)庫(kù)管理系統(tǒng)(DBMS)都有責(zé)任提供一種保證事務(wù)的物理完整性的機(jī)制。就常用的SQL Server2000系統(tǒng)而言,它具備鎖定設(shè)備隔離事務(wù)、記錄設(shè)備保證事務(wù)持久性等機(jī)制。因此,我們不必關(guān)心數(shù)據(jù)庫(kù)事務(wù)的物理完整性,而應(yīng)該關(guān)注在什么情況下使用數(shù)據(jù)庫(kù)事務(wù)、事務(wù)對(duì)性能的影響,如何使用事務(wù)等等。

本文將涉及到在.net框架下使用C#語(yǔ)言操縱數(shù)據(jù)庫(kù)事務(wù)的各個(gè)方面。

+++@@@+++

體驗(yàn)SQL語(yǔ)言的事務(wù)機(jī)制

作為大型的企業(yè)級(jí)數(shù)據(jù)庫(kù),SQL Server2000對(duì)事務(wù)提供了很好的支持。我們可以使用SQL語(yǔ)句來(lái)定義、提交以及回滾一個(gè)事務(wù)。

如下所示的SQL代碼定義了一個(gè)事務(wù),并且命名為"MyTransaction"(限于篇幅,本文并不討論如何編寫(xiě)SQL語(yǔ)言程序,請(qǐng)讀者自行參考相關(guān)書(shū)籍):

DECLARE @TranName VARCHAR(20)

Select @TranName = ''''MyTransaction''''
BEGIN TRANSACTION @TranNameGOUSE pubs
GO

Update roysched
SET royalty = royalty * 1.10
Where title_id LIKE ''''Pc%''''
GO

COMMIT TRANSACTION MyTransaction
GO

這里用到了SQL Server2000自帶的示例數(shù)據(jù)庫(kù)pubs,提交事務(wù)后,將為所有暢銷(xiāo)計(jì)算機(jī)書(shū)籍支付的版稅增加 10%。

打開(kāi)SQL Server2000的查詢分析器,選擇pubs數(shù)據(jù)庫(kù),然后運(yùn)行這段程序,結(jié)果顯而易見(jiàn)。

可是如何在C#程序中運(yùn)行呢?我們記得在普通的SQL查詢中,一般需要把查詢語(yǔ)句賦值給SalCommand.CommandText屬性,這里也就像普通的SQL查詢語(yǔ)句一樣,將這些語(yǔ)句賦給SqlCommand.CommandText屬性即可。要注意的一點(diǎn)是,其中的"GO"語(yǔ)句標(biāo)志著SQL批處理的結(jié)束,編寫(xiě)SQL腳本是需要的,但是在這里是不必要的。我們可以編寫(xiě)如下的程序來(lái)驗(yàn)證這個(gè)想法:

//TranSql.csusing System;
using System.Data;
using System.Data.SqlClient;
namespace Aspcn
{
 public class DbTranSql
 {
  file://將事務(wù)放到SQL Server中執(zhí)行
  public void DoTran()
  {
   file://建立連接并打開(kāi)
   SqlConnection myConn=GetConn();myConn.Open();
   SqlCommand myComm=new SqlCommand();
   try
   {
    myComm.Connection=myConn;
    myComm.CommandText="DECLARE @TranName VARCHAR(20) ";
    myComm.CommandText+="Select @TranName = ''''MyTransaction'''' ";
    myComm.CommandText+="BEGIN TRANSACTION @TranName ";
    myComm.CommandText+="USE pubs ";
    myComm.CommandText+="Update roysched SET royalty = royalty * 1.10 Where title_id LIKE ''''Pc%'''' ";
    myComm.CommandText+="COMMIT TRANSACTION MyTransaction ";
    myComm.ExecuteNonQuery();
   }
   catch(Exception err)
   {
    throw new ApplicationException("事務(wù)操作出錯(cuò),系統(tǒng)信息:"+err.Message);
   }
   finally
   {
    myConn.Close();
   }
  }
  file://獲取數(shù)據(jù)連接
  private SqlConnection GetConn()
  {
   string strSql="Data Source=localhost;Integrated Security=SSPI;user id=sa;password=";
   SqlConnection myConn=new SqlConnection(strSql);
   return myConn;
  }
 }

 public class Test
 {
  public static void Main()
  {
   DbTranSql tranTest=new DbTranSql();
   tranTest.DoTran();
   Console.WriteLine("事務(wù)處理已經(jīng)成功完成。");
   Console.ReadLine();
  }
 }
}


  注意到其中的SqlCommand對(duì)象myComm,它的CommandText屬性僅僅是前面SQL代碼字符串連接起來(lái)即可,當(dāng)然,其中的"GO"語(yǔ)句已經(jīng)全部去掉了。這個(gè)語(yǔ)句就像普通的查詢一樣,程序?qū)QL文本事實(shí)上提交給DBMS去處理了,然后接收返回的結(jié)果(如果有結(jié)果返回的話)。

很自然,我們最后看到了輸出"事務(wù)處理已經(jīng)成功完成",再用企業(yè)管理器查看pubs數(shù)據(jù)庫(kù)的roysched表,所有title_id字段以"PC"開(kāi)頭的書(shū)籍的royalty字段的值都增加了0.1倍。

這里,我們并沒(méi)有使用ADO.net的事務(wù)處理機(jī)制,而是簡(jiǎn)單地將執(zhí)行事務(wù)的SQL語(yǔ)句當(dāng)作普通的查詢來(lái)執(zhí)行,因此,事實(shí)上該事務(wù)完全沒(méi)有用到.net的相關(guān)特性。
了解.net中的事務(wù)機(jī)制

如你所知,在.net框架中主要有兩個(gè)命名空間(namespace)用于應(yīng)用程序同數(shù)據(jù)庫(kù)系統(tǒng)的交互:System.Data.SqlClient和System.Data.OleDb。前者專門(mén)用于連接Microsoft公司自己的SQL Server數(shù)據(jù)庫(kù),而后者可以適應(yīng)多種不同的數(shù)據(jù)庫(kù)。這兩個(gè)命名空間中都包含有專門(mén)用于管理數(shù)據(jù)庫(kù)事務(wù)的類(lèi),分別是System.Data.SqlClient.SqlTranscation類(lèi)和System.Data.OleDb.OleDbTranscation類(lèi)。

就像它們的名字一樣,這兩個(gè)類(lèi)大部分功能是一樣的,二者之間的主要差別在于它們的連接機(jī)制,前者提供一組直接調(diào)用 SQL Server 的對(duì)象,而后者使用本機(jī) OLE DB 啟用數(shù)據(jù)訪問(wèn)。 事實(shí)上,ADO.net 事務(wù)完全在數(shù)據(jù)庫(kù)的內(nèi)部處理,且不受 Microsoft 分布式事務(wù)處理協(xié)調(diào)器 (DTC) 或任何其他事務(wù)性機(jī)制的支持。本文將主要介紹System.Data.SqlClient.SqlTranscation類(lèi),下面的段落中,除了特別注明,都將使用System.Data.SqlClient.SqlTranscation類(lèi)。

+++@@@+++

事務(wù)的開(kāi)啟和提交

現(xiàn)在我們對(duì)事務(wù)的概念和原理都了然于心了,并且作為已經(jīng)有一些基礎(chǔ)的C#開(kāi)發(fā)者,我們已經(jīng)熟知編寫(xiě)數(shù)據(jù)庫(kù)交互程序的一些要點(diǎn),即使用SqlConnection類(lèi)的對(duì)象的Open()方法建立與數(shù)據(jù)庫(kù)服務(wù)器的連接,然后將該連接賦給SqlCommand對(duì)象的Connection屬性,將欲執(zhí)行的SQL語(yǔ)句賦給它的CommandText屬性,于是就可以通過(guò)SqlCommand對(duì)象進(jìn)行數(shù)據(jù)庫(kù)操作了。對(duì)于我們將要編寫(xiě)的事務(wù)處理程序,當(dāng)然還需要定義一個(gè)SqlTransaction類(lèi)型的對(duì)象。并且看到SqlCommand對(duì)象的Transcation屬性,我們很容易想到新建的SqlTransaction對(duì)象應(yīng)該與它關(guān)聯(lián)起來(lái)。

基于以上認(rèn)識(shí),下面我們就開(kāi)始動(dòng)手寫(xiě)我們的第一個(gè)事務(wù)處理程序。我們可以很熟練地寫(xiě)出下面這一段程序:

//DoTran.csusing System;
using System.Data;
using System.Data.SqlClient;
namespace Aspcn
{
 public class DbTran
 {
  file://執(zhí)行事務(wù)處理
  public void DoTran()
  {
   file://建立連接并打開(kāi)
   SqlConnection myConn=GetConn();
   myConn.Open();
   SqlCommand myComm=new SqlCommand();
   SqlTransaction myTran=new SqlTransaction();
   try
   {
    myComm.Connection=myConn;
    myComm.Transaction=myTran;
   
    file://定位到pubs數(shù)據(jù)庫(kù) 
    myComm.CommandText="USE pubs";
    myComm.ExecuteNonQuery();

    file://更新數(shù)據(jù)
    file://將所有的計(jì)算機(jī)類(lèi)圖書(shū)
    myComm.CommandText="Update roysched SET royalty = royalty * 1.10 Where title_id LIKE ''''Pc%''''";
    myComm.ExecuteNonQuery();//提交事務(wù)
    myTran.Commit();
   }
   catch(Exception err)
   {
    throw new ApplicationException("事務(wù)操作出錯(cuò),系統(tǒng)信息:"+err.Message);
   }
   finally
   {
    myConn.Close();
   }
  }
  file://獲取數(shù)據(jù)連接
  private SqlConnection GetConn()
  {
   string strSql="Data Source=localhost;Integrated Security=SSPI;user id=sa;password=";
   SqlConnection myConn=new SqlConnection(strSql);
   return myConn;
  }
 }

 public class Test{public static void Main()
 {
  DbTran tranTest=new DbTran();
  tranTest.DoTran();
  Console.WriteLine("事務(wù)處理已經(jīng)成功完成。");
  Console.ReadLine();
 }
}
}

顯然,這個(gè)程序非常簡(jiǎn)單,我們非常自信地編譯它,但是,出乎意料的結(jié)果使我們的成就感頓時(shí)煙消云散:

error CS1501: 重載"SqlTransaction"方法未獲取"0"參數(shù)

是什么原因呢?注意到我們初始化的代碼:

SqlTransaction myTran=new SqlTransaction();

顯然,問(wèn)題出在這里,事實(shí)上,SqlTransaction類(lèi)并沒(méi)有公共的構(gòu)造函數(shù),我們不能這樣新建一個(gè)SqlTrancaction類(lèi)型的變量。在事務(wù)處理之前確實(shí)需要有一個(gè)SqlTransaction類(lèi)型的變量,將該變量關(guān)聯(lián)到SqlCommand類(lèi)的Transcation屬性也是必要的,但是初始化方法卻比較特別一點(diǎn)。在初始化SqlTransaction類(lèi)時(shí),你需要使用SqlConnection類(lèi)的BeginTranscation()方法:

SqlTransaction myTran; myTran=myConn.BeginTransaction();
  
該方法返回一個(gè)SqlTransaction類(lèi)型的變量。在調(diào)用BeginTransaction()方法以后,所有基于該數(shù)據(jù)連接對(duì)象的SQL語(yǔ)句執(zhí)行動(dòng)作都將被認(rèn)為是事務(wù)MyTran的一部分。同時(shí),你也可以在該方法的參數(shù)中指定事務(wù)隔離級(jí)別和事務(wù)名稱,如:

SqlTransaction myTran;
myTran=myConn.BeginTransaction(IsolationLevel.ReadCommitted,"SampleTransaction"); 
  
關(guān)于隔離級(jí)別的概念我們將在隨后的內(nèi)容中探討,在這里我們只需牢記一個(gè)事務(wù)是如何被啟動(dòng),并且關(guān)聯(lián)到特定的數(shù)據(jù)鏈接的。

先不要急著去搞懂我們的事務(wù)都干了些什么,看到這一行:

myTran.Commit();

是的,這就是事務(wù)的提交方式。該語(yǔ)句執(zhí)行后,事務(wù)的所有數(shù)據(jù)庫(kù)操作將生效,并且為數(shù)據(jù)庫(kù)事務(wù)的持久性機(jī)制所保持--即使系統(tǒng)在這以后發(fā)生致命錯(cuò)誤,該事務(wù)對(duì)數(shù)據(jù)庫(kù)的影響也不會(huì)消失。

對(duì)上面的程序做了修改之后我們可以得到如下代碼(為了節(jié)約篇幅,重復(fù)之處已省略,請(qǐng)參照前文):

//DoTran.cs……}

file://執(zhí)行事務(wù)處理
public void DoTran()
{
 file://建立連接并打開(kāi)
 SqlConnection myConn=GetConn();
 myConn.Open();
 SqlCommand myComm=new SqlCommand();

 file://SqlTransaction myTran=new SqlTransaction();
 file://注意,SqlTransaction類(lèi)無(wú)公開(kāi)的構(gòu)造函數(shù)

 SqlTransaction myTran;

 file://創(chuàng)建一個(gè)事務(wù)
 myTran=myConn.BeginTransaction();
 try
 {
  file://從此開(kāi)始,基于該連接的數(shù)據(jù)操作都被認(rèn)為是事務(wù)的一部分
  file://下面綁定連接和事務(wù)對(duì)象
  myComm.Connection=myConn;
  myComm.Transaction=myTran; file://定位到pubs數(shù)據(jù)庫(kù)
  myComm.CommandText="USE pubs";
  myComm.ExecuteNonQuery();//更新數(shù)據(jù)
  file://將所有的計(jì)算機(jī)類(lèi)圖書(shū)
  myComm.CommandText="Update roysched SET royalty = royalty * 1.10 Where title_id LIKE ''''Pc%''''";
  myComm.ExecuteNonQuery();
 
  file://提交事務(wù)
  myTran.Commit();
 }
 catch(Exception err)
 {
  throw new ApplicationException("事務(wù)操作出錯(cuò),系統(tǒng)信息:"+err.Message);
  }
 finally
 {
  myConn.Close();
  }
}
……

到此為止,我們僅僅掌握了如何開(kāi)始和提交事務(wù)。下一步我們必須考慮的是在事務(wù)中可以干什么和不可以干什么。
 

該文章轉(zhuǎn)載自'大智の博客':http://www.csafe.cn/article.asp?id=271



TiGERTiAN 2007-05-10 22:43 發(fā)表評(píng)論
]]>
Google DeskTop Searchhttp://www.aygfsteel.com/TiGERTiAN/archive/2005/07/21/8092.htmlTiGERTiANTiGERTiANThu, 21 Jul 2005 02:25:00 GMThttp://www.aygfsteel.com/TiGERTiAN/archive/2005/07/21/8092.htmlhttp://www.aygfsteel.com/TiGERTiAN/comments/8092.htmlhttp://www.aygfsteel.com/TiGERTiAN/archive/2005/07/21/8092.html#Feedback2http://www.aygfsteel.com/TiGERTiAN/comments/commentRss/8092.htmlhttp://www.aygfsteel.com/TiGERTiAN/services/trackbacks/8092.htmlhttp://desktop.google.com/zh/CN?promo=app-gds-zh-cn-v1-1
看來(lái)信息檢索技術(shù)真的是一個(gè)很神奇的技術(shù)啊...

TiGERTiAN 2005-07-21 10:25 發(fā)表評(píng)論
]]>
主站蜘蛛池模板: 巨鹿县| 阜新市| 铁力市| 灌阳县| 江口县| 东宁县| 黎平县| 光泽县| 鹿邑县| 新津县| 彩票| 西藏| 新竹市| 辽宁省| 黑山县| 青浦区| 化州市| 修武县| 枝江市| 明水县| 建昌县| 湟源县| 蓬安县| 崇仁县| 娄烦县| 华池县| 浦北县| 乐安县| 黑龙江省| 镇江市| 大荔县| 焦作市| 泽普县| 微山县| 陆良县| 苏尼特右旗| 永春县| 湘阴县| 西乡县| 基隆市| 左云县|