14

14. 使用 T-SQL 檢索資料

SELECT 陳述式

使用 T-SQL 函數

SELECT 的其他用法

本章總結

在本章中，您將學習如何使用 Transcat-SQL（T-SQL）的 SELECT 陳述式來檢索資料，本章同時涵蓋許多在 SELECT 陳述式中使用的選擇性子句、搜尋條件和函數。這些元素在您提出查詢時有助於您找到真正需要的資料。

SELECT 陳述式

儘管 SELECT 陳述式主要用於檢索特定的資料，它同時也可以用來分配值給本域變數或呼叫函數，這部分內容會在本章最後的

〈SELECT 的其他用途〉 一節介紹。SELECT 陳述式可以很簡單，也可以很複雜－當然能不複雜最好。當您還要再繼續檢索結果時，儘量使您的 SELECT 陳述式簡單化。例如，如果您只需要一個資料表中的兩個資料行的資料，那麼只須把這兩個資料行包括在 SELECT 陳述式中，以減少傳回的資料量。

當您決定哪些資料要從哪些資料表回傳後，您可以加入其他任何有必要的選項。這些選項包括：使用索引時，指定 WHERE 子句中要包括哪些資料行、指定傳回的資料是否需要排序、指定是否只需要回傳不同的資料。關於查詢最佳化的相關資訊，請參閱 第35章 。

讓我們從檢視 SELECT 陳述式中的不同選項和每個子句的檢索範例講起。本章範例中使用的資料庫是 pubs 和 Northwind，這兩個資料庫在您安裝 Microsoft SQL Server 2000 時即已自動建立。您可以使用 SQL Server Enterprise Manager 來檢視 pubs 和 Northwind 資料庫的資料表，以熟悉這兩個資料庫。

SELECT 陳述式的語法由幾個選擇性的子句組成。在大多數的情況下，一個SELECT 陳述式至少包括一條 SELECT 子句和 FROM 子句。這兩條子句分別判斷哪一個資料行或哪幾個資料行的資料需要檢索，以及從哪個資料表中檢索資料。例如，對於 pubs 資料庫的 authors 資料表，一條簡單的 SELECT 陳述式如下：

    SELECT  au_fname, au_lname
    FROM    authors

如果您使用 OSQL 命令資料列 ( 第13章 中介紹的 )，不要忘記使用 GO 命令執行陳述式。要使用 OSQL，SELECT 陳述式的完整的 T-SQL 語法如下：

    USE     pubs
    SELECT  au_fname, au_lname
    FROM    authors
    GO

說明

由於關鍵字並不分大小寫，您可以隨意使用，但最好儘量保持一致，以便您的語法便於閱讀。因此，本書中關鍵字均採用大寫字母。

當您互動地使用 SELECT 陳述式時 (例如使用 OSQL 或 SQL Server 查詢 Analyzer )，結果會顯示在各資料行中，同時每一個資料行都以標題指明。 (有關 T-SQL 簡介、OSQL 和查詢 Analyzer 的介紹，請參考 第13章 )

SELECT 子句

SELECT 子句由選擇性的引數和您所要求的選取清單組成。 選取清單 （select list）是一個包含 運算式 （expression）或資料行的清單，用以在 SELECT 子句中指明哪些資料行需要回傳。下面介紹選擇性的引數和選取清單。

引數

SELECT 子句使用下列兩個引數控制傳回的資料資料列：

- DISTINCT 只傳回唯一的資料列。如果選擇的清單包括數個資料行，當至少有一資料行的資料值不同時，資料列會被視為是唯一的。如果有兩資料列資料相同，它們必定在每一資料行中的值都相同。
- TOP n [PERCENT] 傳回結果集合的前 n 資料列。如果指定了PERCENT，那麼將只傳回前 n 個百分比的資料列數。當使用 PERCENT 時，n 必須是介於 0 到 100 之間的數。如果查詢中包括一條 ORDER BY 子句 (ORDER BY 子句將在本章後面 〈ORDER BY子句〉 一節中細述)，資料列會先按順序排資料列，然後從排好的結果中傳回前 n 資料列或前百分之 n 資料列。

下面的 T-SQL 語法顯示了 SELECT 子句範例，它執行了三次，每次使用不同的引數。第一次查詢時使用 DISTINCT 引數，第二次使用 TOP 50 PERCENT 引數，第三次使用 TOP 5 引數。

    SELECT  DISTINCT au_fname, au_lname
    FROM    authors
    GO

    SELECT  TOP 50 PERCENT au_fname, au_lname
    FROM    authors
    GO

    SELECT  TOP 5 au_fname, au_lname
    FROM    authors
    GO

第一次查詢傳回 23 資料列，每一資料列均為唯一的。第二次傳回 12 資料列 (大約為 50%，無條件進入 )，第三次傳回 5 資料列。

選取清單

如上所述，選取清單是關於運算式或資料行的清單，用於在 SELECT 子句中指定哪些資料行的資料需要回傳。運算式可以是資料行名稱、函數或常數的清單。選取清單可以包括好幾個運算式或資料行名稱，彼此之間用逗號隔開。上面的例子使用了以下的選取清單：

    au_fname, au_lname

星號或萬用符號 您可以使用星號 (* )或萬用符號在選取清單中指定需要傳回FROM 子句中指定資料表和檢視表的所有資料和資料行。例如，傳回 sales 資料表的所有資料行的資料列，可以使用下列查詢：

    SELECT  *
    FROM    sales
    GO

本章後面 〈交叉聯結〉 一節將講述當 SELECT * 陳述式的 FROM 子句資料列出了不只一個資料表的情形。

IDENTITYCOL和ROWGUIDCOL 要從資料表中的識別資料行（Identity Column）中檢索數值，可以簡單地在選取清單中使用 IDENTITYCOL 運算式。下面是查詢 Northwind 資料庫的範例，其中 Employess 資料表中定義了識別資料行：

    USE     Northwind
    GO
    SELECT  IDENTITYCOL
    FROM    Employees
    GO

結果集將如下面所示：

    EmployeeID
    ----------
    3
    4
    8
    1
    2
    6
    7
    5
    9
    (影響9個資料列)

請您注意結果集中的資料行標題和資料庫中具有 IDENTITY 屬性的資料行的名稱符合，在本例中是 EmployeeID。

同樣的，您可以在選取清單中使用 ROWGUIDCOL 運算式檢索資料列的通用唯一識別碼（GUID）資料行，即具有 ROWGUIDCOL 屬性的資料行。必須是 uniqueidentifier 資料型別的資料行才有 ROWGUIDCOL 屬性。

資料行別名 使用資料行別名可以使結果集中顯示的資料行標題換成您希望的樣子。使用別名可讓輸出資料行中的資料意義更清楚，可指派標題給函數中使用的資料行，亦可在 ORDER BY 子句中引用這些別名。

當在多個不同的資料表中擁有同名的資料行，為了易於分辨起見，您可能需要在輸出的資料行標題中包含資料表的名稱。舉一個使用別名的例子。讓我們看看 pubs 資料庫中的 employee 資料表中的lname資料行。如果您執行以下的查詢：

    USE     pubs
    GO
    SELECT  lname
    FROM    employee
    GO

將會得到如下的結果：

    lname
    --------
    Cruz
    Roulet
    Devon
    ...
    O'Rourke
    Ashworth
    Latimer

    (影響43個資料列)

要使顯示結果中的資料行標題 lname 換成 「 Employee Last Name」，以強調lname的實質意義，可利用 AS 關鍵字：

    SELECT  lname AS "Employee Last Name"
    FROM    employee
    GO

此命令顯示的結果為：

    Employee Last Name
    ------------------
    Cruz
    Roulet
    Devon
    ...
    O'Rourke
    Ashworth
    Latimer

    (影響43個資料列)

您也可以在選取清單中使用其他類型的運算式和資料行別名，並作為 ORDER BY 子句的引用資料行。假設在選取清單中有一個函數被呼叫，您可以使用 AS 關鍵字來指定一個描述此函數輸出的資料行別名。如果函數不用資料行別名，則根本就沒有資料行標題。例如，下面的陳述式為 MAX 函數的輸出指定了標題 Maximum Job ID：

    SELECT  MAX (job_id ) AS "Maximum Job ID"
    FROM    employee
    GO

