strCn = "driver=MySQL ODBC 5.1 Driver;" & _
"server=192.168.1.40;" & _
"database=bingotest;" & _
"uid=ks;" & _
"pwd=23092342"
cn.Open strCn
索引用來快速地尋找那些具有特定值的記錄,所有MySQL索引都以B-樹的形式保存。如果沒有索引,執行查詢時MySQL必須從第一個記錄開始掃描整個表的所有記錄,直至找到符合要求的記錄。表里面的記錄數量越多,這個操作的代價就越高。如果作為搜索條件的列上已經創建了索引,MySQL無需掃描任何記錄即可迅速得到目標記錄所在的位置。如果表有1000個記錄,通過索引查找記錄至少要比順序掃描記錄快100倍。
假設我們創建了一個名為people的表:
CREATE TABLE people ( peopleid SMALLINT NOT NULL, name CHAR(50) NOT NULL );
然后,我們完全隨機把1000個不同name值插入到people表。在數據文件中name 列沒有任何明確的次序。如果我們創建了name列的索引,MySQL將在索引中排序name列,對于索引中的每一項,MySQL在內部為它保存一個數據文件中實際記錄所在位置的“指針”。因此,如果我們要查找name等于“Mike”記錄的peopleid(SQL命令為“SELECT peopleid FROM people WHERE name='Mike';”),MySQL能夠在name的索引中查找“Mike”值,然后直接轉到數據文件中相應的行,準確地返回該行的 peopleid(999)。在這個過程中,MySQL只需處理一個行就可以返回結果。如果沒有“name”列的索引,MySQL要掃描數據文件中的所有記錄,即1000個記錄!顯然,需要MySQL處理的記錄數量越少,則它完成任務的速度就越快。
二、索引的類型
MySQL提供多種索引類型供選擇:
普通索引 :
這是最基本的索引類型,而且它沒有唯一性之類的限制。普通索引可以通過以下幾種方式創建:
創建索引,例如CREATE INDEX <索引的名字> ON tablename (列的列表);
修改表,例如ALTER TABLE tablename ADD INDEX [索引的名字] (列的列表);
創建表的時候指定索引,例如CREATE TABLE tablename ( [...], INDEX [索引的名字] (列的列表) );
唯一性索引:
這種索引和前面的“普通索引”基本相同,但有一個區別:索引列的所有值都只能出現一次,即必須唯一。唯一性索引可以用以下幾種方式創建:
創建索引,例如CREATE UNIQUE INDEX <索引的名字> ON tablename (列的列表);
修改表,例如ALTER TABLE tablename ADD UNIQUE [索引的名字] (列的列表);
創建表的時候指定索引,例如CREATE TABLE tablename ( [...], UNIQUE [索引的名字] (列的列表) );
主鍵 :
主鍵是一種唯一性索引,但它必須指定為“PRIMARY KEY”。如果你曾經用過AUTO_INCREMENT類型的列,你可能已經熟悉主鍵之類的概念了。主鍵一般在創建表的時候指定,例如“CREATE TABLE tablename ( [...], PRIMARY KEY (列的列表) ); ”。但是,我們也可以通過修改表的方式加入主鍵,例如“ALTER TABLE tablename ADD PRIMARY KEY (列的列表); ”。每個表只能有一個主鍵。
全文索引:
MySQL從3.23.23版開始支持全文索引和全文檢索。在MySQL中,全文索引的索引類型為FULLTEXT。全文索引可以在VARCHAR或者TEXT類型的列上創建。它可以通過CREATE TABLE命令創建,也可以通過ALTER TABLE或CREATE INDEX命令創建。對于大規模的數據集,通過ALTER TABLE(或者CREATE INDEX)命令創建全文索引要比把記錄插入帶有全文索引的空表更快。本文下面的討論不再涉及全文索引,要了解更多信息,請參見MySQL documentation。
三、單列索引與多列索引
索引可以是單列索引,也可以是多列索引。下面我們通過具體的例子來說明這兩種索引的區別。假設有這樣一個people表:
CREATE TABLE people ( peopleid SMALLINT NOT NULL AUTO_INCREMENT,firstname CHAR(50) NOT NULL, lastname CHAR(50) NOT NULL, age SMALLINT NOT NULL,townid SMALLINT NOT NULL, PRIMARY KEY (peopleid) );
下面是我們插入到這個people表的數據:
這個數據片段中有四個名字為“Mikes”的人(其中兩個姓Sullivans,兩個姓McConnells),有兩個年齡為17歲的人,還有一個名字與眾不同的Joe Smith。
這個表的主要用途是根據指定的用戶姓、名以及年齡返回相應的peopleid。例如,我們可 能需要查找姓名為Mike Sullivan、年齡17歲用戶的peopleid(SQL命令為SELECT peopleid FROM people WHERE firstname='Mike' AND lastname='Sullivan' AND age=17;)。由于我們不想讓MySQL每次執行查詢就去掃描整個表,這里需要考慮運用索引。
首先,我們可以考慮在單個列上創建索引,比如firstname、lastname或者 age列。如果我們創建firstname列的索引(ALTER TABLE people ADD INDEX firstname (firstname);),MySQL將通過這個索引迅速把搜索范圍限制到那些firstname='Mike'的記錄,然后再在這個“中間結果集”上進行其他條件的搜索:它首先排除那些lastname不等于“Sullivan”的記錄,然后排除那些age不等于17的記錄。當記錄滿足所有搜索條件之后,MySQL就返回最終的搜索結果。
由于建立了firstname列的索引,與執行表的完全掃描相比,MySQL的效率提高了很多,但我們要求MySQL掃描的記錄數量仍舊遠遠超過了實際所需要的。雖然我們可以刪除firstname列上的索引,再創建lastname或者age 列的索引,但總地看來,不論在哪個列上創建索引搜索效率仍舊相似。
為了提高搜索效率,我們需要考慮運用多列索引。如果為firstname、lastname和age這三個列創建一個多列索引,MySQL只需一次檢索就能夠找出正確的結果!下面是創建這個多列索引的SQL命令:
ALTER TABLE people ADD INDEX fname_lname_age (firstname,lastname,age);
由于索引文件以B-樹格式保存,MySQL能夠立即轉到合適的firstname,然后再轉到合適的lastname,最后轉到合適的age。在沒有掃描數據文件任何一個記錄的情況下,MySQL就正確地找出了搜索的目標記錄!
那么,如果在firstname、lastname、age這三個列上分別創建單列索引,效果是否和創建一個firstname、lastname、age的多列索引一樣呢?答案是否定的,兩者完全不同。當我們執行查詢的時候,MySQL只能使用一個索引。如果你有三個單列的索引,MySQL會試圖選擇一個限制最嚴格的索引。但是,即使是限制最嚴格的單列索引,它的限制能力也肯定遠遠低于 firstname、lastname、age這三個列上的多列索引。
四、最左前綴
多列索引還有另外一個優點,它通過稱為最左前綴(Leftmost Prefixing)的概念體現出來。繼續考慮前面的例子,現在我們有一個firstname、lastname、age列上的多列索引,我們稱這個索引為fname_lname_age。當搜索條件是以下各種列的組合時,MySQL將使用fname_lname_age索引:
firstname,lastname,agefirstname,lastnamefirstname
從另一方面理解,它相當于我們創建了(firstname,lastname,age)、(firstname,lastname)以及(firstname)這些列組合上的索引。下面這些查詢都能夠使用這個fname_lname_age索引:
SELECT peopleid FROM people WHERE firstname='Mike' AND lastname='Sullivan' AND age='17'; SELECT peopleid FROM people WHERE firstname='Mike' AND lastname='Sullivan'; SELECT peopleid FROM people WHERE firstname='Mike'; The following queries cannot use the index at all: SELECT peopleid FROM people WHERE lastname='Sullivan'; SELECT peopleid FROM people WHERE age='17'; SELECT peopleid FROM people WHERE lastname='Sullivan' AND age='17';
五、選擇索引列
在性能優化過程中,選擇在哪些列上創建索引是最重要的步驟之一。可以考慮使用索引的主要有兩種類型的列:在WHERE子句中出現的列,在join子句中出現的列。請看下面這個查詢:
SELECT age ## 不使用索引FROM people WHERE firstname='Mike' ## 考慮使用索引AND lastname='Sullivan' ## 考慮使用索引
這個查詢與前面的查詢略有不同,但仍屬于簡單查詢。由于age是在SELECT部分被引用,MySQL不會用它來限制列選擇操作。因此,對于這個查詢來說,創建age列的索引沒有什么必要。下面是一個更復雜的例子:
SELECT people.age, ##不使用索引town.name ##不使用索引FROM people LEFT JOIN town ONpeople.townid=town.townid ##考慮使用索引WHERE firstname='Mike' ##考慮使用索引AND lastname='Sullivan' ##考慮使用索引
與前面的例子一樣,由于firstname和lastname出現在WHERE子句中,因此這兩個列仍舊有創建索引的必要。除此之外,由于town表的townid列出現在join子句中,因此我們需要考慮創建該列的索引。那么,我們是否可以簡單地認為應該索引WHERE子句和join子句中出現的每一個列呢?差不多如此,但并不完全。我們還必須考慮到對列進行比較的操作符類型。MySQL只有對以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些時候的LIKE。可以在LIKE操作中使用索引的情形是指另一個操作數不是以通配符(%或者_)開頭的情形。例如,“SELECT peopleid FROM people WHERE firstname LIKE 'Mich%';”這個查詢將使用索引,但“SELECT peopleid FROM people WHERE firstname LIKE '%ike';”這個查詢不會使用索引。
create table ( content varchar(20) not null, writer varchar(10) null ) |
col_name col_typy [col_attributes] [general_attributes] |
類型 |
說明 |
tinyint |
非常小的整數 |
smallint |
較小整數 |
mediumint |
中等大小整數 |
int |
標準整數 |
bigint |
較大整數 |
float |
單精度浮點數 |
double |
雙精度浮點數 |
decimal |
一個串的浮點數 |
表1:數值列類型
類型說明 |
取值范圍 |
tinyint[(m)] |
有符號值:-128 到127(- 27 到27 - 1) 無符號值:0到255(0 到28 - 1) |
smallint[(m)] |
有符號值:-32768 到32767(- 215 到215 - 1) 無符號值:0到65535(0 到21 6 - 1) |
mediumint[(m)] |
有符號值:-8388608 到8388607(- 22 3 到22 3 - 1 ) 無符號值:0到16777215(0 到22 4 - 1) |
int[(m)] |
有符號值:-2147683648 到2147683647(- 231 到231- 1) 無符號值:0到4294967295(0 到232 - 1) |
bigint[(m)] |
有符號值:-9223372036854775808 到9223373036854775807(- 263到263-1) 無符號值:0到18446744073709551615(0到264 – 1) 8個字節 |
float[(m, d)] |
最小非零值:±1.175494351e - 38 |
double[(m,d)] |
最小非零值:±2.2250738585072014e - 308 |
decimal (m, d) |
可變;其值的范圍依賴于m 和d |
表2:數值列類型的取值范圍
類型說明 |
存儲需求 |
tinyint[(m)] |
1字節 |
smallint[(m)] |
2字節 |
mediumint[(m)] |
3字節 |
int[(m)] |
4字節 |
bigint[(m)] |
8字節 |
float[(m, d)] |
4字節 |
double[(m, d)] |
8字節 |
decimal (m, d) |
m字節(mysql < 3.23),m+2字節(mysql > 3.23 ) |
表3:數值列類型的存儲需求
mysql提供了五種整型: tinyint、smallint、mediumint、int和bigint。int為integer的縮寫。這些類型在可表示的取值范圍上是不同的。整數列可定義為unsigned從而禁用負值;這使列的取值范圍為0以上。各種類型的存儲量需求也是不同的。取值范圍較大的類型所需的存儲量較大。
類型名 |
說明 |
char |
定長字符串 |
varchar |
可變長字符串 |
tinyblob |
非常小的blob(二進制大對象) |
blob |
小blob |
mediumblob |
中等的blob |
longblob |
大blob |
tinytext |
非常小的文本串 |
text |
小文本串 |
mediumtext |
中等文本串 |
longtext |
大文本串 |
enum |
枚舉;列可賦予某個枚舉成員 |
set |
集合;列可賦予多個集合成員 |
表4:字符串列類型
類型說明 |
最大尺寸 |
存儲需求 |
char( m) |
m 字節 |
m 字節 |
varchar(m) |
m 字節 |
l + 1字節 |
tinyblob, tinytext |
28- 1字節 |
l + 1字節 |
blob, text |
216 - 1 字節 |
l + 2字節 |
mediumblob, mediumtext |
224- 1字節 |
l + 3字節 |
longblob, longtext |
232- 1字節 |
l + 4字節 |
enum(“value1”, “value2”, ...) |
65535 個成員 |
1 或2字節 |
set (“value1”, “value2”, ...) |
64個成員 |
1、2、3、4 或8字節 |
表5:串列類型最大尺寸及存儲需求
類型名 |
說明 |
date |
“yyyy-mm-dd”格式表示的日期值 |
time |
“hh:mm:ss”格式表示的時間值 |
datetime |
“yyyy-mm-dd hh:mm:ss”格式 |
timestamp |
“yyyymmddhhmmss”格式表示的時間戳值 |
year |
“yyyy”格式的年份值 |
表6:日期時間列類型
類型名 |
取值范圍 |
存儲需求 |
date |
“1000-01-01”到“9999-12-31” |
3字節 |
time |
“-838:59:59”到“838:59:59” |
3字節 |
datetime |
“1000-01-01 00:00:00” 到“9999-12-31 23:59:59” |
8字節 |
timestamp |
19700101000000 到2037 年的某個時刻 |
4字節 |
year |
1901 到2155 |
1字節 |
表7: 日前時間列類型的取值范圍和存儲需求
create table student ( name varchar(20) not null, chinese tinyint(3), maths tinyint(3), english tinyint(3), birthday date ) |
當我第一次見到php模板的時候,簡直被迷上了。原來php世界里還有這么好的東西。我瘋狂地學習。
好在當時的php模板還不是很龐大,很快我了解了它的原理,并也能自己寫一些了。
后來,smarty橫空出世,更是擁有了許多為它著迷的fans.許多人開始寫模板引擎,將smarty或其他模板加以改造,使之符合自己的需要。
縱觀當今模板世界,php模板引擎主要分為兩種:
一種是替換特定字串型的。美工做出來的頁面,中間會嵌入一些什么{block.title}這樣的字符串,然后程序讀入這個模板文件,將中間的{block.title}的字樣替換成實際從數據庫中讀取的內容。
還有一種符雜一些,是一種編譯型的。以smarty為代表,模板文件中實際上包含了一些簡化的php代碼,比如有的寫的是<{=$site_name}>,<{if $counts>0 }>這樣的語句,實際是一種自行定義的語言格式。web開發久的人一眼能看出它們指的是什么。這一類往往會在第一次訪問時被預編譯,轉換成一個php文件何存起來。比如 <{=$site_name}>就變成了,<{if $count>0 }> 就變成了0) {?>。從第二次起,就直接包含已經編譯出來的php文件。當然,各種模板的處理的具體細節不一樣。
但是,我們為什么要用模板?
這個問題我也不能很好的回答。但是有很多人這么說:要讓美工和程序員的工作分開。要讓代碼層和表現層分開。
那么我們看這兩種方式實現了嗎?
讓您的美工人員打開一個smarty文件,讓他按他的思路去改一下視圖設計。他會告訴您,天哪,你怎么把我的設計變成這樣一幅樣子了?這些foreach,if都是什么意思?噢,天哪,我的表格怎么撐得這么大了?….
原來編譯型的模板技術讓美工更沒法子做美工了。
那么替換字符串型的呢?看起來是輕松多了。不過您可以測測您的程序速度,看看他是否已經慢了一倍以上。很容易,您用了太多的有preg_match,或是str_ireplace之類的語句來替換。程序重復一次又一次在您那幾萬個甚至更多字節的模板文件中查找某個字串,來替換內容。
不僅如此,你還發現您哪天想在視層面插一點東西時,處處受制于模板。您肯定很想直接在模板中寫一段之類的語句,但是,很抱歉,您不能這么寫。如果只是想echo 一個什么東西,您可以在模板中加一個{{var}}的串,然后在php程序中寫
$var=”這一個串”;
$template->assign(”var”,$var);
但是如果您不是想輸出呢,想做其他操作?抱歉,您還是得改php代碼。這些模板都支持規矩矩的表格狀的輸出,但是,如果我想輸出20條記錄,我要弄一個第一行2列記錄的,第二行3列的,第三行又是2行的之類的,或者是其他類似找碼,您打算怎么辦?
難道再讓您的模板引擎也升級成為編譯性的,好讓您往您的模板中加入php語句?
您錯了。其實,我們看一看,我們的模板引擎充當了什么樣的角色呢?我們把一段特定的按一定規則編寫的html代碼進行一定的數據處理后輸出成了另外一段,輸出過程中某些字符發生了改變。那我們的偉大的php干了什么呢?它幫我們把我們按規則寫好的php代碼也按一定規則輸出成了一段html。兩者本質是一樣的。只是,php本身比我們那些蹩腳的模板引擎更快,更漂亮。
再說細一點,現在用的最多的Smarty.看一看它的結構:
<{if $article.rates}>
<span class="title"><{php}>echo art_constant("MD_RATE");<{/php}>:</span>
<span class="item"><{$article.rating}>/<{$article.rates}></span>
<{/if}>
你覺得它是一個美工能看清的html文件么?
看看編譯后的結果,它看起來應該是這樣的:
<?php
if($articles.rates){
?>
<span class="title"><?php echo art_constant("MD_RATE");?>:</span>
<span class="item"><?php echo $article.rating;?>/<?php echo article.rates;?></span>
<?php
}?>
哈。我們偉大的smarty把這html也變成了php了。然后在以后的工作中,這些php代碼會直接被運行。
有必要嗎?本來是apache承載php,php直接編譯php 源代碼,現在變成了apache承載php,php編譯smarty引擎,smarty引擎又去編譯html文件。反而多了一層,smarty自個兒把本來php要做的工作拿了做了。
而且糟糕的是,smarty的模板打開來以后,比php文件更讓人難以看清。
所以我說,放棄您的模板系統吧。但記住我們開發模板系統的初衷。
您會說:那要是放棄模板系統了,我怎么分開程序邏輯和視圖層面?
這個也太好辦了。您可以這樣寫:
第一個文件這樣寫:
<?php
/**
file :logic.php
@author renlu xu<helloasp@hotmail.com>
@link http://www.162cm.com
*/
$rs=mysql_query("select id,title,body from articles order by id desc limit 30");
while($row=$mysql_fetch_assoc($rs))
{
$rows[]=$row;
}
?>
第二個文件作為您的視層面的文件。您可以讓您的平面設計師在mac上設計漂亮絕頂的圖片,然后切成html文件,交給您的頁面整合工程師。這個工程不需要懂太多php,他基本上只需要會用print就行了。他把這個html文件改名為view.php,然后在里面加上相應代碼:
<?php
/**
file:view.php
@author renlu xu<helloasp@hotmail.com>
@link http://www.162cm.com
*/
foreach($rows as $row)
{
echo "<tr>";
echo "<td>";
echo $row["id"];
echo "</td>";
echo "<td>";
echo $row["title"];
echo "</td>";
echo "</tr>";
}
?>
ok.這就視層面文件。這個文件用織夢者(dreamweaver)打開后,應該跟美工做出來的圖是一樣的,就僅僅多了用那個黃色的問號代表的php代碼。寫得好時,dreamweaver還能列出您的php代碼中輸出了什么內容,效果更佳。
這兩個文件,就是一個是邏輯層,一個是視層面。
然后用index.php將他們包含起來。
<?php
/**
file:index.php
@author renlu xu<helloasp@hotmail.com>
@link http://www.162cm.com
*/
include "databaseConnect.php";//連接數據庫的代碼,我就不寫了
include "logic.php";
include "view.php";
include "cache.php";
?>
這樣邏輯和視,就分開了。那有人會說,如果我用模板,可以將要輸出的內容寫入到靜態文件中,現在沒了模板我該怎么辦?
這個不是問題。我們用的是php,超酷的php.
看看,我們加了cache.php.這就是用來實現您說的緩存效果的。
我們的cache文件:
<?php
/**
file:cache.php
@author renlu xu<helloasp@hotmail.com>
@link http://www.162cm.com
*/
$content=ob_get_content();
$fp=fopen("index.html","w");
fwrite($fp,$content);
fclose($fp);
?>
當然,index.php也改成:
<?php
/**
file:index.php
@author renlu xu<helloasp@hotmail.com>
@link http://www.162cm.com
*/
if(file_exists("index.html"))
{
include "index.html";
exit();
}
include "databaseConnect.php";//連接數據庫的代碼,我就不寫了
include "logic.php";
include "view.php";
incldue "cache.php";
?>
這樣您看這緩存效果是不是就出來了?
有人說:我用模板還有一個好處啊,就是模板寫錯了還不會影響到程序的運行。是的。咳,怎么說呢?您在模板中將<{=$site_name}>寫成了<{=$sie_name}>是沒啥,您的php程序還是很”健壯”地運行著。不過您沒覺得,當您期望的那一個將由<{=$site_name}>來打出來的網站名稱沒有出現的時候,您排起錯來會很難找嗎?對于一個程序師來說,您不覺得,光只視層面的那些echo語句,只應該是您工作中極微不足道的一部分嗎?在這個主要是echo語句構成的視層面文件view.php,您還出現編譯期錯誤,是不是該拉出去打屁屁?
另外,在php程序正式上線運行時,您也可以將它的報錯功能關閉啊。
Private Sub Command1_Click() Function RegExpTest(patrn, strng) |