SQL語法大全

本部分內容
            SQL基礎
           中級SQL
           高級SQL
第10章SQL基礎

                 本章內容
           
                  SQL介紹
                 使用SELECT語句從表中取數據
                 創建新表
                 字段屬性
                 向表中添加數據
                 刪除和修改表
         

    為了建立交互站點,你需要使用數據庫來存儲來自訪問者的信息。例如,你要建立一個職業介紹服務的站點,你就需要存儲諸如個人簡歷,所感興趣的工作等等這樣的信息。創建動態網葉也需要使用數據庫,如果你想顯示符合來訪者要求的最好的工作,你就需要從數據庫中取出這份工作的信息。你將會發現,在許多情況下需要使用數據庫。
    在這一章裡,你將學會怎樣使用“結構化查詢語言”(SQL〕來操作數據庫。SQL語言是數據庫的標準語言。在Active Sever Pages中,無論何時你要訪問一個數據庫,你就要使用SQL語言。因此,掌握好SQL對ASP編程是非常重要的。
     
    注意:
    你可以把“SQL”讀作“sequel”,也可以按單個字母的讀音讀作S-Q-L。兩種發音都是正確的,每種發音各有大量的支持者。在本書裡,認為“SQL”讀作“sequel”。

    通過這一章的學習,你將理解怎樣用SQL實現數據庫查詢,你將學會怎樣使用這種查詢從數據表中取出信息,最後,你將學會怎樣設計和建立自己的數據庫。
            
    注意:
    通過下面幾章對SQL的介紹,你將對SQL有足夠的了解,從而可以有效地使用Active Sever Pages。但是,SQL是一種複雜的語言,本書不可能包括它的全部細節。要全面掌握SQL語言,你需要學習在Microsoft SQL Sever中使用SQL。你可以到附近的書店去買一本Microsoft SQL Sever 6.5。

SQL介紹:
    本書假設你是在SQL操作Microsoft SQL Sever的數據庫。你也可以用SQL操作許多其它類型的數據庫。 SQL是操作數據庫的標準語言。 (事實上,關於SQL語言有一個專門的ANSI標準〕

    注意:
    不要在你的站點上試圖用Microsoft Access代替Microsoft SQL Sever。 SQL Sever可以同時服務於許多用戶,如果你希望你的站點有較高的訪問率,MS Access是不能勝任的。

    在學習SQL的細節之前,你需要理解它的兩大特點。一個特點容易掌握,另一個掌握起來有點困難。
    第一個特點是所有SQL數據庫中的數據都存儲在表中。一個表由行和列組成。例如,下面這個簡單的表包括name和e-mail address:
     Name Email Address
     ………………………………………….. …………..
     Bill Gates billg@microsoft.com
     president Clinton president@whitehouse.com
     Stephen Walther swalther@somewhere.com
    這個表有兩列(列也稱為字段,域〕:Name和Email Address。有三行,每一行包含一組數據。一行中的數據組合在一起稱為一條記錄。
    無論何時你向表中添加新數據,你就添加了一條新記錄。一個數據表可以有幾十個記錄,也可以有幾千甚至幾十億個記錄。雖然你也許永遠不需要存儲十億個Email地址,但知道你能這樣做總是好的,也許有一天你會有這樣的需要。
    你的數據庫很有可能包含幾十個表,所有存儲在你數據庫中的信息都被存儲在這些表中。當你考慮怎樣把信息存儲在數據庫中時,你應該考慮怎樣把它們存儲在表中。
     SQL的第二個特點有些難於掌握。這種語言被設計為不允許你按照某種特定的順序來取出記錄,因為這樣做會降低SQL Sever取記錄的效率。使用SQL,你只能按查詢條件來讀取記錄。
    當考慮如何從表中取出記錄時,自然會想到按記錄的位置讀取它們。例如,也許你會嘗試通過一個循環,逐個記錄地掃描,來選出特定的記錄。在使用SQL時,你必須訓練自己,不要有這種思路。
    假如你想選出所有的名字是“Bill Gates”的記錄,如果使用傳統的編程語言,你也許會構造一個循環,逐個查看表中的記錄,看名字域是否是“Bill Gates”。
    這種選擇記錄的方法是可行的,但是效率不高。使用SQL,你只要說,“選擇所有名字域等於Bill Gates的記錄”,SQL就會為你選出所有符合條件的記錄。 SQL會確定實現查詢的最佳方法。
    建設你想取出表中的前十個記錄。使用傳統的編程語言,你可以做一個循環,取出前十個記錄後結束循環。但使用標準的SQL查詢,這是不可能實現的。從SQL的角度來說,在一個表中不存在前十個記錄這種概念。
    開始時,當你知道你不能用SQL實現某些你感覺應該能實現的功能,你會受到挫折。你也許會以頭撞牆甚至想寫惡毒的信件給SQL的設計者們。但後來你會認識到,SQL的這個特點不僅不是個限制,反而是其長處。因為SQL不根據位置來讀取記錄,它讀取記錄可以很快。
    綜上所述,SQL有兩個特點:所有數據存儲在表中,從SQL的角度來說,表中的記錄沒有順序。在下一節,你將學會怎樣用SQL從表中選擇特殊的記錄。

使用SQL從表中取記錄。
     SQL的主要功能之一是實現數據庫查詢。如果你熟悉Internet引擎,那麼你已經熟悉查詢了。你使用查詢來取得滿足特定條件的信息。例如,如果你想找到有ASP信息的全部站點,你可以連接到Yahoo!並執行一個對Active Sever Pages的搜索。在你輸入這個查詢後,你會收到一個列表,表中包括所有其描述中包含搜索表達式的站點。
    多數Internet引擎允許邏輯查詢。在邏輯查詢中,你可以包括特殊的運算符如AND、OR和NOT,你使用這些運算符來選擇特定的記錄。例如,你可以用AND來限制查詢結果。如果你執行一個對Active Sever Pages AND SQL的搜索。你將得到其描述中同時包含Active Sever Pages和SQL的記錄。當你需要限制查詢結果時,你可以使用AND。
    如果你需要擴展查詢的結果,你可以使用邏輯操作符OR。例如,如果你執行一個搜索,搜索所有的其描述中包含Active Sever Pages OR SQL的站點,你收到的列表中將包括所有其描述中同時包含兩個表達式或其中任何一個表達式的站點。
    如果你想從搜索結果中排除特定的站點,你可以使用NOT。例如,查詢“Active Sever Pages ”AND NOT “SQL”將返回一個列表,列表中的站點包含Active Sever Pages,但不包含SQL。當必須排除特定的記錄時,你可以使用NOT。
    用SQL執行的查詢與用Internet搜索引擎執行的搜索非常相似。當你執行一個SQL查詢時,通過使用包括邏輯運算符的查詢條件,你可以得到一個記錄列表。此時查詢結果是來自一個或多個表。
     SQL查詢的句法非常簡單。假設有一個名為email_table的表,包含名字和地址兩個字段,要得到Bill Gates的e_mail地址,你可以使用下面的查詢:

     SELECT email from email_table WHERE name=”Bill Gates”

    當這個查詢執行時,就從名為email_table的表中讀取Bill Gates的e_mail地址。這個簡單的語句包括三部分:
     ■ SELECT語句的第一部分指名要選取的列。在此例中,只有email列被選取。當執行時,只顯示email列的值billg@microsoft.com。
     ■ SELECTT語句的第二部份指明要從哪個(些)表中查詢數據。在此例中,要查詢的表名為email_table 。
     ■最後,SELECT語句的WHERE子句指明要選擇滿足什麼條件的記錄。在此例中,查詢條件為只有name列的值為Bill Gates的記錄才被選取。
     Bill Gates很有可能擁有不止一個email地址。如果表中包含Bill Gates的多個email地址。用上述的SELECT語句可以讀取他所有的email地址。 SELECT語句從表中取出所有name字段值為Bill Gates的記錄的email字段的值。
    前面說過,查詢可以在查詢條件中包含邏輯運算符。假如你想讀取Bill Gates或Clinton總統的所有email地址,你可以使用下面的查詢語句:

     SELECT email FROM email_table WHERE name=”Bill Gates” OR
                                                    name=”president Clinton”

    此例中的查詢條件比前一個複雜了一點。這個語句從表email_table中選出所有name列為Bill Gates或president Clinton的記錄。如果表中含有Bill Gates或president Clinton的多個地址,所有的地址都被讀取。
     SELECT語句的結構看起來很直觀。如果你請一個朋友從一個表中為你選擇一組記錄,你也許以非常相似的方式提出你的要求。在SQL SELECT語句中,你“SELECT特定的列FROM一個表WHERE某些列滿足一個特定的條件”。
    下一節將介紹怎樣執行SQL查詢來選取記錄。這將幫助你熟悉用SELECT語句從表中取數據的各種不同方法。

使用ISQL執行SELECT查詢
    當你安裝SQL Sever時,你同時安裝了一個叫作ISQL/w的應用程序。 ISQL/w允許你執行交互的SQL查詢。在把查詢包括到你的ASP網頁中之前,用ISQL/w對其進行測試是非常有用的。

    注意:
    在這本書的第一部份,你學習了怎樣安裝和配置Microsoft SQL Sever 。如果沒有安裝SQL Sever或者SQL Sever不能運行,請參閱第三章“安裝和使用SQL Sever”。

    選擇任務上SQL Sever程序組中的ISQL_w以啟動該程序。程序啟動時,首先會出現一個對話框,要求輸入服務器信息和登錄信息(見圖10.1)。在Sever框中,輸入你的SQL服務器的名字。如果服務器正運行在本地計算機上,服務器名字就是你計算機的名字。在登錄信息框中,輸入一個登錄帳號和密碼或選擇使用“可信連接”,然後單擊Connect按鈕。

    圖10。1

    注意:
    如果你將SQL Sever配置為使用完整安全或混合安全,那麼你可以使用可信連接。如果你使用標準安全,你則需要提供用戶帳號和密碼。要了解更多信息,參見第三章。

    如果一切正常,在你單擊連接按鈕後會出現一個查詢窗口,如圖10.2所示。 (如果有異常,請參考第三章)

        圖10.2

    在執行查詢之前,你需要選擇數據庫。安裝SQL Sever時你已為自己創建了一個數據庫,SQL Sever還有許多系統數據庫,如master,model,msdb,和tempdb。
    方便的是,SQL Sever帶有一個特殊的名為pubs的例子數據庫。庫pubs中包含供一個虛擬的出版商使用的各個表。文檔中所有的例子程序都是針對這個庫來設計的。本書中的許多例子也使用這個數據庫。
    在查詢窗口頂部的DB下拉框中選擇數據庫pubs,這樣你就選擇了數據庫。你所有的查詢都將針對這個庫中的各個表來執行。現在你可以執行你的第一個查詢了。這真讓人興奮!
    你的第一個查詢將針對一個名為autrors的表,表中包含所有為某個虛擬出版商工作的作者的相關數據。單擊查詢窗口並輸入以下的語句:

     SELECT phone FROM authors WHERE au_name=”Ringer”

    輸入完成後,單擊執行查詢按鈕(一個綠色三角形,看起來像VCR播放鍵)。單擊此按鈕後,任何出現在查詢窗口中的語句均會被執行。查詢窗口會自動變成結果顯示窗口,你可以看到查詢的結果(見圖10.3)。
    你看到的查詢結果也許與圖10.3所示的不同。在SQL Sever的不同版本中,庫pubs中的數據會有所不同。對SQL Sever 6.5來說,將會找到兩條記錄。結果顯示窗口中應顯示如下內容:
    
     phone
……………….
801 826_0752
801 826_0752
     (2 row(s) affected)

  
 
  圖10.3

    你所執行的SELECT語句從表authors中取出所有名字為Ringer的作者的電話號碼。你通過在WHERE子句中使用特殊的選擇條件來限制查詢的結果。你也可以忽略選擇條件,從表中取出所有作者的電話號碼。要做到這一點,單擊Query標籤,返回到查詢窗口,輸入以下的SELECT語句:

SELECT Phone FROM authors
    這個查詢執行後,會取出表authors中的所有電話號碼(沒有特定的順序)。如果表authors中包含一百個電話號碼,會有一百個記錄被取出,如果表中有十億個電話號碼,這十億條記錄都會被取出(這也許需要一些時間)。
    表authrs的字段包括姓,名字,電話號碼,地址,城市,州和郵政編碼。通過在SELECT語句的第一部份指定它們,你可以從表中取出任何一個字段。你可以在一個SELECT語句中一次取出多個字段,比如:

     SELECT au_fname ,au_lname, phone FROM authors

    這個SELECT語句執行後,將取出這三個列的所有值。下面是這個查詢的結果的一個示例(為了節省紙張,只顯示查詢結果的一部分,其餘記錄用省略號代替):

     au_fname au_lname phone
     ………………………………………………………………………….
     Johnson White 408 496_7223
     Marjorie Green 415 986_7020
     Cheryl Carson 415 548_7723
     Michael O’Leary 408 286_2428
     …
     (23 row(s) affected)

    在SELECT語句中,你需要列出多少個字段,你就可以列出多少。不要忘了把字段名用逗號隔開。你也可以用星號(*)從一個表中取出所有的字段。這裡有一個使用星號的例子:

     SELECT * FROM authors

    這個SELECT語句執行後,表中的所有字段的值都被取出。你會發現你將在SQL查詢中頻繁使用星號。

    技巧:
    你可以使用星號來查看一個表的所有列的名字。要做到這一點,只需要在執行完SELECT語句後看一下查詢結果的列標題。

操作多個表
    到現在為止,你只嘗試了用一句SQL查詢從一個表中取出數據。你也可以用一個SELECT語句同時從多個表中取出數據,只需在SELECT語句的FROM從句中列出要從中取出數據的表名稱即可:
        
     SELECT au_lname ,title FROM authors, titles

    這個SELECT語句執行時,同時從表authors和表titles中取出數據。從表authors中取出所有的作者名字,從表titles中取出所有的書名。在ISQL/w程序中執行這個查詢,看一下查詢結果。你會發現一些奇怪的出乎意料的情況:作者的名字並沒有和它們所著的書相匹配,而是出現了作者名字和書名的所有可能的組合,這也許不是你所希望見到的。
    出了什麼差錯?問題在於你沒有指明這兩個表之間的關係。你沒有通過任何方式告訴SQL如何把表和表關聯在一起。由於不知道如何關聯兩個表,服務器只能簡單地返回取自兩個表中的記錄的所有可能組合。
    要從兩個表中選出有意義的記錄組合,你需要通過建立兩表中字段的關係來關聯兩個表。要做到這一點的途徑之一是創建第三個表,專門用來描述另外兩個表的字段之間的關係。
    表authors有一個名為au_id的字段,包含有每個作者的唯一標識。表titles有一個名為title_id的字段,包含每個書名的唯一標識。如果你能在字段au_id和字段title_id之間建立一個關係,你就可以關聯這兩個表。數據庫pubs中有一個名為titleauthor的表,正是用來完成這個工作。表中的每個記錄包括兩個字段,用來把表titles和表authors關聯在一起。下面的SELECT語句使用了這三個表以得到正確的結果:

     SELECT au_name,title FROM authors,titles,titleauthor
                             WHERE authors.au_id=titleauthor.au_id
                              AND titles.title_id=titleauthor.title_id

    當這個SELECT語句執行時,每個作者都將與正確的書名相匹配。表titleauthor指明了表authors和表titles的關係,它通過包含分別來自兩個表的各一個字段實現這一點。第三個表的唯一目的是在另外兩個表的字段之間建立關係。它本身不包含任何附加數據。
    注意在這個例子中字段名是如何書寫的。為了區別表authors和表titles中相同的字段名au_id,每個字段名前面都加上了表名前綴和一個句號。名為author.au_id的字段屬於表authors,名為titleauthor.au_id的字段屬於表titleauthor,兩者不會混淆。
    通過使用第三個表,你可以在兩個表的字段之間建立各種類型的關係。例如,一個作者也許寫了許多不同的書,或者一本書也許由許多不同的作者共同完成。當兩個表的字段之間有這種“多對多”的關係時,你需要使用第三個表來指明這種關係。
    但是,在許多情況下,兩個表之間的關係並不復雜。比如你需要指明表titles和表publishers之間的關係。因為一個書名不可能與多個出版商相匹配,你不需要通過第三個表來指明這兩個表之間的關係。要指明表titles和表publishers之間的關係,你只要讓這兩個表有一個公共的字段就可以了。在數據庫pubs中,表titles和表publishers都有一個名為pub_id的字段。如果你想得到書名及其出版商的一個列表,你可以使用如下的語句:
     SELECT title,pub_name FROM titles,publishers
                             WHERE titles.pub_id=publishers.pub_id

    當然,如果一本書是由兩個出版商聯合出版的,那麼你需要第三個表來代表這種關係。
    通常,當你予先知道兩個表的字段間存在“多對多”關係時,就使用第三個表來關聯這兩個表。反之,如果兩個表的字段間只有“一對一”或“一對多”關係,你可以使用公共字段來關聯它門。

操作字段
    通常,當你從一個表中取出字段值時,該值與創建該表時所定義的字段名聯繫在一起。如果你從表authors中選擇所有的作者名字,所有的值將會與字段名au_lname相聯繫。但是在某些情況下,你需要對字段名進行操作。在SELECT語句中,你可以在缺省字段名後面僅跟一個新名字來取代它。例如,可以用一個更直觀易讀的名字Author Last Name來代替字段名au_lname:

     SELECT au_lname “Author Last Name” FROM authors

    當這個SELECT語句執行時,來自字段au_lname的值會與“Author Last Name”相聯繫。查詢結果可能是這樣:
         
       Author Last Name
       ……………………………………………………………………..
       White
       Green
       Carson
       O’Leary
       Straight
       …
       (23 row(s) affected)
 
注意字段標題不再是au_lname,而是被Author Last Name所取代。
    你也可以通過執行運算,來操作從一個表返回的字段值。例如,如果你想把表titles中的所有書的價格加倍,你可以使用下面的SELECT語句:
    
     SELECT price*2 FROM titles

    當這個查詢執行時,每本書的價格從表中取出時都會加倍。但是,通過這種途徑操作字段不會改變存儲在表中的書價。對字段的運算只會影響SELECT語句的輸出,而不會影響表中的數據。為了同時顯示書的原始價格和漲價後的新價格,你可以使用下面的查詢:

SELECT price “Original price”, price*2 “New price” FROM titles

    當數據從表titles中取出時,原始價格顯示在標題Original price下面,加倍後的價格顯示在標題New price下面。結果可能是這樣:
          
       original price new price
       ……………………………………………………………….
19.99 39.98
11.95 23.90
2.99 5.98
19.99 39.98

(18 row(s) affected)
        
    你可以使用大多數標準的數學運算符來操作字段值,如加(+),減(-),乘(*)和除(/)。你也可以一次對多個字段進行運算,例如:

      SELECT price*ytd_sales “total revenue” FROM titles

    在這個例子中,通過把價格與銷售量相乘,計算出了每種書的總銷售額。這個SELECT語句的結果將是這樣的:
       
       total revenue
       ……………………………………………..
       81,859,05
       46,318,20
       55,978,78
       81,859,05
       40,619,68
       …
       (18 row(s) affected)
  
    最後,你還可以使用連接運算符(它看起來像個加號)來連接兩個字符型字段:

       SELECT au_fname+” “+au_lname “author name” FROM authors

    在這個例子中,你把字段au_fname和字段au_lname粘貼在一起,中間用一個逗號隔開,並把查詢結果的標題指定為author name。這個語句的執行結果將是這樣的:

        author names
        …………………………………………………………
        Johnson White
        Marjorie Green
        Cheryl Carson
       Michael O’Leary
       Dean Straight
       …
       (23 row(s) affected)
 
    可以看到,SQL為你提供了對查詢結果的許多控制。你應該在ASP編程過程中充分利用這些優點。使用SQL來操作查詢結果幾乎總是比使用有同樣作用的腳本效率更高。

排序查詢結果
    本章的介紹中曾強調過,SQL表沒有內在的順序。例如,從一個表中取第二個記錄是沒有意義的。從SQL的角度看來,沒有一個記錄在任何其他記錄之前。
然而,你可以操縱一個SQL查詢結果的順序。在缺省情況下,當記錄從表中取出時,記錄不以特定的順序出現。例如,當從表authors中取出字段au_lname時,查詢結果顯示成這樣:

        au_lname
        …………………………………….
        White
        Green
        Carson
        O’Leary
        Straight
        …
        (23 row(s) affected)
   
    看一列沒有特定順序的名字是很不方便的。如果把這些名字按字母順序排列,讀起來就會容易得多。通過使用ORDER BY子句,你可以強制一個查詢結果按升序排列,就像這樣:

     SELECT au_lname FROM authors ORDER BY au_lname

    當這個SELECT語句執行時,作者名字的顯示將按字母順序排列。 ORDER BY子句將作者名字按升序排列。
    你也可以同時對多個列使用ORDER BY子句。例如,如果你想同時按升序顯示字段au_lname和字段au_fname,你需要對兩個字段都進行排序:

     SELECT au_lname,au_fname FROM authors ORDER BY au_lname ,au_fname

    這個查詢首先把結果按au_lname字段進行排序,然後按字段au_fname排序。記錄將按如下的順序取出:

       au_lname au_fname
       …………………………………………………………………….
       Bennet Abraham
       Ringer Albert
       Ringer Anne
       Smith Meander
       …
       (23 row(s) affected)

    注意有兩個作者有相同的名字Ringer。名為Albert Ringer的作者出現名為Anne Ringer的作者之前,這是因為姓Albert按字母順序應排在姓Anne之前。
如果你想把查詢結果按相反的順序排列,你可以使用關鍵字DESC。關鍵字DESC把查詢結果按降序排列,如下例所示:
     
        SELECT au_lname,au_fname FROM authors
               WHERE au_lname=”Ringer” ORDER BY au_lname ,au_fname DESC

    這個查詢從表authors中取出所有名字為Ringer的作者記錄。 ORDER BY子句根據作者的名字和姓,將查詢結果按降序排列。結果是這樣的:

       au_lname au_fname
       ……………………………………………………………………………………….
       Ringer Anne
       Ringer Albert
       (2 row(s) affectec)

    注意在這個表中,姓Anne出現在姓Albert之前。作者名字按降序顯示。
你也可以按數值型字段對一個查詢結果進行排序。例如,如果你想按降序取出所有書的價格,你可以使用如下的SQL查詢:

        SELECT price FROM titles ORDER BY price DESC

    這個SELECT語句從表中取出所有書的價格,顯示結果時,價格低的書先顯示,價格高的書後顯示。

    警告:
    不是特別需要時,不要對查詢結果進行排序,因為服務器完成這項工作要費些力氣。這意味著帶有ORDER BY子句的SELECT語句執行起來比一般的SELECT語句花的時間長。

取出互不相同的記錄
    一個表有可能在同一列中有重複的值。例如,數據庫pubs的表authors中有兩個作者的名字是Ringer。如果你從這個表中取出所有的名字,名字Ringer將會顯示兩次。
    在特定情況下,你可能只有興趣從一個表中取出互不相同的值。如果一個字段有重複的值,你也許希望每個值只被選取一次,你可以使用關鍵字DISTINCT來做到這一點:

      SELCET DISTINCT au_lname FROM authors WHERE au_lname=”Ringer”

    當這個SELECT語句執行時,只返回一個記錄。通過在SELECT語句中包含關鍵字DISTINCT,你可以刪除所有重複的值。例如,假設有一個關於新聞組信息發布的表,你想取出所有曾在這個新聞組中發布信息的人的名字,那麼你可以使用關鍵字DISTINCT。每個用戶的名字只取一次——儘管有的用戶發布了不止一篇信息。
    警告:
    如同ORDER BY子句一樣,強制服務器返回互不相同的值也會增加運行開銷。福氣不得不花費一些時間來完成這項工作。因此,不是必須的時候不要使用關鍵字DISTINCT。

創建新表
    前面說過,數據庫中的所有數據存儲在表中。數據表包括行和列。列決定了表中數據的類型。行包含了實際的數據。
    例如,數據庫pubs中的表authors有九個字段。其中的一個字段名為為au_lname,這個字段被用來存儲作者的名字信息。每次向這個表中添加新作者時,作者名字就被添加到這個字段,產生一條新記錄。
    通過定義字段,你可以創建一個新表。每個字段有一個名字和一個特定的數據類型(數據類型在後面的“字段類型”一節中講述),例如字段au_lname存儲的是字符型數據。一個字段也可以存儲其它類型的數據。
    使用SQL Sever,創建一個新表的方法是很多的。你可以可執行一個SQL語句或使用SQL事務管理器(SQL Enterprise Manager)來創建一個新表。在下一節裡,你將學會如何用SQL語句來創建一個新表。

用SQL創建新表
    注意:
    如果你還沒有建立自己的數據庫,現在就跳回到第三章創建這個庫。你絕不能向master,tempdb或任何其他任何系統數據庫中添加數據。

    從SQL Sever程序組(在任務欄中)中啟動ISQL/w程序。出現查詢窗口後,從窗口頂部的下拉列表中選擇你在第三章所創建的數據庫。下一步,在查詢窗口中鍵入下面的SQL語句,單擊執行查詢按鈕,執行這個語句:

       CREATE TABLE guestbook (visitor VARCHAR(40),comments TEXT,entrydate
                                          DATETIME)

如果一切正常,你會在結果窗口中看到如下的文字(如果出現異常,請參閱第三章):

       This command dit not return data ,and it did not return any rows

祝賀你,你已經建立了你的第一個表!
    你所創建的表名為guestbook,你可以使用這個表來存儲來字你站點訪問者的信息。你是用REEATE TABLE語句創建的這個表,這個語句有兩部分:第一部份指定表的名子;第二部份是括在括號中的各字段的名稱和屬性,相互之間用逗號隔開。
    表guestbook有三個字段:visitor,comments和entrydate。 visitor字段存儲訪問者的名字,comments字段存儲訪問者對你站點的意見,entrydate字段存儲訪問者訪問你站點的日期和時間。
    注意每個字段名後面都跟有一個專門的表達式。例如,字段名comments後面跟有表達式TEXT。這個表達式指定了字段的數據類型。數據類型決定了一個字段可以存儲什麼樣的數據。因為字段comments包含文本信息,其數據類型定義為文本型。
    字段有許多不同的數據類型。下一小節講述SQL所支持的一些重要的數據類型。

字段類型
    不同的字段類型用來存放不同類型的數據。創建和使用表時,更你應該理解五種常用的字段類型:字符型,文本型,數值型,邏輯性和日期型。

字符型數據
    字符型數據非常有用。當你需要存儲短的字符串信息時,你總是要用到字符型數據。例如,你可以把從HTML form的文本框中蒐集到的信息放在字符型字段中。
    要建立一個字段用來存放可變長度的字符串信息,你可以使用表達式VARCHAR。考慮你前面創建的表guestbook:

      CREATE TABLE guestbook (visitor VARCHAR(40),comments TEXT,entrydate
                                          DATETIME)

    在這個例子中,字段visitor的數據類型為VARCHAR。注意跟在數據類型後面的括號中的數字。這個數字指定了這個字段所允許存放的字符串的最大長度。在這個例子中,字段visitor能存放的字符串最長為四十個字符。如果名字太長,字符串會被截斷,只保留四十個字符。
     VARCHAR類型可以存儲的字符串最長為255個字符。要存儲更長的字符串數據,可以使用文本型數據(下一節中講述)。
    另一種字符型數據用來存儲固定長度的字符數據。下面是一個使用這種數據類型的例子:

      CREATE TABLE guestbook (visitor CHAR(40),comments TEXT,entrydate
                                          DATETIME)

    在這個例子中,字段visitor被用來存儲四十個字符的固定長度字符串。表達式CHAR指定了這個字段應該是固定長度的字符串。
     VARCHAR型和CHAR型數據的這個差別是細微的,但是非常重要。假如你向一個長度為四十個字符的VARCHAR型字段中輸入數據Bill Gates。當你以後從這個字段中取出此數據時,你取出的數據其長度為十個字符——字符串Bill Gates的長度。
    現在假如你把字符串輸入一個長度為四十個字符的CHAR型字段中,那麼當你取出數據時,所取出的數據長度將是四十個字符。字符串的後面會被附加多餘的空格。
    當你建立自己的站點時,你會發現使用VARCHAR型字段要比CHAR型字段方便的多。使用VARCHAR型字段時,你不需要為剪掉你數據中多餘的空格而操心。
     VARCHAR型字段的另一個突出的好處是它可以比CHAR型字段佔用更少的內存和硬盤空間。當你的數據庫很大時,這種內存和磁盤空間的節省會變得非常重要。

文本型數據
    字符型數據限制了字符串的長度不能超過255個字符。而使用文本型數據,你可以存放超過二十億個字符的字符串。當你需要存儲大串的字符時,應該使用文本型數據。
    這裡有一個使用文本型數據的例子:

     CREATE TABLE guestbook (visitor VARCHAR(40),comments TEXT,entrydate
                                          DATETIME)

    在這個例子中,字段comments被用來存放訪問者對你站點的意見。注意文本型數據沒有長度,而上一節中所講的字符型數據是有長度的。一個文本型字段中的數據通常要么為空,要么很大。
    當你從HTML form的多行文本編輯框(TEXTAREA)中收集數據時,你應該把收集的信息存儲於文本型字段中。但是,無論何時,只要你能避免使用文本型字段,你就應該不適用它。文本型字段既大且慢,濫用文本型字段會使服務器速度變慢。文本型字段還會吃掉大量的磁盤空間。
    警告:
    一旦你向文本型字段中輸入了任何數據(甚至是空值),就會有2K的空間被自動分配給該數據。除非刪除該記錄,否則你無法收回這部分存儲空間。

數值型數據
     SQL Sever支持許多種不同的數值型數據。你可以存儲整數、小數、和錢數。
    通常,當你需要在表中的存放數字時,你要使用整型(INT)數據。 INT型數據的表數範圍是從-2,147,483,647到2,147,483,647的整數。下面是一個如何使用INT型數據的例子:

        CREATE TABLE visitlog (visitor VARCHAR(40),numvisits INT)

    這個表可以用來記錄你站點被訪問的次數。只要沒有人訪問你的站點超過2,147,483,647次,nubvisits字段就可以存儲訪問次數。
    為了節省內存空間,你可以使用SMALLINT型數據。 SMALLINT型數據可以存儲從-32768到32768的整數。這種數據類型的使用方法與INT型完全相同。
    最後,如果你實在需要節省空間,你可以使用TINYINT型數據。同樣,這種類型的使用方法也與INT型相同,不同的是這種類型的字段只能存儲從0到255的整數。 TINYINT型字段不能用來存儲負數。
    通常,為了節省空間,應該盡可能的使用最小的整型數據。一個TINYINT型數據只佔用一個字節;一個INT型數據佔用四個字節。這看起來似乎差別不大,但是在比較大的表中,字節數的增長是很快的。另一方面,一旦你已經創建了一個字段,要修改它是很困難的。因此,為安全起見,你應該預測以下,一個字段所需要存儲的數值最大有可能是多大,然後選擇適當的數據類型。
    為了能對字段所存放的數據有更多的控制,你可以使用NUMERIC型數據來同時表示一個數的整數部分和小數部分。 NUMERIC型數據使你能表示非常大的數——比INT型數據要大得多。一個NUMERIC型字段可以存儲從-1038到1038範圍內的數。 NUMERIC型數據還使你能表示有小數部分的數。例如,你可以在NUMERIC型字段中存儲小數3.14。
    當定義一個NUMERIC型字段時,你需要同時指定整數部分的大小和小數部分的大小。這裡有一個使用這種數據類型的例子:
     
        CREATE TABLE numeric_data (bignumber NUMERIC(28,0),
                                    fraction NUMERIC (5,4) )

    當這個語句執行時,將創建一個名為numeric_data的包含兩個字段的表。字段bignumber可以存儲直到28位的整數。字段fraction可以存儲有五位整數部分和四位小數部分的小數。
    一個NUMERIC型數據的整數部分最大隻能有28位,小數部分的位數必須小於或等於整數部分的位數,小數部分可以是零。
    你可以使用INT型或NUMERIC型數據來存儲錢數。但是,專門有另外兩種數據類型用於此目的。如果你希望你的網點能掙很多錢,你可以使用MONEY型數據。如果你的野心不大,你可以使用SMALLMONEY型數據。 MONEY型數據可以存儲從-922,337,203,685,477.5808到922,337,203,685,477.5807的錢數。如果你需要存儲比這還大的金額,你可以使用NUMERIC型數據。
     SMALLMONEY型數據只能存儲從-214,748.3648到214,748.3647的錢數。同樣,如果可以的話,你應該用SMALLMONEY型來代替MONEY型數據,以節省空間。下面的例子顯示瞭如何使用這兩種表示錢的數據類型:

       CREATE TABLE products (product VARCHAR(40),price MONEY,
                                Discount_price SMALLMONEY)

    這個表可以用來存儲商品的折扣和普通售價。字段price的數據類型是MONEY,字段discount_price的數據類型是SMALLMONEY。

存儲邏輯值
    如果你使用複選框(CHECKBOX)從網頁中蒐集信息,你可以把此信息存儲在BIT型字段中。 BIT型字段只能取兩個值:0或1。這裡有一個如何使用這種字段的例子:

     CREATE TABLE opinion (visitor VARCHAR(40),good BIT)

    這個表可以用來存放對你的網點進行民意調查所得的信息。訪問者可以投票表示他們是否喜歡你的網點。如果他們投YES,就在BIT型字段中存入1。反之,如果他們投NO,就在字段中存入0(在下一章裡,你將學會如何計算投票)。
    當心,在你創建好一個表之後,你不能向表中添加BIT型字段。如果你打算在一個表中包含BIT型字段,你必須在創建表時完成。

存儲日期和時間
    當你建立一個網點時,你也許需要記錄在一段時間內的訪問者數量。為了能夠存儲日期和時間,你需要使用DATETIME型數據,如下例所示:

     CREATE TABL visitorlog( visitor VARCHAR (40), arrivaltime DATETIME ,
                                departuretime DATETIME)

    這個表可以用來記錄訪問者進入和離開你網點的時間和日期。一個DATETIME型的字段可以存儲的日期範圍是從1753年1月1日第一毫秒到9999年12月31日最後一毫秒。
    如果你不需要覆蓋這麼大範圍的日期和時間,你可以使用SMALLDATETIME型數據。它與DATETIME型數據同樣使用,只不過它能表示的日期和時間範圍比DATETIME型數據小,而且不如DATETIME型數據精確。一個SMALLDATETIME型的字段能夠存儲從1900年1月1日到2079年6月6日的日期,它只能精確到秒。
     DATETIME型字段在你輸入日期和時間之前並不包含實際的數據,認識這一點是重要的。在下一章,你將學習怎樣使用大量的SQL函數來讀取和操作日期和時間(參見下面的“缺省值”一節)。你也可以在VBScript和JScript中使用日期和時間函數來向一個DATETIME型字段中輸入日期和時間。

字段屬性
    上一節介紹瞭如何建立包含不同類型字段的表。在這一節中,你將學會如何使用字段的三個屬性。這些屬性允許你控制空值,缺省值和標識值。

允許和禁止空值
    大多數字段可以接受空值(NULL)。當一個字段接受了空值後,如果你不改變它,它將一直保持空值。空值(NULL)和零是不同的,嚴格的說,空值表示沒有任何值。
為了允許一個字段接受空值,你要在字段定義的後面使用表達式NULL。例如,下面的表中兩個字段都允許接受空值:
      
       CREATE TABLE empty (empty1 CHAR (40) NULL,empty2 INT NULL(
   
    注意:
     BIT型數據不能是空值。一個這種類型的字段必須取0或者1。

    有時你需要禁止一個字段使用空值。例如,假設有一個表存儲著信用卡號碼和信用卡有效日期,你不會希望有人輸入一個信用卡號碼但不輸入有效日期。為了強制兩個字段都輸入數據,你可以用下面的方法建立這個表:
      
       CREATE TABLE creditcards (creditcard_number CHAR(20) NOT NULL,
                                  Creditcard_expire DATETIME NOT NULL)
    注意字段定義的後面跟有表達式NOT NULL。通過包含表達式NOT NULL,你可以禁止任何人只在一個字段中插入數據,而不輸入另一個字段的數據。
    你將會發現,在你建設自己的網點過程中,這種禁止空值的能力是非常有用的。如果你指定一個字段不能接受空值,那麼當你試圖輸入一個空值時,會有錯誤警告。這些錯誤警告可以為程序調試提供有價值的線索。

缺省值
    假設有一個存儲地址信息的表,這個表的字段包括街道、城市、州、郵政編碼和國家。如果你預計地址的大部分是在美國,你可以把這個值作為country字段的缺省值。
    為了在創建一個表時指定缺省值,你可以使用表達式DEFAULT。請看下面這個在創建表時使用缺省值的例子:
       
       CREATE TABLE addresses (street VARCHAR(60) NULL,
                                city VARCHAR(40) NULL,
                                state VARCHAR(20) NULL
                                zip VARCHAR(20) NULL,
                                country VARCHAR(30) DEFAULT ‘USA’)

    在這個例子中,字段country的缺省值被指定為美國。注意單引號的使用,引號指明這是字符型數據。為了給非字符型的字段指定缺省值,不要把該值擴在引號中:
    
       CREATE TABLE orders(price MONEY DEFAULT $38.00,
                            quantity INT DEFAULT 50,
                             entrydate DATETIME DEFAULT GETDATE())

    在這個CREATE TABLE語句中,每個字段都指定了一個缺省值。注意DATETIME型字段entrydate所指定的缺省值,該缺省值是函數Getdate()的返回值,該函數返回當前的日期和時間。

標識字段
    每個表可以有一個也只能有一個標識字段。一個標識字段是唯一標識表中每條記錄的特殊字段。例如,數據庫pubs中的表jobs包含了一個唯一標識每個工作標識字段:
    
       job_id job_desc
       …………………………………………………………….
1 New Hire Job not specified
2 Chief Executive officer
3 Bushness Operations Manager
4 Chief Financial Officier
5 Publisher
 
    字段job_id為每個工作提供了唯一的一個數字。如果你決定增加一個新工作,新增記錄的job_id字段會被自動賦給一個新的唯一值。
    為了建立一個標識字段,你只需在字段定義後面加上表達式IDENTITY即可。你只能把NUMERIC型或INT型字段設為標識字段,這裡有一個例子:
  
        CREATE TABLE visitorID (theID NUBERIC(18) IDENTITY,name VARCHAR(40))
   
    這個語句所創建的表包含一個名為theid的標識字段。每當一個新的訪問者名字添加到這個表中時,這個字段就被自動賦給一個新值。你可以用這個表為你的站點的每一個用戶提供唯一標識。
    技巧:
    建立一個標示字段時,注意使用足夠大的數據類型。例如你使用TINYINT型數據,那麼你只能向表中添加255個記錄。如果你預計一個表可能會變得很大,你應該使用NUMERIC型數據。

    標識字段的存在會使你想嘗試許多不可能的事情。例如,你也許想利用標識字段來對記錄進行基於它們在表中位置的運算。你應該拋棄這種意圖。每個記錄的標識字段的值是互不相同的,但是,這並不禁止一個標識字段的標識數字之間存在間隔。例如,你永遠不要試圖利用一個表的標識字段來取出表中的前十個記錄。這種操作會導致失敗,比如說6號記錄和7號記錄根本不存在。

使用SQL事務管理器創建新表
    你可以使用前面幾節所講的方法創建新表。但是,使用事務管理器創建新表會更容易。這一節介紹如何使用這個程序創建新表。
    從任務欄的SQL Sever程序組中選擇SQL Enterprise Manager,啟動該程序,你會看到如圖10.4所示的窗口。瀏覽服務管理器窗口中的樹形結構,選擇名為Database的文件夾。打開文件夾Database後,選擇你在第三章中所建立的數據庫。
    
注意:
    如果你還沒有創建自己的數據庫,回到第三章創建它。你決不要向master,tempdb或任何其它系統數據庫中添加數據。
  
    在選擇了數據庫之後,你會看到一個名為Group/users的文件夾和一個名為objects的文件夾。打開文件夾objects,你會看到許多文件夾,其中一個名為Tables。用右鍵單擊文件夾Tables並選擇New table,就會出現如圖10.5所示的窗口。
    你可以使用Manager Tables窗口來創建一個新表。 Manager Tables窗口有7個列:Key,Column,Name,Datatype,Size,Nulls和Default。 Manager Tables窗口中的每一行標明表中一個字段的信息。

        圖10.4
            10.5

    要建立一個新表,你至少要輸入一行信息。在名為Column Name的列下面鍵入mycolumn。下一步,選擇Datatype列,並從下拉列表中選擇CHAR。當你在這兩個列中輸入信息後,窗口將是如圖10.6所示的樣子。

 圖10.6

    你已經建立了一個只有一個字段的簡單的表。單擊保存按扭保存這個新表。當要求你輸入新表的名字時,輸入mytable並單擊OK。現在這個表已經保存到了你的數據庫中。
如果你打開服務管理器窗口中的文件夾Tables,你會看到你所建立的新表被列出。你可以雙擊該表的圖表來編輯它,這時Manager Tables窗口會重新出現,你可以增加新的字段並重新保存。
    用SQL事務管理器可以做的工作,你都可以用SQL語句來實現。但是,事務管理器使得建表過程變得更加簡單。

向表中添加數據
    下一章將討論如何使用SQL向一個表中插入數據。但是,如果你需要向一個表中添加許多條記錄,使用SQL語句輸入數據是很不方便的。幸運的是,Microsoft SQL Sever帶有一個稱為Microsoft Query的客戶端應用程序,這個程序使得向表中添加數據變得容易了。
    啟動位於任務欄SQL Sever程序組中的Microsoft Query程序。從窗口頂部的菜單中選擇File|New Query。這時會顯示一個Select Data Source對話框(見圖10.7)。選擇你的數據源名字並單擊Use。
    
圖10。7

    輸入你的登錄帳號和密碼後,程序要求你選擇一個表和一個數據庫。選擇你在上一節中所建立的表(mytable ),單擊按鈕Add,然後單擊按鈕Close關閉該對話框。
    在窗口的左上角會出現一個對話框,框中是取自表mytable的一列字段名。你可以雙擊任何一個字段,把它添加到主窗口中。如果你雙擊星號(*)字符,所有的字段都會被添加到主窗口中。
    如果你的表中有記錄,它們現在已經出現在主窗口的字段標題下面了。但是,因為你剛剛建立了這個表,表還是空的。要添加新記錄,選擇Records|Allow Editing,主窗口中就會出現一條新記錄。輸入一行數據完成這個記錄,就向表中添加了一條新記錄。
       
                圖10。8

    當你轉到下一條新記錄時,你向上一條記錄中輸入的值會自動被保存。如果你需要,你可以用Microsoft Query向表中輸入幾百條記錄。

刪除和修改表
    你應該在建立表之前仔細設計它們,因為你在改變一個已經存在的表時會受到很大的限制。例如,一旦已經建立了一個表,你就不能刪除表中的字段或者改變字段的數據類型。在這種情況你所能做的是刪除這個表,然後重頭開始(參見第十一章“中級SQL”中的“使用SQL創建記錄和表”一節)。
    要刪除一個表,你可以使用SQL語句DROP TABLE。例如,又從數據庫中徹底刪除表mytable,你要使用如下的語句:
    
        DROP TABLE mytable

    警告:
    使用DROP TABLE命令時一定要小心。一旦一個表被刪除之後,你將無法恢復它。

    當你建設一個站點時,你很可能需要向數據庫中輸入測試數據。而當你準備向世界提供你的網點時,你會想清空表中的這些測試信息。如果你想清除表中的所有數據但不刪除這個表,你可以使用TRUNCATE TABLE語句。例如,下面的這個SQL語句從表mytable中刪除所有數據:

         TRUNCATE TABLE mytable

    雖然你不能刪除和修改已經存在的字段,但你可以增加新字段。最容易的實現方法是使用SQL事務管理器中的Manager Tables窗口。你也可以使用SQL語句ALTER TABLE。下面是一個如何使用這種語句的例子:

      ALTER TABLE mytable ADD mynewcolumn INT NULL

    這個語句向表mytable中增加了一個新字段mynewcolumn。當你增加新字段時,你必須允許它接受空值,因為表中原來可能已經有了許多記錄。

總結
    這一章向你介紹了SQL。使用SQL,你可以操作Microsoft SQL Sever數據庫。你已經學會了使用SELECT語句從數據庫中取出數據,你還學會了怎樣使用CREATE TABLE語句和SQL事務管理器來創建新表。最後,你學會瞭如何指明一系列重要的字段屬性。
下一章將介紹如何使用索引來增強SQL查詢的操作。還將通過許多其它的SQL語句和函數,使你的SQL知識得到進一步擴充。

 

第十一章中級SQL

               本章內容
                ■創建索引
                ■SQL核心語句
                ■集合函數
■其它常用的SQL表達式,
  函數,和過程

 

    第十章“SQL基礎”向你初步介紹了SQL。你學會瞭如何用SELECT語句進行查詢,你還學會瞭如何建立自己的表。在這一章裡,你將加深你的SQL知識。你將學習如何建立索引來加快查詢速度。你還將學會如果用更多的SQL語句和函數來操作表中的數據。

建立索引
    假設你想找到本書中的某一個句子。你可以一頁一頁地逐頁搜索,但這會花很多時間。而通過使用本書的索引,你可以很快地找到你要搜索的主題。
    表的索引與附在一本書後面的索引非常相似。它可以極大地提高查詢的速度。對一個較大的表來說,通過加索引,一個通常要花費幾個小時來完成的查詢只要幾分鐘就可以完成。因此沒有理由對需要頻繁查詢的表增加索引。
    注意:
    當你的內存容量或硬盤空間不足時,也許你不想給一個表增加索引。對於包含索引的數據庫,SQL Sever需要一個可觀的額外空間。例如,要建立一個聚簇索引,需要大約1.2倍於數據大小的空間。要看一看一個表的索引在數據庫中所佔的空間大小,你可以使用系統存儲過程sp_spaceused,對象名指定為被索引的表名。

聚簇索引和非聚簇索引
    假設你已經通過本書的索引找到了一個句子所在的頁碼。一旦已經知道了頁碼後,你很可能漫無目的翻尋這本書,直至找到正確的頁碼。通過隨機的翻尋,你最終可以到達正確的頁碼。但是,有一種找到頁碼的更有效的方法。
    首先,把書翻到大概一半的地方,如果要找的頁碼比半本書處的頁碼小,就書翻到四分之一處,否則,就把書翻到四分之三的地方。通過這種方法,你可以繼續把書分成更小的部分,直至找到正確的頁碼附近。這是找到書頁的非常有效的一種方法。
SQL Sever的表索引以類似的方式工作。一個表索引由一組頁組成,這些頁構成了一個樹形結構。根頁通過指向另外兩個頁,把一個表的記錄從邏輯上分成和兩個部分。而根頁所指向的兩個頁又分別把記錄分割成更小的部分。每個頁都把記錄分成更小的分割,直至到達葉級頁。
    索引有兩種類型:聚簇索引和非聚簇索引。在聚簇索引中,索引樹的葉級頁包含實際的數據:記錄的索引順序與物理順序相同。在非聚簇索引中,葉級頁指向表中的記錄:記錄的物理順序與邏輯順序沒有必然的聯繫。
    聚簇索引非常象目錄表,目錄表的順序與實際的頁碼順序是一致的。非聚簇索引則更像書的標準索引表,索引表中的順序通常與實際的頁碼順序是不一致的。一本書也許有多個索引。例如,它也許同時有主題索引和作者索引。同樣,一個表可以有多個非聚簇索引。
    通常情況下,你使用的是聚簇索引,但是你應該對兩種類型索引的優缺點都有所理解。
    每個表只能有一個聚簇索引,因為一個表中的記錄只能以一種物理順序存放。通常你要對一個表按照標識字段建立聚簇索引。但是,你也可以對其它類型的字段建立聚簇索引,如字符型,數值型和日期時間型字段。
    從建立了聚簇索引的表中取出數據要比建立了非聚簇索引的表快。當你需要取出一定範圍內的數據時,用聚簇索引也比用非聚簇索引好。例如,假設你用一個表來記錄訪問者在你網點上的活動。如果你想取出在一定時間段內的登錄信息,你應該對這個表的DATETIME型字段建立聚簇索引。
    對聚簇索引的主要限制是每個表只能建立一個聚簇索引。但是,一個表可以有不止一個非聚簇索引。實際上,對每個表你最多可以建立249個非聚簇索引。你也可以對一個表同時建立聚簇索引和非聚簇索引。
    假如你不僅想根據日期,而且想根據用戶名從你的網點活動日誌中取數據。在這種情況下,同時建立一個聚簇索引和非聚簇索引是有效的。你可以對日期時間字段建立聚簇索引,對用戶名字段建立非聚簇索引。如果你發現你需要更多的索引方式,你可以增加更多的非聚簇索引。
    非聚簇索引需要大量的硬盤空間和內存。另外,雖然非聚簇索引可以提高從表中取數據的速度,它也會降低向表中插入和更新數據的速度。每當你改變了一個建立了非聚簇索引的表中的數據時,必須同時更新索引。因此你對一個表建立非聚簇索引時要慎重考慮。如果你預計一個表需要頻繁地更新數據,那麼不要對它建立太多非聚簇索引。另外,如果硬盤和內存空間有限,也應該限制使用非聚簇索引的數量。

索引屬性
    這兩種類型的索引都有兩個重要屬性:你可以用兩者中任一種類型同時對多個字段建立索引(複合索引);兩種類型的索引都可以指定為唯一索引。
    你可以對多個字段建立一個複合索引,甚至是複合的聚簇索引。假如有一個表記錄了你的網點訪問者的姓和名字。如果你希望根據完整姓名從表中取數據,你需要建立一個同時對姓字段和名字字段進行的索引。這和分別對兩個字段建立單獨的索引是不同的。當你希望同時對不止一個字段進行查詢時,你應該建立一個對多個字段的索引。如果你希望對各個字段進行分別查詢,你應該對各字段建立獨立的索引。
    兩種類型的索引都可以被指定為唯一索引。如果對一個字段建立了唯一索引,你將不能向這個字段輸入重複的值。一個標識字段會自動成為唯一值字段,但你也可以對其它類型的字段建立唯一索引。假設你用一個表來保存你的網點的用戶密碼,你當然不希望兩個用戶有相同的密碼。通過強制一個字段成為唯一值字段,你可以防止這種情況的發生。

用SQL建立索引
    為了給一個表建立索引,啟動任務欄SQL Sever程序組中的ISQL/w程序。進入查詢窗口後,輸入下面的語句:

        CREATE INDEX mycolumn_index ON mytable (myclumn)

    這個語句建立了一個名為mycolumn_index的索引。你可以給一個索引起任何名字,但你應該在索引名中包含所索引的字段名,這對你將來弄清楚建立該索引的意圖是有幫助的。
    注意:
    在本書中你執行任何SQL語句,都會收到如下的信息:
     This command did not return data,and it did not return any rows
這說明該語句執行成功了。
索引mycolumn_index對錶mytable的mycolumn字段進行。這是個非聚簇索引,也是個非唯一索引。 (這是一個索引的缺省屬性)
如果你需要改變一個索引的類型,你必須刪除原來的索引並重建一個。建立了一個索引後,你可以用下面的SQL語句刪除它:

DROP INDEX mytable.mycolumn_index

注意在DROP INDEX語句中你要包含表的名字。在這個例子中,你刪除的索引是mycolumn_index,它是表mytable的索引。
要建立一個聚簇索引,可以使用關鍵字CLUSTERED。 )記住一個表只能有一個聚簇索引。 (這裡有一個如何對一個表建立聚簇索引的例子:

CREATE CLUSTERED INDEX mycolumn_clust_index ON mytable(mycolumn)

如果表中有重複的記錄,當你試圖用這個語句建立索引時,會出現錯誤。但是有重複記錄的表也可以建立索引;你只要使用關鍵字ALLOW_DUP_ROW把這一點告訴SQL Sever即可:
 
CREATE CLUSTERED INDEX mycolumn_cindex ON mytable(mycolumn)
                      WITH ALLOW_DUP_ROW

這個語句建立了一個允許重複記錄的聚簇索引。你應該盡量避免在一個表中出現重複記錄,但是,如果已經出現了,你可以使用這種方法。
要對一個表建立唯一索引,可以使用關鍵字UNIQUE。對聚簇索引和非聚簇索引都可以使用這個關鍵字。這裡有一個例子:

CREATE UNIQUE COUSTERED INDEX myclumn_cindex ON mytable(mycolumn)

這是你將經常使用的索引建立語句。無論何時,只要可以,你應該盡量對一個對一個表建立唯一聚簇索引來增強查詢操作。
最後,要建立一個對多個字段的索引──複合索引──在索引建立語句中同時包含多個字段名。下面的例子對firstname和lastname兩個字段建立索引:

CREATE INDEX name_index ON username(firstname,lastname)

這個例子對兩個字段建立了單個索引。在一個複合索引中,你最多可以對16個字段進行索引。

用事務管理器建立索引
用事務管理器建立索引比用SQL語句容易的多。使用事務管理器,你可以看到已經建立的索引的列表,並可以通過圖形界面選擇索引選項。
使用事務管理器你可以用兩種方式建立索引:使用Manage Tables窗口或使用Manage Indexes窗口。
    要用Manage Tables窗口建立一個新索引,單擊按鈕Advanced Options(它看起來像一個前面有一加號的表)。這樣就打開了Advanced Options對話框。這個對話框有一部分標名為Primary Key(見圖11.1)。
 
圖11。1

要建立一個新索引,從下拉列表中選擇你想對之建立索引的字段名。如果你想建立一個對多字段的索引,你可以選擇多個字段名。你還可以選擇索引是聚簇的還是非聚簇的。在保存表信息後,索引會自動被建立。在Manage Tables窗口中的字段名旁邊,會出現一把鑰匙。
你已經為你的表建立了“主索引”。主索引必須對不包含空值的字段建立。另外,主索引強制一個字段成為唯一值字段。
要建立沒有這些限制的索引,你需要使用Manage Indexes窗口。從菜單中選擇Manage|Indexes,打開Manage Indexes窗口。在Manage Indexes窗口中,你可以通過下拉框選擇表和特定的索引。 (見圖11.2)。要建立一個新索引,從Index下拉框中選擇New Index.,然後就可以選擇要對之建立索引的字段。單擊按鈕Add,把字段加人到索引中。

圖11。2

你可以為你的索引選擇許多不同的選項。例如,你可以選擇該索引是聚簇的還是非聚簇的。你還可以指定該索引為唯一索引。設計好索引後,單擊按鈕Build,建立該索引。

注意:
唯一索引是指該字段不能有重複的值,而不是只能建立這一個索引。

SQL核心語句
在第十章,你學會瞭如何用SQL SELECT語句從一個表中取數據。但是,到現在為止,還沒有討論如何添加,修改或刪除表中的數據。在這一節中,你將學習這些內容。

插入數據
向表中添加一個新記錄,你要使用SQL INSERT語句。這裡有一個如何使用這種語句的例子:

      INSERT mytable (mycolumn) VALUES (‘some data’)

這個語句把字符串’some data’插入表mytable的mycolumn字段中。將要被插入數據的字段的名字在第一個括號中指定,實際的數據在第二個括號中給出。
INSERT語句的完整句法如下:

INSERT [INTO] {table_name|view_name} [(column_list)] {DEFAULT VALUES |
                 Values_list | select_statement}

如果一個表有多個字段,通過把字段名和字段值用逗號隔開,你可以向所有的字段中插入數據。假設表mytable有三個字段first_column,second_column,和third_column。下面的INSERT語句添加了一條三個字段都有值的完整記錄:

INSERT mytable (first_column,second_column,third_column)
         VALUES (‘some data’,’some more data’,’yet more data’)

注意:
你可以使用INSERT語句向文本型字段中插入數據。但是,如果你需要輸入很長的字符串,你應該使用WRITETEXT語句。這部分內容對本書來說太高級了,因此不加討論。要了解更多的信息,請參考Microsoft SQL Sever的文檔。

如果你在INSERT語句中只指定兩個字段和數據會怎麼樣呢?換句話說,你向一個表中插入一條新記錄,但有一個字段沒有提供數據。在這種情況下,有下面的四種可能:
■如果該字段有一個缺省值,該值會被使用。例如,假設你插入新記錄時沒有給字段third_column提供數據,而這個字段有一個缺省值’some value’。在這種情況下,當新記錄建立時會插入值’some value’。
■如果該字段可以接受空值,而且沒有缺省值,則會被插入空值。
■如果該字段不能接受空值,而且沒有缺省值,就會出現錯誤。你會收到錯誤信息:
The column in table mytable may not be null.
■最後,如果該字段是一個標識字段,那麼它會自動產生一個新值。當你向一個有標識字段的表中插入新記錄時,只要忽略該字段,標識字段會給自己賦一個新值。

    注意:
    向一個有標識字段的表中插入新記錄後,你可以用SQL變量@@identity來訪問新記錄
的標識字段的值。考慮如下的SQL語句:

      INSERT mytable (first_column) VALUES(‘some value’)

      INSERT anothertable(another_first,another_second)
          VALUES(@@identity,’some value’)

    如果表mytable有一個標識字段,該字段的值會被插入表anothertable的another_first字段。這是因為變量@@identity總是保存最後一次插入標識字段的值。
字段another_first應該與字段first_column有相同的數據類型。但是,字段another_first不能是應該標識字段。 Another_first字段用來保存字段first_column的值。

刪除記錄
要從表中刪除一個或多個記錄,需要使用SQL DELETE語句。你可以給DELETE語句提供WHERE子句。 WHERE子句用來選擇要刪除的記錄。例如,下面的這個DELETE語句只刪除字段first_column的值等於’Delete Me’的記錄:
 
DELETE mytable WHERE first_column=’Deltet Me’

DELETE語句的完整句法如下:

        DELETE [FROM] {table_name|view_name} [WHERE clause]

在SQL SELECT語句中可以使用的任何條件都可以在DELECT語句的WHERE子句中使用。例如,下面的這個DELETE語句只刪除那些first_column字段的值為’goodbye’或second_column字段的值為’so long’的記錄:

DELETE mytable WHERE first_column=’goodby’ OR second_column=’so long’

如果你不給DELETE語句提供WHERE子句,表中的所有記錄都將被刪除。你不應該有這種想法。如果你想刪除應該表中的所有記錄,應使用第十章所講的TRUNCATE TABLE語句。

注意:
為什麼要用TRUNCATE TABLE語句代替DELETE語句?當你使用TRUNCATE TABLE語句時,記錄的刪除是不作記錄的。也就是說,這意味著TRUNCATE TABLE要比DELETE快得多。

更新記錄
要修改表中已經存在的一條或多條記錄,應使用SQL UPDATE語句。同DELETE語句一樣,UPDATE語句可以使用WHERE子句來選擇更新特定的記錄。請看這個例子:

UPDATE mytable SET first_column=’Updated!’ WHERE second_column=’Update Me!’

這個UPDATE語句更新所有second_column字段的值為’Update Me!’的記錄。對所有被選中的記錄,字段first_column的值被置為’Updated!’。
下面是UPDATE語句的完整句法:

UPDATE {table_name|view_name} SET [{table_name|view_name}]
        {column_list|variable_list|variable_and_column_list}
        [,{column_list2|variable_list2|variable_and_column_list2}…
         [,{column_listN|variable_listN|variable_and_column_listN}]]
         [WHERE clause]

注意:
你可以對文本型字段使用UPDATE語句。但是,如果你需要更新很長的字符串,應使用UPDATETEXT語句。這部分內容對本書來說太高級了,因此不加討論。要了解更多的信息,請參考Microsoft SQL Sever的文檔。

    
如果你不提供WHERE子句,表中的所有記錄都將被更新。有時這是有用的。例如,如果你想把表titles中的所有書的價格加倍,你可以使用如下的UPDATE語句:
你也可以同時更新多個字段。例如,下面的UPDATE語句同時更新first_column,second_column,和third_column這三個字段:

UPDATE mytable SET first_column=’Updated!’
                      Second_column=’Updated!’
                      Third_column=’Updated!’
                  WHERE first_column=’Update Me1’

技巧:
SQL忽略語句中多餘的空格。你可以把SQL語句寫成任何你最容易讀的格式。

用SELECT創建記錄和表
你也許已經註意到,INSERT語句與DELETE語句和UPDATE語句有一點不同,它一次只操作一個記錄。然而,有一個方法可以使INSERT語句一次添加多個記錄。要作到這一點,你需要把INSERT語句與SELECT語句結合起來,像這樣:

INSERT mytable (first_column,second_column)
SELECT another_first,another_second
FROM anothertable
WHERE another_first=’Copy Me!’

這個語句從anothertable拷貝記錄到mytable.只有表anothertable中字段another_first的值為’Copy Me! ’的記錄才被拷貝。
當為一個表中的記錄建立備份時,這種形式的INSERT語句是非常有用的。在刪除一個表中的記錄之前,你可以先用這種方法把它們拷貝到另一個表中。
如果你需要拷貝整個表,你可以使用SELECT INTO語句。例如,下面的語句創建了一個名為newtable的新表,該表包含表mytable的所有數據:
 
SELECT * INTO newtable FROM mytable

你也可以指定只有特定的字段被用來創建這個新表。要做到這一點,只需在字段列表中指定你想要拷貝的字段。另外,你可以使用WHERE子句來限制拷貝到新表中的記錄。下面的例子只拷貝字段second_columnd的值等於’Copy Me!’的記錄的first_column字段。
 
SELECT first_column INTO newtable
FROM mytable
WHERE second_column=’Copy Me!’

使用SQL修改已經建立的表是很困難的。例如,如果你向一個表中添加了一個字段,沒有容易的辦法來去除它。另外,如果你不小心把一個字段的數據類型給錯了,你將沒有辦法改變它。但是,使用本節中講述的SQL語句,你可以繞過這兩個問題。
例如,假設你想從一個表中刪除一個字段。使用SELECT INTO語句,你可以創建該表的一個拷貝,但不包含要刪除的字段。這使你既刪除了該字段,又保留了不想刪除的數據。
如果你想改變一個字段的數據類型,你可以創建一個包含正確數據類型字段的新表。創建好該表後,你就可以結合使用UPDATE語句和SELECT語句,把原來表中的所有數據拷貝到新表中。通過這種方法,你既可以修改表的結構,又能保存原有的數據。

集合函數
到現在為止,你只學習瞭如何根據特定的條件從表中取出一條或多條記錄。但是,假如你想對一個表中的記錄進行數據統計。例如,如果你想統計存儲在表中的一次民意測驗的投票結果。或者你想知道一個訪問者在你的站點上平均花費了多少時間。要對錶中的任何類型的數據進行統計,都需要使用集合函數。
Microsoft SQL支持五種類型的集合函數。你可以統計記錄數目,平均值,最小值,最大值,或者求和。當你使用一個集合函數時,它只返回一個數,該數值代表這幾個統計值之一。

注意:
要在你的ASP網頁中使用集合函數的返回值,你需要給該值起一個名字。要作到這一點,你可以在SELECT語句中,在集合函數後面緊跟一個字段名,如下例所示:

SELECT AVG(vote) ‘the_average’ FROM opinion

在這個例子中,vote的平均值被命名為the_average。現在你可以在你的ASP網頁的數據庫方法中使用這個名字。

統計字段值的數目
函數COUNT()也許是最有用的集合函數。你可以用這個函數來統計一個表中有多少條記錄。這裡有一個例子:

SELECT COUNT(au_lname) FROM authors

這個例子計算表authors中名字(last name)的數目。如果相同的名字出現了不止一次,該名字將會被計算多次。如果你想知道名字為某個特定值的作者有多少個,你可以使用WHERE子句,如下例所示:

SELECT COUNT(au_lname) FROM authors WHERE au_lname=’Ringer’

這個例子返回名字為’Ringer’的作者的數目。如果這個名字在表authors中出現了兩次,則次函數的返回值是2。
假如你想知道有不同名字的作者的數目。你可以通過使用關鍵字DISTINCT來得到該數目。如下例所示:

SELECT COUNT(DISTINCT au_lname) FROM authors

如果名字’Ringer’出現了不止一次,它將只被計算一次。關鍵字DISTINCT決定了只有互不相同的值才被計算。
通常,當你使用COUNT()時,字段中的空值將被忽略。一般來說,這正是你所希望的。但是,如果你僅僅想知道表中記錄的數目,那麼你需要計算表中所有的記錄─不管它是否包含空值。下面是一個如何做到這一點的例子:

SELECT COUNT(*) FROM authors

注意函數COUNT()沒有指定任何字段。這個語句計算表中所有記錄所數目,包括有空值的記錄。因此,你不需要指定要被計算的特定字段。
函數COUNT()在很多不同情況下是有用的。例如,假設有一個表保存了對你站點的質量進行民意調查的結果。這個表有一個名為vote的字段,該字段的值要么是0,要么是1。0表示反對票,1表示贊成票。要確定贊成票的數量,你可以所有下面的SELECT語句:

SELECT COUNT(vote) FROM opinion_table WHERE vote=1

計算字段的平均值
使用函數COUNT(),你可以統計一個字段中有多少個值。但有時你需要計算這些值的平均值。使用函數AVG(),你可以返回一個字段中所有值的平均值。
假如你對你的站點進行一次較為複雜的民意調查。訪問者可以在1到10之間投票,表示他們喜歡你站點的程度。你把投票結果保存在名為vote的INT型字段中。要計算你的用戶投票的平均值,你需要使用函數AVG():

SELECT AVG(vote) FROM opinion

這個SELECT語句的返回值代表用戶對你站點的平均喜歡程度。函數AVG()只能對數值型字段使用。這個函數在計算平均值時也忽略空值。

計算字段值的和
假設你的站點被用來出售卡片,已經運行了兩個月,是該計算賺了多少錢的時候了。假設有一個名為orders的表用來記錄所有訪問者的定購信息。要計算所有定購量的總和,你可以使用函數SUM():

SELECT SUM(purchase_amount) FROM orders

函數SUM()的返回值代表字段purchase_amount中所有值的平均值。字段purchase_amount的數據類型也許是MONEY型,但你也可以對其它數值型字段使用函數SUM()。

返回最大值或最小值
再一次假設你有一個表用來保存對你的站點進行民意調查的結果。訪問者可以選擇從1到10的值來表示他們對你站點的評價。如果你想知道訪問者對你站點的最高評價,你可以使用如下的語句:

SELECT MAX(vote) FROM opinion

你也許希望有人對你的站點給予了很高的評價。通過函數MAX(),你可以知道一個數值型字段的所有值中的最大值。如果有人對你的站點投了數字10,函數MAX()將返回該值。
另一方面,假如你想知道訪問者對你站點的的最低評價,你可以使用函數MIN(),如下例所示:

SELECT MIN(vote) FROM opinion

函數MIN()返回一個字段的所有值中的最小值。如果字段是空的,函數MIN()返回空值。

其它常用的SQL表達式,函數,和過程
這一節將介紹一些其它的SQL技術。你將學習如何從表中取出數據,其某個字段的值處在一定的範圍,你還將學習如何把字段值從一種類型轉換成另一種類型,如何操作字符串和日期時間數據。最後,你將學會一個發送郵件的簡單方法。

通過匹配一定範圍的值來取出數據
假設你有一個表用來保存對你的站點進行民意調查的結果。現在你想向所有對你的站點的評價在7到10之間的訪問者發送書面的感謝信。要得到這些人的名字,你可以使用如下的SELECT語句:

SELECT username FROM opinion WHERE vote>6 and vote<11

這個SELECT語句會實現你的要求。你使用下面的SELECT語句也可以得到同樣的結果:

SELECT username FROM opinion WHERE vote BETWEEN 7 AND 10

這個SELECT語句與上一個語句是等價的。使用哪一種語句是編程風格的問題,但你會發現使用表達式BETWEEN的語句更易讀。
現在假設你只想取出對你的站點投了1或者10的訪問者的名字。要從表opinion中取出這些名字,你可以使用如下的SELECT語句:

SELECT username FROM opinion WHERE vote=1 or vote

   這個SELECT語句會返回正確的結果,沒有理由不使用它。但是,存在一種等價的方式。使用如下的SELECT可以得到相同的結果:

SELECT username FROM opinion WHERE vote IN (1,10)

注意表達式IN的使用。這個SELECT語句只取出vote的值等於括號中的值之一的記錄。
你也可以使用IN來匹配字符數據。例如,假設你只想取出Bill Gates或President Clinton的投票值。你可以使用如下的SELECT語句:

SELECT vote FROM opinion WHERE username IN (‘Bill Gates’,’President Clinton’)

最後,你可以在使用BETWEEN或IN的同時使用表達式NOT。例如,要取出那些投票值不在7到10之間的人的名字,你可以使用如下的SELECT語句:

SELECT username FROM opinion WHERE vote NOT BETWEEN 7 and 10

要選取那些某個字段的值不在一列值之中的記錄,你可以同時使用NOT和IN,如下例所示:

SELECT vote FROM opinion
             WHERE username NOT IN (‘Bill Gates’,’President Clinton’)

你不是必須在SQL語句中使用BETWEEN或IN,但是,要使你的查詢更接近自然語言,這兩個表達式是有幫助的。

轉換數據
SQL Sever足夠強大,可以在需要的時候把大部分數值從一種類型轉換為另一種類型。例如,要比較SMALLINT型和INT型數據的大小,你不需要進行顯式的類型轉換。 SQL Sever會為你完成這項工作。但是,當你想在字符型數據和其它類型的數據之間進行轉換時,你的確需要自己進行轉換操作。例如,假設你想從一個MONEY型字段中取出所有的值,並在結果後面加上字符串“US Dollars”。你需要使用函數CONVERT(),如下例所示:

SELECT CONVERT(CHAR(8),price)+’US Dollars’ FROM orders

函數CONVERT()帶有兩個變量。第一個變量指定了數據類型和長度。第二個變量指定了要進行轉換的字段。在這個例子中,字段price被轉換成長度為8個字符的CHAR型字段。字段price要被轉換成字符型,才可以在它後面連接上字符串’US Dollars’。
當向BIT型,DATETIME型,INT型,或者NUMERIC型字段添加字符串時,你需要進行同樣的轉換操作。例如,下面的語句在一個SELECT語句的查詢結果中加入字符串’The vote is’,該SELECT語句返回一個BIT型字段的值:

SELECT ‘The vote is’+CONVERT(CHAR(1),vote) FROM opinion

下面是這個語句的結果示例:

The vote is 1
The vote is 1
The vote is 0
(3 row(s) affected)

如果你不進行顯式的轉換,你會收到如下的錯誤信息:
Implicit conversion from datatype ‘varchar’ to ‘bit’ is not allowec.
Use the CONVERT function to run this query.

操作字符串數據
SQL Sever有許多函數和表達式,使你能對字符串進行有趣的操作,包括各種各樣的模式匹配和字符轉換。在這一節中,你將學習如何使用最重要的字符函數和表達式。

匹配通配符
假設你想建立一個與Yahoo功能相似的Internet目錄。你可以建立一個表用來保存一系列的站點名稱,統一資源定位器(URL),描述,和類別,並允許訪問者通過在HTML form中輸入關鍵字來檢索這些內容。
假如有一個訪問者想從這個目錄中得到其描述中包含關鍵字trading card的站點的列表。要取出正確的站點列表,你也許試圖使用這樣的查詢:

SELECT site_name FROM site_directory WHERE site_desc=’trading card’

這個查詢可以工作。但是,它只能返回那些其描述中只有trading card這個字符串的站點。例如,一個描述為We have the greatest collection of trading cards in the world!的站點不會被返回。
要把一個字符串與另一個字符串的一部分相匹配,你需要使用通配符。你使用通配符和關鍵字LIKE來實現模式匹配。下面的語句使用通配符和關鍵字LIKE重寫了上面的查詢,以返回所有正確站點的名字:
 
SELECT SITE_name FROM site_directory
                  WHERE site_desc LIKE ‘%trading cark%’

    在這個例子中,所有其描述中包含表達式trading card的站點都被返回。描述為We have the greatest collection of trading cards in the world!的站點也被返回。當然,如果一個站點的描述中包含I am trading cardboard boxes online ,該站點的名字也被返回。
注意本例中百分號的使用。百分號是通配符的例子之一。它代表0個或多個字符。通過把trading card括在百分號中,所有其中嵌有字符串trading card的字符串都被匹配。
現在,假設你的站點目錄變得太大而不能在一頁中完全顯示。你決定把目錄分成兩部分。在第一頁,你想顯示所有首字母在A到M之間的站點。在第二頁,你想顯示所有首字母在N到Z之間的站點。要得到第一頁的站點列表,你可以使用如下的SQL語句:

SELECT site_name FROM site_directory WHERE site_name LIKE ‘[AM]%’

在這個例子中使用了表達式[AM],只取出那些首字母在A到M之間的站點。中括號([])用來匹配處在指定範圍內的單個字符。要得到第二頁中顯示的站點,應使用這個語句:

SELECT site_name FROM site_directory
                  WHERE site_name LIKE ‘[N-Z]%’

在這個例子中,括號中的表達式代表任何處在N到Z之間的單個字符。
假設你的站點目錄變得更大了,你現在需要把目錄分成更多頁。如果你想顯示那些以A,B或C開頭的站點,你可以用下面的查詢來實現:
 
SELECT site_name FROM site_directory WHERE site_name LIKE ‘[ABC]%’

在這個例子中,括號中的表達式不再指定一個範圍,而是給出了一些字符。任何一個其名字以這些字符中的任一個開頭的站點都將被返回。
通過在括號內的表達式中同時包含一個範圍和一些指定的字符,你可以把這兩種方法結合起來。例如,用下面的這個查詢,你可以取出那些首字母在C到F之間,或者以字母Y開頭的站點:

SELECT site_name FROM site_directory WHERE site_name LIKE ‘[C-FY]%’

在這個例子中,名字為Collegescape和Yahoo的站點會被選取,而名字為Magicw3的站點則不會被選取。
你也可以使用脫字符(^)來排除特定的字符。例如,要得到那些名字不以Y開頭的站點,你可以使用如下的查詢:

SELECT site_name FROM site_directory WHERE site_name LIKE ‘[^Y]%’

對給定的字符或字符範圍均可以使用脫字符。
最後,通過使用下劃線字符(_),你可以匹配任何單個字符。例如,下面這個查詢返回每一個其名字的第二個字符為任何字母的站點:

SELECT site_name FROM site_directory WHERE site_name LIKE ‘M_crosoft’

這個例子既返回名為Microsoft的站點,也返回名為Macrosoft的站點。但是,名字為Moocrosoft的站點則不被返回。與通配符’%’不同,下劃線只代表單個字符。

注意:
如果你想匹配百分號或下劃線字符本身,你需要把它們括在方括號中。如果你想匹配連字符(-),應把它指定為方括號中的第一個字符。如果你想匹配方括號,應把它們也括在方括號中。例如,下面的語句返回所有其描述中包含百分號的站點:

     SELECT site_name FROM site_directory WHERE site_desc LIKE ‘%[%]%’
匹配發音
Microsoft SQL有兩個允許你按照發音來匹配字符串的函數。函數SOUNDEX()給一個字符串分配一個音標碼,函數DIFFERENCE()按照發音比較兩個字符串。當你不知道一個名字的確切拼寫,但多少知道一點它的發音時,使用這兩個函數將有助於你取出該記錄。
例如,如果你建立一個Internet目錄,你也許想增加一個選項,允許訪問者按照站點名的發音來搜索站點,而不是按名字的拼寫。考慮如下的語句:

SELECT site_name FROM site_directory
                  WHERE DIFFERENCE(site_name , ‘Microsoft’>3

這個語句使用函數DEFFERENCE()來取得其名字的發音與Microsoft非常相似的站點。函數DIFFERENCE()返回一個0到4之間的數字。如果該函數返回4,表示發音非常相近;如果該函數返回0,說明這兩個字符串的發音相差很大。
例如,上面的語句將返回站點名Microsoft和Macrosoft。這兩個名字的發音與Microsoft都很相似。如果你把上一語句中的大於3改為大於2,那麼名為Zicrosoft和Megasoft的站點也將被返回。最後,如果你只需要差別等級大於1即可,則名為Picosoft和Minisoft的站點也將被匹配。
要深入了解函數DIFFERENCE()是如何工作的,你可以用函數SOUNDEX()來返回函數DIFFERENCE()所使用的音標碼。這裡有一個例子:

SELECT site_name ‘site name’,SOUNDEX(site_name) ‘sounds like’

這個語句選取字段site_name的所有數據及其音標碼。下面是這個查詢的結果:

site name sounds like
……………………………………………………………….
Yahoo Y000
Mahoo M000
Microsoft M262
Macrosoft M262
Minisoft M521
Microshoft M262
Zicrosoft Z262
Zaposoft Z121
Millisoft M421
Nanosoft N521
Megasoft M221
Picosoft P221
(12 row(s) affected)

如果你仔細看一下音標碼,你會注意到音標碼的第一個字母與字段值的第一個字母相同。例如,Yahoo和Mahoo的音標碼只有第一個字母不同。你還可以發現Microsoft和Macrosoft的音標碼完全相同。
函數DIFFERENDE()比較兩個字符串的第一個字母和所有的輔音字母。該函數忽略任何元音字母(包括y),除非一個元音字母是一個字符串的第一個字母。
不幸的是,使用SOUNDEX()和DIFFERENCE()有一個欠缺。 WHERE子句中包含這兩個函數的查詢執行起來效果不好。因此,你應該小心使用這兩個函數。

刪除空格
有兩個函數,TTRIM()和LTRIM(),可以用來從字符串中剪掉空格。函數LTRIM()去除應該字符串前面的所有空格;函數RTRIM()去除一個字符串尾部的所有空格。這裡有一個任何使用函數RTRIM()的例子:

SELECT RTRIM(site_name) FROM site_directory

在這個例子中,如果任何一個站點的名字尾部有多餘的空格,多餘的空格將從查詢結果中刪去。
你可以嵌套使用這兩個函數,把一個字符串前後的空格同時刪去:

SELECT LTRIM(RTRIM(site_name) FROM site_directory

你會發現,在從CHAR型字段中剪掉多餘的空格時,這兩個函數非常有用。記住,如果你把一個字符串保存在CHAR型字段中,該字符串會被追加多餘的空格,以匹配該字段的長度。用這兩個函數,你可以去掉無用的空格,從而解決這個問題。

操作日期和時間
日期和時間函數對建立一個站點是非常有用的。站點的主人往往對一個表中的數據何時被更新感興趣。通過日期和時間函數,你可以在毫秒級跟踪一個表的改變。

返回當前日期和時間
通過函數GETDATE(),你可以獲得當前的日期和時間。例如,語句SELECT GETDATE()返回如下的結果:
……………………………..
     NOV 30 1997 3:29AM
(1 row(s) affected)

顯然,如果你將來使用這個函數,你得到的日期將比這個時間晚,或者梗早。
函數GETDATE()可以用來作為DATEDIME()型字段的缺省值。這對插入記錄時保存當時的時間是有用的。例如,假設有一個表用來保存你站點上的活動日誌。每當有一個訪問者訪問到你的站點時,就在表中添加一條新記錄,記下訪問者的名字,活動,和進行訪問的時間。要建立一個表,其中的記錄包含有當前的日期和時間,可以添加一個DATETIME型字段,指定其缺省值為函數GETDATE()的返回值,就像這樣:

CREATE TABLE site_log (
        username VARCHAR(40),
        useractivity VARCHAR(100),
        entrydate DATETIME DEFAULT GETDATE())

轉換日期和時間
你也許已經註意到,在上一節的例子中,函數GETDATE()的返回值在顯示時只顯示到秒。實際上,SQL Sever內部時間可以精確到毫秒級(確切地說,可以精確到3.33毫秒)。
要得到不同格式的日期和時間,你需要使用函數CONVERT()。例如,當下面的這個語句執行時,顯示的時間將包括毫秒:

SELECT CONVERT(VARCHAR(30),GETDATE(),9)

注意例子中數字9的使用。這個數字指明了在顯示日期和時間時使用哪種日期和時間格式。當這個語句執行時,將顯示如下的日期和時間:
…………………………………..
Nov 30 1997 3:29:55:170AM
(1 row(s) affected)

在函數CONVERT()中你可以使用許多種不同風格的日期和時間格式。表11.1顯示了所有的格式。

表11.1日期和時間的類型
類型值標準輸出
    0 Default mon dd yyyy hh:miAM
    1 USA mm/dd/yy
    2 ANSI yy.mm.dd
    3 British/French dd/mm/yy
    4 German dd.mm.yy
    5 Italian dd-mm-yy
    6 – dd mon yy
    7 – mon dd,yy
    8 – hh:mi:ss
9 Default + milliseconds–mon dd yyyy
hh:mi:ss:mmmAM(or )
    10 USA mm-dd-yy
    11 JAPAN yy/mm/dd
    12 ISO yymmdd
    13 Europe Default + milliseconds–dd mon yyyy
                                        hh:mi:ss:mmm(24h)
    14 – hh:mi:ss:mmm(24h)

   類型0,9,和13總是返回四位的年。對其它類型,要顯示世紀,把style值加上100。類型13和14返回24小時時鐘的時間。類型0,7,和13返回的月份用三位字符表示(用Nov代表November).

對錶11.1中所列的每一種格式,你可以把類型值加上100來顯示有世紀的年(例如,00年將顯示為2000年)。例如,要按日本標準顯示日期,包括世紀,你應使用如下的語句:

SELECT CONVERT(VARCHAR(30),GETDATE(),111)

    在這個例子中,函數CONVERT()把日期格式進行轉換,顯示為1997/11/30

抽取日期和時間
在許多情況下,你也許只想得到日期和時間的一部分,而不是完整的日期和時間。例如,假設你想列出你的站點目錄中每個站點被查詢的月份。這時你不希望完整的日期和時間把網頁弄亂。為了抽取日期的特定部分,你可以使用函數DATEPART(),像這樣:

SELECT site_name ‘Site Name’,
DATEPART(mm,site_entrydate) ‘Month Posted’ FROM site_directory

函數DATEPART()的參數是兩個變量。第一個變量指定要抽取日期的哪一部分;第二個變量是實際的數據。在這個例子中,函數DATEPART()抽取月份,因為mm代表月份。下面是這個SELECT語句的輸出結果:

Site Name Month Posted
………………………………………………………………
Yahoo 2
Microsoft 5
Magicw3 5
(3 row(s) affected)

    Month Posted列顯示了每個站點被查詢的月份。函數DATEPART()的返回值是一個整數。你可以用這個函數抽取日期的各個不同部分,如表11.2所示。

表11.2日期的各部分及其簡寫
日期部分簡寫值
year yy 1753–9999
quarter qq 1–4
month mm 1–12
day of year dy 1–366
day dd 1–31
week wk 1–53
weekday dw 1–7(Sunday–Saturday)
hour hh 0–23
minute mi 0–59
second ss 0–59
milisecond ms 0–999

當你需要進行日期和時間的比較時,使用函數DATEPART()返回整數是有用的。但是,上例中的查詢結果(2,5)不是十分易讀。要以更易讀的格式得到部分的日期和時間,你可以使用函數DATENAME(),如下例所示:
 
SELECT site_name ‘Site Name’
DATENAME(mm,site_entrydate) ‘Month Posted’
FROM site_directory

函數DATENAME()和函數DATEPART()接收同樣的參數。但是,它的返回值是一個字符串,而不是一個整數。下面是上例該用DATENAME()得到的結果:

Site Name Month Postec
………………………………………………………………….
Yahoo February
Microsoft June
Magicw3 June
(3 row(s) affected)

你也可以用函數DATENAE()來抽取一個星期中的某一天。下面的這個例子同時抽取一周中的某一天和日期中的月份:

SELECT site_name ‘Site Name’,
DATENAME(dw,site_entrydate)+ ‘-’ + DATENAME(mm,site_entrydate)
          ‘Day and Month Posted’ FORM site_directory

這個例子執行時,將返回如下的結果:

Site Name Day and Month Posted
………………………………………………………………………
Yahoo Friday – February
Microsoft Tuesday – June
Magicw3 Monday – June
(3 row(s) affected)

返回日期和時間範圍
當你分析表中的數據時,你也許希望取出某個特定時間的數據。你也許對特定的某一天中――比如說2000年12月25日――訪問者在你站點上的活動感興趣。要取出這種類型的數據,你也許會試圖使用這樣的SELECT語句:

SELECT * FROM weblog WHERE entrydate=”12/25/20000”

不要這樣做。這個SELECT語句不會返回正確的記錄――它將只返回日期和時間是12/25/2000 12:00:00:000AM的記錄。換句話說,只有剛好在午夜零點輸入的記錄才被返回。

注意:
在本節的討論中,假設字段entrydate是DATETIME型,而不是SMALLDATETIME型。本節的討論對SMALLDATETIME型字段也是適用的,不過SMALLDATETIME型字段只能精確到秒。

問題是SQL Sever將用完整的日期和時間代替部分日期和時間。例如,當你輸入一個日期,但不輸入時間時,SQL Sever將加上缺省的時間“12:00:00:000AM”。當你輸入一個時間,但不輸入日期時,SQL Sever將加上缺省的日期“Jan 1 1900”。
要返回正確的記錄,你需要適用日期和時間範圍。有不止一種途徑可以做到這一點。例如,下面的這個SELECT語句將能返回正確的記錄:

SELECT * FROM weblog
WHERE entrydate>=”12/25/2000” AND entrydate<”12/26/2000”

這個語句可以完成任務,因為它選取的是表中的日期和時間大於等於12/25/2000 12:00:00:000AM並小於12/26/2000 12:00:00:000AM的記錄。換句話說,它將正確地返回2000年聖誕節這一天輸入的每一條記錄。
另一種方法是,你可以使用LIKE來返回正確的記錄。通過在日期表達式中包含通配符“%”,你可以匹配一個特定日期的所有時間。這裡有一個例子:

SELECT * FROM weblog WHERE entrydate LIKE ‘Dec 25 2000%’

這個語句可以匹配正確的記錄。因為通配符“%”代表了任何時間。
使用這兩種匹配日期和時間範圍的函數,你可以選擇某個月,某一天,某一年,某個小時,某一分鐘,某一秒,甚至某一毫秒內輸入的記錄。但是,如果你使用LIKE來匹配秒或毫秒,你首先需要使用函數CONVERT()把日期和時間轉換為更精確的格式(參見前面“轉換日期和時間”一節)。

比較日期和時間
最後,還有兩個日期和時間函數對根據日期和時間取出記錄是有用的。使用函數DATEADD()和DATEDIFF(),你可以比較日期的早晚。例如,下面的SELECT語句將顯示表中的每一條記錄已經輸入了多少個小時:

SELECT entrydate ‘Time Entered’
DATEDIFF(hh,entrydate,GETDATE()) ‘Hours Ago’ FROM weblog

如果當前時間是2000年11月30號下午6點15分,則會返回如下的結果:

Time Entered Hours Ago
…………………………………………………..
Dec 30 2000 4:09PM 2
Dec 30 2000 4:13PM 2
Dec 1 2000 4:09PM 698
(3 row(s) affected)

函數DADEDIFF()的參數是三個變量。第個變量指定日期的某一部分。在這個例子中,是按小時對日期進行比較,(要了解日期各部分的詳細內容,請參考表11.2)在日期2000年11月1日和2000年11月30日的指定時間之間有689個小時。另外兩個參數是要進行比較的時間。為了返回一個正數,較早的時間應該先給。
函數DATEADD()把兩個日期相加。當你需要計算截止日期這一類的數據時,這個函數是有用處的。例如,假設訪問者必須先註冊才能使用你的站點。註冊以後,他們可以免費使用你的站點一個月。要確定什麼時候他們的免費時間會用完,你可以使用如下的SELECT語句:

SELECT username ‘User Name’,
DATEADD(mm,1,firstvisit_date) ‘Registration Expires’
FROM registration_table

函數DATEADD()的參數有三個變量。第一個變量代表日期的某一部分(參見表11.2),這個例子用到了代表月份的mm。第二個變量指定了時間的間隔――在本例中是一個月。最後一個變量是一個日期,在這個例子中,日期是取自DATETIME型字段firstvisit_date.假設當前日期是June 30,2000,這個語句將返回如下的內容:

User Name Registration Expires
……………………………………………………………………………
Bill Gates Jul 30 2000 4:09PM
President Clinton Jul 30 2000 4:13PM
William Shakespeare Jul 1 2000 4:09PM
(3 row(s) affected)

注意:
與你預料的相反,使用函數DATEADD()把一個日期加上一個月,它並不加上30天。這個函數只簡單地把月份值加1。這意味著在11月註冊的人將比在2月註冊的人多得到2天或3天的時間。要避免這個問題,你可以用函數DATEADD()直接增加天數,而不是月份。

發送郵件
你可以用SQL Sever發送簡單的e_mail信息。要做到這一點,你需要在你的系統中安裝郵件服務器,如Microsoft Exchange Sever(參見第四章“Exchange Active Sever,Index Sever,和NetShow”)。你還需要配置SQL Sever以識別郵件服務器。
要讓SQL Sever能識別郵件服務器,啟動事務管理器並從菜單中選擇Sever|SQL Mail|Configue,這時會出現一個如圖11.3所示的對話框。輸入你在郵件服務器中註冊的用戶名和口令,然後單擊OK。

注意:
如果你使用Microsoft Exchange Sever,配置SQL Sever的過程將會大大不同。你需要在同一個(域)用戶帳號下運行Microsoft SQL Sever和Exchange Sever。你還需要在安裝了SQL Sever的機器上安裝Exchange Cliect並給這個帳號創建一個一個配置文件。完成這些之後,你就可以在SQL Mail Configuration對話框中輸入該配置文件的名字。
 
圖11。3

在發送郵件之前,你要先啟動SQL Mail。從菜單中選擇Sever|SQL Mail|Start。如果你的郵件服務器配置正確,並且你輸入了正確的用戶名和口令,則SQL Mail會成功啟動。

注意:
你可以把SQL Sever配置為自動啟動郵件服務。要做到這一點,在Set Sever Optons對話框(從菜單中選擇Sever|SQL Sever|Configure)中選擇Auto Start Mail Client即可。

要發送一個郵件,你可以使用名為xp_sendmail的擴展存儲過程。這裡有一個如何使用這個過程的例子:

master..xp_sendmail “president@whitehouse.gov”,”Hello Mr. President”

這個過程調用向e_mail地址president@whitehouse.gov發送一個簡單的email信息:“Hello Mr. President”。你可以用任何其它的email地址和信息取代上例中相應的內容,但是,你所發送的信息不能超過255個字符長度。
    當你想隨時了解你的站點數據庫的狀態時,存儲過程xp_sendmail是有用處的。例如,你可以向一個頁面管理程序發送信息。如果你的站點出了什麼問題,你馬上就可以知道。下一章將講述更多有關存儲過程的內容。

總結
這一章加深了你的SQL知識。你學會瞭如何建立索引,使你的查詢速度更快。你還學會瞭如何插入,刪除和更新一個表中的數據,如何使用集合函數得到一個表中數據的統計信息。最後,你學會了許多有價值的表達式,函數和過程,用來操作字符串,日期和時間及郵件。
下一章將進一步加深你對Microsoft SQL Sever的掌握。你將學習如何用SQL來進行程序設計,如何建立存儲過程,觸發器和執行計劃。更另人興奮的是,你將學會讓SQL Sever自動創建網頁的一個簡單方法。

This entry was posted in News.

发表评论

邮箱地址不会被公开。 必填项已用*标注


*

在线客服系统