此別名上有引號，是因為它由多個字組成，而且之中有空格。如果別名中不包含空格，就可以不用引號。

您可以引用在 SELECT 子句中指定的資料行別名，作為 ORDER BY 子句的引數。當選取清單中包含一個運算結果需要排序的函數時，這個技巧會十分有用。例如，以下的命令將檢索每一間書店的銷售量，並且將輸出結果依量的大小排序。在選取清單中指派的別名將被應用在 ORDER BY 子句。

    SELECT   SUM (qty ) AS Quantity_of_Books, stor_id
    FROM     sales
    GROUP BY stor_id
    ORDER BY Quantity_of_Books
    GO

本例中，別名不含空格，因此我們不需為別名加上引號。

如果我們並未替查詢的 SUM (qty )資料行指定別名，下面的例子將輸出同樣的結果，但 sum 資料行則沒有標題：

    SELECT   SUM (qty ), stor_id FROM sales
    GROUP BY stor_id
    ORDER BY SUM (qty )
    GO

記住，資料行別名可讓您分派標題給輸出資料行，但是僅止於名稱的改變；無論它們為何都不會影響查詢的結果。

FROM 子句

FROM 子句可內含資料表的名稱和用來選擇資料的檢視表。每個 SELECT 子句都會要求使用 FROM 子句，除非是選取清單內不含資料行名稱，而且只有常數、變數、數學運算式。您已經看到一些使用 FROM 子句的例子，不過 FROM 子句也可以內含衍生的資料表、聯結（Join）和別名。

衍生資料表

衍生資料表 （derived table）是在 FROM 子句中內含另一個 SELECT 子句的結果集。被內含的 SELECT 子句的結果集被視為一個資料表，在該資料表以外的 SELECT 子句可以選擇其資料。下面的查詢使用一個衍生資料表尋找商店名稱，此商店提供至少一種可能的折扣方式。

    USE     pubs
    GO
    SELECT  s.stor_name
    FROM    stores AS s,(SELECT stor_id, COUNT (DISTINCT discounttype )
                            AS d_count
                            FROM discounts
                            GROUP BY stor_id ) AS d
    WHERE   s.stor_id = d.stor_id AND
            d.d_count >= 1
    GO

如果執行這個命令，會看到某一資料列被選取，表示資料庫中僅有一家商店─Bookbeat－提供至少一種折扣。

注意此查詢使用速記法來代表資料表名稱（s代表stores資料表；d代表discounts資料表）。這種速記法稱作 資料表別名 （Table Aliases），本章後面的 〈資料表別名〉 部分將會討論這點。

說明

在 WHERE 子句中不能有衍生資料表。WHERE 子句中的 SELECT 陳述式是當成搜尋條件。本章後面的 〈WHERE子句及搜尋條件〉 部分將詳細介紹這點。

聯結資料表

聯結資料表 （join table）是兩個更多資料表聯結操作後產生的結果集。資料表之間可以執行的聯結方式有：內部聯結、完全外部聯結、左外部聯結、右外部聯結及交叉聯結等。下面簡述了每一種聯結。

內部聯結 內部聯結（inner join）為預設的聯結形式；它指定只有符合 ON 條件資料表中的資料列才能被包含在結果集中，而不符合的資料列都被排除在外。要指定一個聯結，可以使用 JOIN 關鍵字。使用 ON 關鍵字是用來定義聯結基本的搜尋條件。下面的查詢聯結了 stores 和 discounts 兩個資料表，以顯示哪些商店提供了折扣以及折扣的種類（預設為內部聯結，表示只有符合 ON 搜尋條件的資料列會被傳回）。

    SELECT  s.stor_id, d.discounttype
    FROM    stores s JOIN discounts d
    ON      s.stor_id = d.stor_id
    GO

結果集如下：

    stor_id  discounttype
    -------  -------------------
    8042     Customer Discount

您可以看到，只有一家商店提供折扣，而且只有一種折扣。在傳回的唯一資料資料列裡，它從 stores 資料表中得到的 stor_id 與從 discounts 資料表中得到的 sotr_id 相符合，所以此 stor_id 及它的 discounttype 被傳回。

完全外部聯結 完全外部聯結（full outer join）指定了不論是符合的資料列（滿足 ON 搜尋條件的資料列）或不符合的資料列（不滿足 ON 條件的資料列）都被包含在結果集中。對於不符合的資料列，NULL 將顯示在不符合的資料行中。在本例中，NULL 即表示商店不提供任何折扣，這樣它在 stores 資料表中有 sotr_id 值，但在 discounts 資料表中則沒有值。NULL 也代表在 discounts 資料表中此種形式的折扣任何商店都不提供。下面的查詢用了與前面內部聯結相同的查詢，但這次我們要指定完全外部聯結：

    SELECT  s.stor_id, d.discounttype
    FROM    stores s FULL OUTER JOIN discounts d
    ON      s.stor_id = d.stor_id
    GO

結果集如下：

    stor_id  discounttype
    -------  ------------------
    NULL     Initial Customer
    NULL     Volume Discount
    6380     NULL
    7066     NULL
    7067     NULL
    7131     NULL
    7896     NULL
    8042     Customer Discount

結果集顯示只有一資料列完全符合。其他資料列則會在某一欄中具有 NULL 值。

左外部聯結 左外部聯結（left outer join）傳回符合的資料列及指定於 JOIN 關鍵字左邊之資料表的所有資料列。使用與上述相同的查詢，在此我們指定左外部聯結，如下所示：

    SELECT  s.stor_id, d.discounttype
    FROM    stores s LEFT OUTER JOIN discounts d
    ON      s.stor_id = d.stor_id
    GO

結果集如下：

    stor_id discounttype
    ------- ----------------------------------------
    6380    NULL
    7066    NULL
    7067    NULL
    7131    NULL
    7896    NULL
    8042    Customer Discount

結果集中包含符合 ON 條件的一資料列和 stores 資料表中的其他資料列，這些資料列在 discounts 資料表中並沒有與之符合的 stor_id（這些資料列的 discounttype 欄為 NULL）。

右外部聯結 右外部聯結（right outer join）與左外部聯結相反，它傳回符合的資料列及指定於 JOIN 關鍵字右邊的資料表中所有的資料列。下面使用同一個查詢說明右外部聯結：

    SELECT  s.stor_id, d.discounttype
    FROM    stores s RIGHT OUTER JOIN discounts d
    ON      s.stor_id = d.stor_id
    GO

結果集如下：

    stor_id  discounttype
    -------  -------------------
    NULL     Initial Customer
    NULL     Volume Discount
    8042     Customer Discount

結果集顯示了符合 ON 條件的資料列和在 discounts 資料表中的其他資料列，這些資料列在 stores 資料表中沒有與之符合的 stor_id（其 stor_id 欄為 NULL）。

交叉聯結 交叉聯結（cross join）是沒有指定 WHERE 子句時，兩個資料表的交叉產物。如果存在 WHERE 子句，則交叉聯結就與內部聯結類似。當沒有 WHERE 子句時，所有的欄和資料列將從這兩個資料表中以下列方式傳回：第一個資料表的每一資料列與第二個資料表的每一資料列相符，所以結果集的數目為第一個資料表的資料列數乘以第二個資料表的資料列數。

為了理解交叉聯結，下面將舉幾個新的例子。先讓我們看一個沒有 WHERE 子句的交叉聯結的情況，然後舉三個包含 WHERE 子句的交叉聯結的例子。下面的查詢是一個簡單的範例。執行這三個查詢，並且注意每個查詢輸出的資料列數。

    SELECT  *
    FROM    stores
    GO

    SELECT  *
    FROM    sales
    GO

    SELECT  *
    FROM    stores CROSS JOIN sales
    GO

說明

如果 FROM 子句中包含兩個資料表，其作用相當於指定了一個 CROSS JOIN，例如：

    SELECT  *
    FROM    stores, sales
    GO

要避免出現大量雜亂的資訊（如果遠超過了我們需要的部分），便需要加一個WHERE 子句來縮小查詢的範圍，如下所述：

    SELECT  *
    FROM    sales CROSS JOIN stores
    WHERE   sales.stor_id = stores.stor_id
    GO

該陳述式只傳回符合 WHERE 子句中搜尋條件的資料列，將結果縮減到只有 21資料列。WHERE 子句使交叉聯結與內部聯結的作用相同（即只有滿足搜尋條件的資料列被傳回）。該查詢將傳回 sales 資料表中的資料資料列與 stores 資料表中有相同 stor_id 的資料列，這些資料表將集合在一起。不符合的資料列不會被傳回。

為了進一步縮減結果集，可以在星號（*）前面加上資料表名稱，指定將選擇該資料表的所有資料列和資料行，如下面的例子所示。您也可以透過在資料表名稱和資料行名稱之間插入一點（.）來選定資料表的某一資料行。

    SELECT  sales.*, stores.city
    FROM    sales CROSS JOIN stores
    WHERE   sales.stor_id = stores.stor_id
    GO

這個查詢傳回 sales 資料表中的所有資料行，並附加上 stores 資料表資料列中有同樣 stor_id 的 city 資料行。實際上，結果集包含 sales 資料表中符合 stor_id 的資料列以及所附加的商店所在的城市。

以下是不含星號（*）的同一個查詢，sales 資料表中僅有 stor_id 資料行被選取。

    SELECT  sales.stor_id, stores.city
    FROM    sales CROSS JOIN stores
    WHERE   sales.stor_id = stores.stor_id
    GO

資料表別名

我們已經看過了幾個使用資料表別名的例子。AS 關鍵字是選擇性的（FROM tablename AS alias 的結果與 FROM tablename alias 是相同的）。讓我們再看一下

〈右外部聯結〉 部分的查詢，這個查詢用到了別名：

    SELECT  s.stor_id, d.discounttype
    FROM    stores s RIGHT OUTER JOIN discounts d
    ON      s.stor_id = d.stor_id
    GO

這兩個資料表中都含有 stor_id 資料行。要區分在查詢中所引用的是哪一個資料表中的 stor_id 資料行，必須提供資料表名稱或在資料表別名後插入一點（.）再加上資料行名稱。在本例中，s 代表 stores 資料表，d代表 discounts 資料表。在指定某一欄時，s或d加在資料行名稱前以表示此資料行屬於哪一個資料表。包含 AS 關鍵字的同一個查詢如下：

    SELECT  s.stor_id, d.discounttype
    FROM    stores AS s RIGHT OUTER JOIN discounts AS d
    ON      s.stor_id = d.stor_id
    GO

INTO 子句

現在進入 SELECT 陳述式的第一個真正的選擇性子句：INTO 子句。利用 SELECT<select list> INTO<new_tablename > 語法可以從一個或多個資料表中檢索資料並將結果資料列輸出到一個新的資料表。當執行 SELECT...INTO 陳述式時，新資料表會自動產生並按照選取清單中的資料行定義。新資料表中的每一個資料行與來源資料行有相同的資料型別，並且和選取清單中的資料行名稱相同。使用者必須在目標資料庫中有 CREAT TABLE 權限，才能執行 SELECT...INTO。在 第30章 中將介紹如何設定權限。

您可以使用 SELECT...INTO 將結果存入暫存性或永久性的資料表中。對於一個本域暫存資料表（只有目前的連線或使用者能看見），必須在資料表明前加上 ＃ 號。對於一個全域暫存資料表（對任何使用者都能看見），必須在資料表前加上兩個 ＃ 號（＃＃）。當所有正在使用同一個暫存資料表的使用者都與 SQL 伺服器斷線時，該暫存資料表將會自動被刪除。要選擇存入永久性資料表，您無需為資料表名稱加前置字，但是目標資料庫的 Select Into/Bulk Copy 選項必須選取。要為 pubs 資料庫選取這個選項，可以執行以下 SQL 查詢：

    sp_dboption pubs, "select into/bulkcopy", true
    GO

您也可以用 SQL Server Enterprise Manager 來選擇這個選項，如下所示：

1. 在 Enterprise Manager 視窗中點選 pubs 資料庫並且按右鈕，出現快顯功能表後選擇 內容 ，可顯示 pubs 資料庫的屬性，如圖14-1（這個畫面曾在 第九章 出現過，當時我們建立了一個資料庫並指定其檔案成長選項）。
2. 按一下 選項 標籤頁，如圖14-2，並在 模型 的下拉式選單中選擇 大量登入 。其他選項保持原先的設定。按一下 確定 。

下面的查詢使用 SELECT...INTO 建立一個名為 emp_info 的永久資料表，其中包含所有員工的姓名，以及它們的職務說明（在 pubs 資料庫內）：

    SELECT  employee.fname, employee.lname, jobs.job_desc
    INTO    emp_info
    FROM    employee, jobs
    WHERE   employee.job_id = jobs.job_id
    GO

圖14-1 資料庫屬性視窗的「一般」標籤頁

圖14-2 資料庫屬性視窗的「選項」標籤頁

emp_info資料表包含了三個資料行名稱－fname、lname以及 job_desc，其資料型別與原資料表（employee與 jobs資料表）的定義的資料型別相同。若要使新資料表成為一個本域暫存資料表，必須在資料表名稱前加上 ＃ 符號，如 #emp_info；若要成為全域暫存資料表，則使用 ＃＃ 符號，如 ##emp_info。

WHERE 子句及搜尋條件

WHERE 子句依據搜尋條件來限定查詢所傳回的資料列。本節中我們將說月許多可用在搜尋條件的操作。

說明

搜尋條件不只可用在 SELECT 陳述式中的 WHERE 子句，也可用於 UPDATE 以及 DELETE 陳述式。（UPDATE 與 DELETE 陳述式將在

20章 討論）

首先讓我們先複習一些術語。搜尋條件可以透過結合邏輯運算子 AND、OR 和NOT 而包含無限多個述詞。 述詞 （predicate）是指傳回 TRUE、FALSE 和 UNKNOWN 值的運算式。一個 運算式 （expression）可以是資料行名稱、常數、純量函數（傳回一個值的函數）、變數、純量子查詢（傳回一個資料行的子查詢）或是透過運算子結合這些元素所形成的總體。本節中，述詞或運算式均以 運算式 這個術語來表達。

比較運算子

運算式中可使用的等式與不等式運算子資料列於表14-1。

表14-1 比較運算子

運算子 | 檢驗的條件
--- | ---
= | 檢驗兩個運算式是否相等
<> | 檢驗兩個運算式是否不等
!= | 檢驗兩個運算式是否不等（同<>）
> | 檢驗前一個運算式是否大於後一個運算式
>= | 檢驗前一個運算式是否大於或等於後一個運算式
!> | 檢驗前一個運算式是否不大於後一個運算式
< | 檢驗前一個運算式是否小於後一個運算式
<= | 檢驗前一個運算式是否小於或等於後一個運算式
!< | 檢驗前一個運算式是否不小於後一個運算式

一個簡單的 WHERE 子句可能利用等於運算子（＝）來比較兩個運算式。例如，下面的 SELECT 陳述式可用來檢驗每資料列 lname 資料行中資料型別為 char 的值，如果為 Latimer，則傳回 TRUE（傳回 TRUE 的資料列將被包含在結果集）。

    SELECT  *
    FROM    employee
    WHERE   lname = "Latimer"
    GO

本例的查詢將傳回一資料列。Latimer必須加上引號，因為它是字串。

說明

SQL Server 預設接受單引號（'）與雙引號（"），例如 'Latimer'或 "Latimer"。本書的範例只使用雙引號來避免混淆。您可以使用 SET QUOTED_IDENTIFIER 選項來允許保留關鍵字當作物件名稱，並只使用單引號。將該選項設定為 ON（OFF為預設值）。

下面的查詢使用不等於運算子（<>），資料行為 integer 資料型別的 job_id。

    SELECT  job_desc
    FROM    jobs
    WHERE   job_id <> 1
    GO

這個查詢將傳回在jobs 資料表中 job_id 不等於1的資料列中的說明文字。本例中有13資料列被傳回。如果某資料列中有 NULL 值，它不等於1也不等於其他值，故此資料列也將被傳回。

邏輯運算子

邏輯運算子 AND 和 OR 測試兩個運算式，並根據結果傳回一個 TRUE、FALSE 或 UNKNOWN 的布林值。NOT 邏輯運算子會傳回與運算式相反的結果。圖14-3中資料列出了 AND、OR 與 NOT 運算子傳回的每種可能值。AND 與 OR 表格中，左邊的資料行是第一個運算式的值，最上面的資料列是第二個運算式的值，兩者交叉的表格中即是結果的布林值。NOT 表格則更直接一些。UNKNOWN 是運算式中有 NULL 值所傳回的結果。

圖14-3 AND、OR 與 NOT 的運算結果

下面的查詢是在 WHERE 子句中對兩個運算式運用 AND 邏輯運算子例子：

    SELECT  job_desc, min_lvl,max_lvl
    FROM    jobs
    WHERE   min_lvl >= 100 AND
            max_lvl <= 225
    GO

如圖14-3，要讓 AND 運算傳回 TRUE，必須兩個條件均為 TRUE。在這個查詢中，有 4 資料列被傳回。

下一個查詢中，一個 OR 運算將檢測出版商是否位於華盛頓 D.C.或位於麻州。若兩個檢驗中只要有一個值為 TRUE，則此資料列將被傳回。

    SELECT  p.pub_name, p.state, t.title
    FROM    publishers p, titles t
    WHERE   p.state = "DC" OR
            p.state = "MA" AND
            t.pub_id = p.pub_id
    GO

此查詢傳回 23 資料列。

NOT 運算單純的傳回其後布林運算式的相反值。例如，要查詢所有的作者版稅不少於 20% 的書籍名稱，您可以下列方法來運用 NOT 運算子：

    SELECT  t.title, r.royalty
    FROM    titles t, roysched r
    WHERE   t.title_id = r.title_id AND NOT
            r.royalty < 20
    GO

其他關鍵字

除了以上這些運算子，還有多種 T-SQL 的關鍵字可用於搜尋條件。本節將解釋最常用的一些關鍵字，也會給予範例說明。

LIKE LIKE 關鍵字在搜尋條件中指定模式比對。 模式比對 （Pattern matching）是在一個比對運算式與搜尋條件中指定的模式之間進行的比對檢測，語法如下：

    <match_EXPRESSION> LIKE <pattern>

若比對運算式符合模式，則傳回 TRUE 的布林值。反之則傳回 FALSE。對比運算是必須是字串資料型別。如果不是的話，SQL Server 會儘可能將其自動轉換為字串資料型別。

模式是真正的字串運算式。 字串運算式 定義為一個由字元或萬用字元組成的字串。 萬用字元 是在字串運算式中代表特殊意義的字元。表14-2資料列出了模式中可用的萬用字元：

表14-2 T-SQL的萬用字元

萬用字元 | 說明
--- | ---
% | 百分比符號；與零個或若干個字元比對
_ | 底線；與任何一個單一字元比對
[ ] | 範圍萬用字元，與指定範圍或集合中的單一字元比對，例如 [m-p] 或 [mnop] 表示m、n、o、p中的任一字元。
[^] | 不在範圍內萬用字元；與不包括在該範圍或集合中的任一個字元比對，例如 [^m-p] 或 [^mnop] 表示除了m、n、o、p以外的其他任一字元。

要進一步了解 LIKE 關鍵字與萬用字元，請看面幾個例子。若要在 authors資料表中尋找以 S 為開頭的作者姓氏，您可以使用以下含有 % 萬用字元的查詢：

    SELECT  au_lname
    FROM    authors
    WHERE   au_lname LIKE "S%"
    GO

結果將為：

    au_lname
    -----
    Smith
    Straight
    Stringer

這個查詢中，S% 表示傳回以 S 開頭，後接任何字元的姓氏。

說明

本範例假設您使用的是預設的按字典順序排序，且不區分大小寫。若指定其他的排序方法，您的結果可能不同，但使用 LIKE 關鍵字的原理是相同的。

要搜尋 ID 以 724 開頭的作者資訊，若已知每個 ID 的格式就像社會安全號碼（3個數字－2個數字－4個數字），您可使用底線（_）萬用字元，如下所示：

    SELECT  *
    FROM    authors
    WHERE   au_id LIKE "724-_ _-_ _ _ _"
    GO

結果集將包含兩資料列，au_id 值為 724-08-9931 與 724-80-9391。

現在讓我們看一個使用 [ ] 萬用字元的例子。要搜尋開頭為 A 到 M 之間的任一字母的作者姓氏，您可以組合 [ ] 及 % 萬用字元：

    SELECT  au_lname
    FROM    authors
    WHERE   au_lname LIKE "[A-M]%"
    GO

結果集將包含有以 A-M 開頭的名字的 14 資料列。(如果使用區分大小寫的排序，則得到 13 資料列。 )

如果用 [^] 萬用字元取代 [ ] 萬用字元執行類似的查詢，將得到除了 A 到 M以外字母為開頭的姓氏，如下所示：

    SELECT  au_lname
    FROM    authors
    WHERE   au_lname LIKE "[^A-M]%"
    GO

此查詢傳回 9 資料列。

如果使用區分大小寫的排序，但要得到符合條件但不區分大小寫的所有名稱，可以使用一個檢驗第一個字母為小寫或大寫的查詢，如下所示：

    SELECT  au_lname
    FROM    authors
    WHERE   au_lname LIKE "[A-M]%" OR
            au_lname LIKE "[a-m]%"
    GO

結果集包含名稱 del Castillo，如果僅用區分大小寫的查詢方式，則得不到這個結果。

LIKE 關鍵字也可放在 NOT 運算子之後。NOT LIKE 傳回不符合搜尋條件的資料列。例如，要找出不以 The 開頭的書名，您可以使用 NOT LIKE，如下列查詢所示：

    SELECT  title
    FROM    titles
    WHERE   title NOT LIKE "The %"
    GO

此查詢將傳回 15 資料列。

您可以很有創意的使用 LIKE 關鍵字。不過要小心的測試您的查詢以便確定傳回的資料是您真正需要的部分。如果遺漏了原本應該包含的 NOT 或 ^ 字元，得到的結果集會剛好與您期望的值相反。遺漏了原本必須的 % 字元也會得到錯誤的結果，您還必須注意前後空格的使用。

ESCAPE ESCAPE 關鍵字使您能為萬用字元本身進行模式比對，例如 ^、%、[ 以及 _。在 ESCAPE 關鍵字之後接著您指定要避開的字元，代表字串運算式中的這些字元必須按字面意義逐字比對。例如，在 titles資料表中要找出 title 資料行中有下底線的所有資料列，您可以使用下列查詢：

    SELECT  title
    FROM    titles
    WHERE   title LIKE "%e_%" ESCAPE "e"
    GO

此查詢將沒有資料列被傳回，因為資料庫中並沒有任何一本書名含有下底線。

BETWEEN BETWEEN 關鍵字通常與 AND 一起使用來為搜尋條件指定包括的範圍。語法如下：

    <test_expression> BETWEEN <begin_ expression> AND <end_ expression>

當 test_ expression 運算式大於或等於 begin_ expression 運算式並且小於或等於 end_ expression運算式 時，搜尋條件的結果為 TRUE 布林值；否則結果為 FALSE。

下面的查詢使用 BETWEEN 關鍵字來找出價格在 5 到 25 美元之間的書名：

    SELECT  price, title
    FROM    titles
    WHERE   price BETWEEN 5.00 AND 25.00
    GO

此查詢將傳回 14 資料列。

您也可以將 NOT 與 BETWEEN 一起使用，找出不在指定範圍內的資料列。例如，尋找價格不在 20 到 30 美元的書名（亦即價格小於 20 美元或大於 30 美元），您可以使用下列查詢：

    SELECT  price, title
    FROM    titles
    WHERE   price NOT BETWEEN 20.00 AND 30.00
    GO

當您使用 BETWEEN 關鍵字，test_expression 運算式必須與begin_expression 運算式及 end_expression 運算式有相同的資料型別。

在剛剛的範例中，price資料行的資料型別為 money，因此 begin_ expression 運算式與 end_ expression 運算式必須是一個可以被與之比較或被 隱含轉換 （implicit conversion）為 money 資料型別的數字。您不可在 test_ expression 運算式使用價格卻在 begin_ expression 運算式與 end_ expression 運算式使用字串或 char 資料型別；否則 SQL Server 將傳回一個錯誤訊息。

說明

如果隱含轉換是可行的話，SQL Server 會在需要時自動的轉換資料型別。隱含轉換能自動的將資料型別轉換為另一個能相容的資料型別。轉換後便可進行比較。例如，把一個 smallint 資料型別的資料行與一個 int 資料型別的資料行作比較時，SQL Server 會在比較前將 smallint 隱含轉換為 int 資料型別。如果沒有提供隱含轉換，您可以使用 CAST 或 CONVERT 函數來明確的指定轉換資料行。關於 SQL Server 中哪些資料型別可隱含轉換、哪些資料型別需外顯轉換，可於 SQL Server《線上叢書》索引「CAST」，並在 找到的主題 對話方塊中選擇 CAST與CONVERT 。

最後一個與 BETWEEN 關鍵字相關的例子，是在搜尋條件中使用字串。要找出從 Bennet 到 McBadden 之間按字母順序排資料列的作者姓氏，您可以使用下列查詢：

    SELECT  au_lname
    FROM    authors
    WHERE   au_lname BETWEEN "Bennet" AND "McBadden"
    GO

BETWEEN 的範圍會包含起始值和結束值，因此存在於資料表中的 Bennet 與 McBadden 也會包括在查詢結果之內。

IS NULL IS NULL 關鍵字用來在搜尋條件中選擇在指定資料行中有 NULL 值的資料列。例如，在 titles資料表尋找 notes資料行沒有資料（亦即 notes值為 NULL）的書名，您可以使用下列查詢：

    SELECT  title, notes
    FROM    titles
    WHERE   notes IS NULL
    GO

結果將為：

    title                                  notes
    ------------------------------------   ------
    The Psychology of Computer Cooking     NULL

如您所見，notes 資料行的 NULL 值在結果集中顯示為 NULL。NULL 並不是資料行中的實際值－它僅指出資料行中存在 NULL 值。

要找到 notes資料行有資料的書名 (notes 值不為 null 的書名 )，使用 IS NOT NULL，如下所示：

    SELECT  title, notes
    FROM    titles
    WHERE   notes IS NOT NULL
    GO

結果集顯示有 17 資料列其 notes資料行內至少有一個字元，所以其 notes資料行中沒有 null 值。

IN IN 關鍵字在搜尋條件中用來判斷給定的測試運算式是否符合子查詢或值清單中的任一值。如果找到一個相符的情況， TRUE 將被傳回。

NOT IN 傳回 IN 的相反結果，因此，如果測試運算式在子查詢數值清單中沒有找到，TRUE 將被傳回。語法如下：

    <test_expression> IN (<subquery>)
    或
    <test_expression> IN (<list of values>)

子查詢 （subquery）是一個結果集中只傳回一個資料行的 SELECT 陳述式。子查詢必須被包含在小括號內。 值清單 也是如此，這些清單被包含在小括號內，並以逗號分開。從子查詢或值清單所得的資料行，其資料型別必須與 test_expression 運算式相同。必要時 SQL Server 會執行隱含轉換。

您可以使用 IN 關鍵字和值清單找到3個特定工作說明的 job_id 數字，查詢如下：

    SELECT  job_id
    FROM    jobs
    WHERE   job_desc IN ("Operations Manager",
                         "Marketing Manager",
                         "Designer" )
    GO

此查詢的值清單為：("Operations Manager"、"Marketing Manager"、"Designer" )，為傳回 job ID 的條件，必須是資料列中 job_desc 資料行中的值為前述三個之一。比起使用兩個 OR 運算子來完成查詢，IN 關鍵字可以讓您的查詢易讀易懂，如下所示：

    SELECT  job_id
    FROM    jobs
    WHERE   job_desc = "Operations Manager" OR
            job_desc = "Marketing Manager"  OR
            job_desc = "Designer"
    GO

下面的查詢在一個陳述式中使用 IN 關鍵字兩次－一次針對子查詢，另一次針對子查詢中的值清單：

    SELECT  fname, lname --Outer query
    FROM    employee
    WHERE   job_id IN ( SELECT job_id --Inner query, or subquery
                        FROM jobs
                        WHERE job_desc IN ("Operations Manager",
                                           "Marketing Manager",
                                           "Designer"))
    GO

子查詢的結果集將先被找到－本例中，它是一個 job_id 值的集合。此子查詢產生的 job_id 值的集合並不會傳回到螢幕上，外層的查詢將把它當作運算式一般運用在自己的 IN 搜尋條件。最後的結果集將包含所有職業頭銜為 Operations Manager、Marketing Manager 或 Designer 的員工姓名。結果集如下：

    fname                lname
    -------------------- ----------------------
    Pedro                Afonso
    Lesley               Brown
    Palle                Ibsen
    Karin                Josephs
    Maria                Larsson
    Elizabeth            Lincoln
    Patricia             McKenna
    Roland               Mendel
    Helvetius            Nagy
    Miguel               Paolino
    Daniel               Tonini

    (影響11個資料列)

IN 也可與 NOT 運算子一起使用。例如，要傳回所有不位在加州、德州或伊利諾州的出版商姓名，請執行以下查詢：

    SELECT  pub_name
    FROM    publishers
    WHERE   state NOT IN ("CA",
                          "TX",
                          "IL")
    GO

查詢將傳回 state 資料行值不為值清單所資料列三州之一的五個資料列。如果您的資料庫選項 ANSI nulls 設定為 ON ，結果將只傳回三資料列。這是因為原始結果集中的五資料列裡有兩資料列的 state值為 NULL，而當 ANSI nulls 設定為 ON 時 NULL 是不會被選取。

要判斷 pubs 資料庫中您的 ANSI nulls 設定為何，可以執行以下的系統預存程序：

    sp_dboption "pubs", "ANSI nulls"
    GO

如果 ANSI nulls 設定為 OFF ，可使用以下陳述式將其改為 ON ：

    sp_dboption "pubs", "ANSI nulls", TRUE
    GO

要將 ON 改為 OFF ，以 FALSE 代替 TRUE 。

EXISTS EXISTS 關鍵字用來檢測其後的子查詢中的資料資料列的存在性。語法如下：

    EXISTS (<subquery>)

如果有任何資料列滿足此一子查詢，TRUE 將被傳回。

要選擇曾經出版過書籍的作者姓名，您可使用以下查詢：

    SELECT  au_fname, au_lname
    FROM    authors
    WHERE   EXISTS (SELECT au_id
                    FROM   titleauthor
                    WHERE  titleauthor.au_id = authors.au_id )
    GO

存在於 authors資料表，但沒有出版任何書（資料列於 titleauthor 資料表）的作者姓名將不會被選擇。如果在子查詢中沒有任何資料列被選擇，交給外層查詢使用的結果集為空。（零資料列被選擇）

CONTAINS 與 FREETEXT CONTAINS 與 FREETEXT 關鍵字用來在基於字元的資料型別的資料行中進行全文檢索。它們比 LIKE 關鍵字提供更大的彈性。例如，CONTAINS 關鍵字讓您可以搜尋與指定字元或片語不完全相符但類似的文字（是一種 模糊 （fuzzy）比對）。FREETEXT 允許您搜尋與指定字串部份或全部相符（或模糊比對）的若干文字。這些文字不需要完全符合指定的搜尋字串，也不需要與字串中的文字有相同的順序。這兩個關鍵字有多種用途並對全文檢索提供了不少選項，不過這些主題已超過本章的討論範圍。

相關資訊

關於 CONTAINS 與 FREETEXT 關鍵字的相關資訊，可於 SQL Server《線上叢書》索引「CONTAINS」與「FREETEXT」。

GROUP BY 子句

GROUP BY 子句用於 WHERE 子句之後，指示結果集中的資料列將按照指定的群組資料行來群組化。如果 SELECT 子句使用到彙總函數，則每個群組都會計算一個總計值並顯示於輸出結果。（ 彙總函數 （aggregate function）執行計算並傳回值；這些函數將在本章稍後 〈彙總函數〉 一節裡有詳細說明。）

說明

選取清單中的每個資料行－除了彙總函數使用的資料行－必須在 GROUP BY 子句中指定為群組資料行；否則 SQL Server 將傳回一個錯誤訊息。如果不遵循此一規則（指定的 GROUP BY 資料行必須群組選取清單中的每個資料行），輸出結果將無法以合理的方式顯示。

當 SELECT 子句含有彙總函數時，GROUP BY 最為有用。讓我們看一下用GROUP BY 子句尋找每種書籍銷售總量的 SELECT 陳述式：

    SELECT  title_id, SUM (qty )
    FROM    sales
    GROUP   BY title_id
    GO

結果集如下：

    title_id
    -------- ------------
    BU1032            15
    BU1111            25
    BU2075            35
    BU7832            15
    MC2222            10
    MC3021            40
    PC1035            30
    PC8888            50
    PS1372            20
    PS2091           108
    PS2106            25
    PS3333            15
    PS7777            25
    TC3218            40
    TC4203            20
    TC7777            20

    (影響16個資料列)

此查詢沒有 WHERE 子句－您並不需要。結果集顯示了一個 title_id 資料行以及一個沒有標題的摘要資料行。對每個不同的 title_id，各個書籍的總銷售量顯示在摘要資料行。例如，title_id 為 BU1032的書籍在 sales資料表出現了兩次－一次在 qty資料行顯示銷售了 5 本， 第二次在另一個訂單中銷售了 10 本。SUM 彙總函數總和這兩次的銷售得出 15 本的總量，並顯示於摘要資料行。要為摘要資料行加上一個標題，可使用 AS 關鍵字：

    SELECT   title_id, SUM(qty) AS "Total Sales"
    FROM     sales
    GROUP BY title_id
    GO

現在結果集中的摘要資料行標題為 Total Sales：

    title_id  Total Sales
    --------  ------------
    BU1032            15
    BU1111            25
    BU2075            35
    BU7832            15
    MC2222            10
    MC3021            40
    PC1035            30
    PC8888            50
    PS1372            20
    PS2091           108
    PS2106            25
    PS3333            15
    PS7777            25
    TC3218            40
    TC4203            20
    TC7777            20

    (影響16個資料列)

您可以在 GROUP BY 子句中包含一個以上的資料行讓群組巢狀化。 巢狀群組 （nesting groups）意味著結果集將以每個群組資料行被指定的順序來進行群組化的工作。例如，要找出已經依類型及出版商群組化的書籍的平均價格，可執行下列查詢：

    SELECT   type, pub_id, AVG(price ) AS "Average Price"
    FROM     titles
    GROUP BY type, pub_id
    GO

結果集如下：

    type         pub_id            Average Price
    ------------ ------ ------------------------
    business     0736                       2.99
    psychology   0736                      11.48
    UNDECIDED    0877                       NULL
    mod_cook     0877                      11.49
    psychology   0877                      21.59
    trad_cook    0877                      15.96
    business     1389                      17.31
    popular_comp 1389                      21.48

    (影響8個資料列)

注意心理學和商業類型的書籍出現了不只一次，因為它們按照不同的出版商 ID分組。UNDECIDED 類型的 NULL 值表示資料表中沒有此種類型的價格，因此也沒有計算平均值。

GROUP BY 子句提供了一個選擇性的關鍵字 ALL，用以指定結果集中將包含所有群組，即使它們可能不符合搜尋條件。沒有資料列符合搜尋條件的群組將會在摘要資料行出現 NULL 以便於辨別。例如，要顯示版稅為 12% 的書籍的平均價格（以及其他條件不符的書籍，其摘要資料行為 NULL），並將這些書籍先以類型再以出版商 ID 來群組化，執行下列查詢：

    SELECT   type, pub_id, AVG(price) AS "Average Price"
    FROM     titles
    WHERE    royalty = 12
    GROUP BY ALL type, pub_id
    GO

結果集如下：

    type         pub_id            Average Price
    ------------ ------ ------------------------
    business     0736                       NULL
    psychology   0736                      10.95
    UNDECIDED    0877                       NULL
    mod_cook     0877                      19.99
    psychology   0877                       NULL
    trad_cook    0877                       NULL
    business     1389                       NULL
    popular_comp 1389                       NULL

    (影響8個資料列)

所有類型的書籍都在輸出中顯示，版稅不為 12% 的書籍則出現了 NULL 值。

如果我們不使用關鍵字 ALL，結果集將只包含版稅為 12% 的書籍類型：

    type         pub_id            Average Price
    ------------ ------ ------------------------
    psychology   0736                      10.95
    mod_cook     0877                      19.99

    (影響2個資料列)

GROUP BY 子句常與 HAVING 子句一起使用，我們將在下面討論。

HAVING 子句

HAVING 用來為一個群組或一個彙總函數指定搜尋條件。HAVING 通常使用在GROUP BY 子句之後，主要用途在於對一個已經被群組化的結果指定要檢測的搜尋條件。如果搜尋條件是使用在群組化之前，則 WHERE 子句要比 HAVING 更有效率。HAVING 技巧減少了必須群組化的資料列數。如果沒有 GROUP BY 子句，HAVING 只能與選取清單的彙總函數一起使用。在這種情況下，HAVING 子句與 WHERE 子句的作用相同。如果 HAVING 不是用在這些情況下，SQL Server 將傳回錯誤資訊。

HAVING 子句的語法如下：

    HAVING <search_condition>

這裏的 search_condition 與在本章前面 〈WHERE子句和搜尋條件〉 部分所講的搜尋條件具有相同的意義。HAVING 子句和 WHERE 子句不同在於 HAVING 子句可以在搜尋條件中包含彙總函數，而 WHERE 子句不行。

說明

您可以在 SELECT 和 HAVING 子句中使用彙總函數，但在 WHERE 子句不能。

下面的查詢用 HAVING 子句選擇平均價格超過 15 美元的出版商的書籍種類：

    SELECT   type, pub_id, AVG (price ) AS "Average Price"
    FROM     titles
    GROUP BY type,pub_id
    HAVING   AVG(price ) > 15.00
    GO

結果集如下：

    type         pub_id            Average Price
    ------------ ------ ------------------------
    psychology   0877                      21.59
    trad_cook    0877                      15.96
    business     1389                      17.31
    popular_comp 1389                      21.48

    (影響4個資料列)

您也可以將邏輯運算子與 HAVING 子句一起使用。加上 AND 運算子的查詢如下：

    SELECT   type, pub_id, AVG (price ) AS "Average Price"
    FROM     titles
    GROUP BY type, pub_id
    HAVING   AVG(price) >= 15.00 AND AVG(price ) <= 20.00
    GO

結果集如下：

    type         pub_id            Average Price
    ------------ ------ ------------------------
    trad_cook    0877                      15.96
    business     1389                      17.31

    (影響2個資料列)

您也可以用 BETWEEN 子句代替 AND，得到的結果是相同的：

    SELECT   type, pub_id, AVG(price) AS "Average Price"
    FROM     titles
    GROUP BY type, pub_id
    HAVING   AVG (price ) BETWEEN 15.00 AND 20.00
    GO

若您在使用 HAVING 時不用 GROUP BY 子句，則在選取清單與 HAVING 子句裡必須包含一個彙總函數。例如要選取 mod_cook 類型的總金額大於 20 美元的各種書籍，可執行以下查詢：

    SELECT  SUM(price)
    FROM    titles
    WHERE   type = "mod_cook"
    HAVING  SUM(price) > 20
    GO

如果將運算式 SUM(price) > 20 置於 WHERE 子句中，SQL Server 將會傳回錯誤資訊（在 WHERE 子句中不允許使用彙總函數）。

說明

記住，使用 HAVING 子句是為檢測 GROUP BY 子句所得到的結果集而加上一個搜尋條件，或用來檢測一個彙總函數。其他的情況應該用 WHERE 子句指定搜尋條件，那樣將更有效率而且避免 SQL Server 發生錯誤。

ORDER BY 子句

ORDER BY 子句用來指定結果集裡資料列的排序方式。您可以用 ASC 或DESC 選項來指定是升冪（從小到大）或是降冪（從大到小）。當沒有選定時，預設為升冪。您可以在 ORDER BY 子句中指定很多資料行，結果將按第一個資料行來排序，如果第一個資料行有重複值，就按第二個資料行排序，依此類推。在後面的內容中可以看到，如果讓 ORDER BY 與 GROUP BY 共同使用，這種排序將更有意義。首先讓我們看一個在 ORDER BY 子句使用一個資料行，按作者姓氏來升冪排序的例子：

    SELECT   au_lname, au_fname
    FROM     authors
    ORDER BY au_lname ASC
    GO

結果集將按姓的字母順序排列。記住若在安裝 SQL Server 時設定 排序順序區分大小寫 ，將會對例如 del Castillo 這樣的姓氏產生影響。

如果要排序的結果資料行不只一個，只要加上這些資料行的名稱，並在 ORDER BY 子句中用逗號區隔即可。下面的查詢選擇 job_id 和員工的姓名然後將它們先按姓氏再按名字排序：

    SELECT   job_id, lname, fname
    FROM     employee
    ORDER BY job_ifd, lname, fname
    GO

結果集如下：

    job_id lname                          fname
    ------ ------------------------------ -------------
         2 Cramer                         Philip
         3 Devon                          Ann
         4 Chang                          Francisco
         5 Henriot                        Paul
         5 Hernadez                       Carlos
         5 Labrune                        Janine
         5 Lebihan                        Laurence
         5 Muller                         Rita
         5 Ottlieb                        Sven
         5 Pontes                         Maria
         6 Ashworth                       Victoria
         6 Karttunen                      Matti
         6 Roel                           Diego
         6 Roulet                         Annette
         7 Brown                          Lesley
         7 Ibsen                          Palle
         7 Larsson                        Maria
         7 Nagy                           Helvetius
         ...                         	...
        13 Accorti                        Paolo
        13 O'Rourke                       Timothy
        13 Schmitt                        Carine
        14 Afonso                         Pedro
        14 Josephs                        Karin
        14 Lincoln                        Elizabeth

    (影響43個資料列)

在這個查詢的結果集中看不出依照名稱排序，這是因為在同一個 job_id 中沒有同樣的兩個姓氏。

現在讓我們看一下如何將 ORDER BY 字句和 GROUP BY 子句及彙總函數一起使用：

    SELECT   type, pub_id, AVG(price) AS "Average Price"
    FROM     titles
    GROUP BY type, pub_id
    ORDER BY type
    GO

結果集如下：

    type         pub_id            Average Price
    ------------ ------ ------------------------
    UNDECIDED    0877                       NULL
    business     0736                       2.99
    business     1389                      17.31
    mod_cook     0877                      11.49
    popular_comp 1389                      21.48
    psychology   0736                      11.48
    psychology   0877                      21.59
    trad_cook    0877                      15.96

    (影響8個資料列)

查詢結果將按書籍類型的字母順序（升冪）來排列。並請注意在這個查詢中，type和 pub_id 必須包括在 GROUP BY 子句中，因為它們不是彙總函數的一部分。如果在 GROUP BY 子句中沒有包含 pub_id 資料行，SQL Server 將顯示與圖14-4類似的錯誤訊息。

在 ORDER BY 子句中不能使用彙總函數或子查詢，但如果在 SELECT 子句中給彙總函數一個別名，就可以在 ORDER BY 子句中使用，如下所示：

    SELECT   type, pub_id, AVG(price) AS "Average Price"
    FROM     titles
    GROUP BY type, pub_id
    ORDER BY "Average Price"
    GO

結果集如下：

    type         pub_id            Average Price
    ------------ ------ ------------------------
    UNDECIDED    0877                       NULL
    business     0736                       2.99
    psychology   0736                      11.48
    mod_cook     0877                      11.49
    psychology   0877                      21.59
    trad_cook    0877                      15.96
    business     1389                      17.31
    popular_comp 1389                      21.48

    (影響8個資料列)

圖14-4 在GROUP BY 子句中不包含 pub_id 時顯示的錯誤訊息

現在，結果是按平均價格排序。NULL 值被認為最低，因此它排在最上面。

說明

ORDER BY 子句檢索的實際結果將依 SQL Server 設定的排序順序而定。

UNION 運算子

UNION 被認為是一個運算子，而不是一個子句。它用來將兩個以上的查詢結果合併到一個結果集中。使用 UNION 時必須遵循以下兩個規則：

- 所有的查詢有相同的資料行數目。
- 每個查詢所對應的資料行，資料型別必須能彼此相容。

  在 SELECT 子句中用 UNION 聯合的資料行將以下列方式相互對應：第一個SELECT 子句的第一資料行與其後的 SELECT 子句的第一資料行相對應，第二資料行與其後 SELECT 中的第二資料行相對應，以此類推。如此，由 UNION 聯合的每個 SELECT 子句有相同的資料行數，且一一對應。

  另外，對應的資料行必須要有相容的資料型別，即兩個對應的資料行要不是有相同的資料型別，就是 SQL Server 能隱含轉換其中一個。下面的例子用 UNION 聯合兩個 SELECT 陳述式的結果集，該陳述式用於在 publisher 和 stores 資料表中檢索 city和 state資料行：

      SELECT  city, state
      FROM    publishers
      UNION   SELECT  city, state
              FROM    stores
      GO

  結果集如下：

      city                 state
      -------------------- ------
      Fremont              CA
      Los Gatos            CA
      Portland             OR
      Remulade             WA
      Seattle              WA
      Tustin               CA
      Chicago              IL
      Dallas               TX
      Munchen              NULL
      Boston               MA
      New York             NY
      Paris                NULL
      Berkeley             CA
      Washington           DC

      (影響14個資料列)

  說明

  此查詢的輸出順序可能和您使用的 SQL Server 排序順序而有不同。

  在 publisher 和 stores 資料表中，city 和 state 兩資料行有相同的資料型別（char）；這樣就不需要轉換資料型別。UNION 結果集的資料行標題取自第一個 SELECT 陳述式。如果要為標題建立別名，請將其放在第一個 SELECT 陳述式中，如下所示：

      SELECT  city AS "All Cities", state AS "All States"
      FROM    publishers
      UNION   SELECT city, state
              FROM stores
      GO

  結果集如下：

      All Cities           All States
      -------------------- ------------
      Fremont              CA
      Los Gatos            CA
      Portland             OR
      Remulade             WA
      Seattle              WA
      Tustin               CA
      Chicago              IL
      Dallas               TX
      Munchen              NULL
      Boston               MA
      New York             NY
      Paris                NULL
      Berkeley             CA
      Washington           DC

      (影響14個資料列)

  您並不需要在 UNION 的全部兩個 SELECT 子句中使用相同的資料行。例如，可以選擇 stores 資料表中的 city 和 state 資料行，選擇 publishers 資料表中的 city 和 country 資料行，如下所示：

      SELECT  city, country
      FROM    publishers
      UNION   SELECT city, state
              FROM stores
      GO

  結果集如下：

      city                 country
      -------------------- ---------
      Fremont              CA
      Los Gatos            CA
      Portland             OR
      Remulade             WA
      Seattle              WA
      Tustin               CA
      New York             USA
      Paris                France
      Boston               USA
      Munchen              Germany
      Washington           USA
      Chicago              USA
      Berkeley             USA
      Dallas               USA

      (影響14個資料列)

  注意在這個結果集中，前六列來自對 stores 資料表的查詢，而後八列則是對publishers 資料表的查詢。state 資料行的資料型別為 char；country資料行的資料型別為 varchar。因為資料型別必須相容，SQL Server 將執行隱式資料型別轉換，使兩資料行都變為 varchar 型別。資料行標題為city和country，它們是由第一個 SELECT 子句中的資料行提供的。但在結果集中，country 資料行標題若為 State or Country 會更好一些。

  有一個可與 UNION 共同使用的選擇性關鍵字：ALL。如果指定了 ALL，所有相同的資料列都將會顯示在結果集中，換句話說，所有的資料列都被包含。如果沒有指定 ALL，預設值是在結果集中去除重複的列。

  您僅能在最後一個 UNION 的陳述式中使用 ORDER BY，而不能在每個SELECT 陳述式中使用，這個限制保證了最終的結果集將對所有的結果只排序一次。另一方面，只能在個別的陳述式中使用 GROUP BY 和 HAVING，它們只影響各自的結果集但不能影響最終的結果集。下面的 UNION 聯合了兩個各含有一個 GROUP BY 子句的 SELECT 陳述式所傳回的結果。

      SELECT   type, COUNT(title) AS "Number of Titles"
      FROM     titles
      GROUP BY type
      UNION    SELECT   pub_name, COUNT(titles.title)
               FROM     publishers, titles
               WHERE    publishers.pub_id = titles.pub_id
               GROUP BY pub_name
      GO

  結果集如下：

      type                                     Number of Titles
      ---------------------------------------- ----------------
      Algodata Infosystems                                    6
      Binnet & Hardley                                        7
      New Moon Books                                          5
      psychology                                              5
      mod_cook                                                2
      trad_cook                                               3
      popular_comp                                            3
      UNDECIDED                                               1
      business                                                4

      (影響9個資料列)

  結果集顯示每一個出版商實際出版書的項目數（前三列），並顯示每一種類別書籍的項目數。注意在每個各自的查詢中使用 GROUP BY。

  UNION 也可以使用於多於兩個的 SELECT 陳述式。當您建立 UNION 時，注意確認過這些查詢的所有資料行及資料型別正確相符。

  相關資訊

  請參閱在 SQL Server《線上叢書》中與所有可用的 T-SQL 關鍵字和參數的相關文件。

使用 T-SQL 函數

現在您已經對 SELECT 陳述式的基本子句相當熟悉了（

第20章 中將介紹更多），讓我們看一下可以用在 SELECT 子句中的 T-SQL 函數，這樣您就能在建立查詢時有更大的彈性。這些函數有以下幾類：組態、資料指標（cursor）、日期和時間、安全性、中斷資料、系統、系統統計、文字與影像、數學、資料列集（rowset）、字串及彙總函數。這些函數可以執行計算、轉換或其他的一些操作，或傳回一定的資訊。您能夠使用的函數有很多，但在本節裡我們只詳細討論常用的彙總函數。

相關資訊

若要得到以上這些種類及未列出種類的函數的資訊，您可以在線上叢書的索引籤頁中搜尋「函數」。

彙總函數

如前所述，彙總函數針對一群值進行計算並傳回一個單一值。它們可以在選取清單中指定，但大部分情況下是用於陳述式中包含 GROUP BY 子句的時候。前述的一些查詢範例使用了彙總函數 AVG 和 COUNT。資料表14-3列出了可用的彙總函數。

表14-3 彙總函數

函數 | 說明
--- | ---
AVG | 傳回運算式中除了NULL值以外的值的平均數。
COUNT | 傳回運算式中的項目數（與列數相同）。
COUNT_BIG | 類似 COUNT 函數。其間差異在於傳回 bigint 資料型別的計數值，而非 int資料型別。
GROUPING | 傳回一個額外的特殊資料行。僅在 GROUP BY 子句中包含 CUBE或 ROLLUP 運算子時使用。
MAX | 傳回運算式中的最大值。
MIN | 傳回運算式中的最小值。
STDEV | 傳回運算式中所有值的統計標準差。此函數假定計算中使用的值為母體的一個抽樣樣本。
STDEVP | 傳回運算式所有值的母體標準差。此函數假定計算中使用的值為統計母體。
SUM | 傳回運算式中所有值的和。
VAR | 傳回運算式所有值的統計變異數。此函數假定計算中使用的值為母體的一個抽樣樣本。
VARP | 傳回運算式中所有值的統計母體變異數。此函數假定計算中使用的值為統計母體。

COUT 函數有一個特殊的用處：計算資料表中所有資料列的數目。在 COUNT後用（*）就可達成，如下所示：

    SELECT  COUNT(*)
    FROM    publishers
    GO

結果集如下：

    --------
           8

結果集顯示 publishers 資料表現在含有8個資料列。

AVG、COUNT、MAX、MIN 和 SUM 函數都可以與選擇性關鍵字 ALL 和DISTINCT 一起使用。對於每一個函數，ALL 表示這個函數必須運用於這個運算式中的所有值，DISTINCT 則指出重複值將被忽略，相同的數值將只被計算一次。如果選項沒有指定，預設為 ALL。

下面的例子在選取清單中一起使用 MAX 和 MIN 函數，以尋找最貴和最便宜的書之間的價格差：

    SELECT  MAX(price) - MIN(price) AS "Price Difference"
    FROM    titles
    GO

結果集如下：

    Price Difference
    ------------------------
                       19.96

這個範例中，利用 SUM 來得到各個商店中所訂購的項目總數：

    SELECT   stores.stor_name, SUM(sales.qty) AS "Total Items Ordered"
    FROM     sales, stores
    WHERE    sales.stor_id = stores.stor_id
    GROUP BY stor_name
    GO

結果集如下：

    stor_name                                Total Items Ordered
    ---------------------------------------- -------------------
    Barnum's                                                 125
    Bookbeat                                                  80
    Doc-U-Mat: Quality Laundry and Books                     130
    Eric the Read Books                                        8
    Fricative Bookshop                                        60
    News & Brews                                              90

    (影響6個資料列)

說明

記住，SQL Server 提供了許多種函數。如果要執行一個特殊的作業，先檢視一下 SQL Server《線上叢書》有沒有提供現成的內建函數。

SELECT 的其他用法

SELECT 陳述式主要用來執行查詢，從資料庫中檢索資料。這些查詢可以很簡單，也可以很複雜。SELECT 也可以用來設定變數的值或呼叫函數。

您可以用 SELECT 陳述式在交易或預存程序中設定一個本機變數的值，儘管更好的方式是用 SET 陳述式。本機變數必須用 ＠ 作為其第一個字元。例如使用SET給定本機變數 ＠count 值為0，所用的陳述式如下：

    SET  @count = 0
    GO

您可以用 SELECT 將一個本域變數給定為一個查詢所傳回的值。例如把一個本域變數 ＠price 指定為從 price資料行中得到的最高價格，使用以下陳述式：

    SELECT  @price = MAX(price)
    FROM    items
    GO

當在 SELECT 陳述式中設定一個本域變數時，最好的情況是查詢只傳回一資料列。如果有多個資料列被傳回時，本域變數將被設定為最後一列傳回的值。

您也可以用 SELECT 呼叫函數，例如系統提供的函數 GETDATE。下面的陳述式呼叫 GATDATE 搜尋當前的日期和時間：

    SELECT  GETDATE()
    GO

GETDATE 並不包含任何參數，但括號還是必須的。

本章總結

在這一章中，我們討論了 SELECT 陳述式、各種可以在 SELECT 陳述式中使用的子句及其使用方法，以及搜尋條件和函數。您已經學習了邏輯運算子、聯結、別名、模式比對、結果群組和排序以及更多的內容。我們已經概括了不少資訊，但關於 SELECT 陳述式還有更多您可利用的地方。在

第20章 中，我們將學習可以在 SELECT 陳述式及其他資料操作陳述式 INSERT、UPDATE 和 DELETE 中使用的進階選項。在 第15章 中，我們將學習如何修改資料庫資料表。