本文介紹如何使用 SELECT 語句查詢 SQL 如何對錶進行創建、更新和刪除操作 中創建的 Product 表中數據。這裡使用的 SELECT 語句是 SQL 最基本也是最重要的語句。 請大家在實際運行本文中的 SELECT 語句時,親身體驗一下其書寫方法和執行結果。 執行查詢操作時可以指定想要查 ...
目錄
本文介紹如何使用 SELECT
語句查詢 SQL 如何對錶進行創建、更新和刪除操作 中創建的 Product
表中數據。這裡使用的 SELECT
語句是 SQL 最基本也是最重要的語句。
請大家在實際運行本文中的 SELECT
語句時,親身體驗一下其書寫方法和執行結果。
執行查詢操作時可以指定想要查詢數據的條件(查詢條件)。查詢時可以指定一個或多個查詢條件,例如“某一列等於這個值”“某一列計算之後的值大於這個值”等。
一、SELECT 語句基礎
本節重點
使用
SELECT
語句從表中選取數據。為列設定顯示用的別名。
SELECT
語句中可以使用常數或者表達式。通過指定
DISTINCT
可以刪除重覆的行。SQL 語句中可以使用註釋。
可以通過
WHERE
語句從表中選取出符合查詢條件的數據。
1.1 列的查詢
從表中選取數據時需要使用 SELECT
語句,也就是只從表中選出(SELECT
)必要數據的意思。通過 SELECT
語句查詢並選取出必要數據的過程稱為匹配查詢或查詢(query)。
SELECT
語句是 SQL 語句中使用最多的最基本的 SQL 語句。掌握了 SELECT
語句,距離掌握 SQL 語句就不遠了。
SELECT
語句的基本語法如下所示。
語法 1 基本的 SELECT 語句
SELECT <列名>,……
FROM <表名>;
該 SELECT
語句包含了 SELECT
和 FROM
兩個子句(clause)。子句是 SQL 語句的組成要素,是以 SELECT
或者 FROM
等作為起始的短語。
SELECT
子句中列舉了希望從表中查詢出的列的名稱,而 FROM
子句則指定了選取出數據的表的名稱。
接下來,我們嘗試從 SQL 如何對錶進行創建、更新和刪除操作 中創建出的 Product
(商品)表中,查詢出圖 1 所示的 product_id
(商品編號)列、product_name
(商品名稱)列和 purchase_price
(進貨單價)列。

圖 1 查詢出 Product 表中的列
對應的 SELECT
語句請參見代碼清單 1,該語句正常執行的結果如執行結果所示 [1]。
代碼清單 1 從 Product 表中輸出 3 列
SELECT product_id, product_name, purchase_price
FROM Product;
執行結果:
product_id | product_name | purchase_price
-----------+--------------+---------------
0001 | T恤衫 | 500
0002 | 打孔器 | 320
0003 | 運動T恤 | 2800
0004 | 菜刀 | 2800
0005 | 高壓鍋 | 5000
0006 | 叉子 |
0007 | 擦菜板 | 790
0008 | 圓珠筆 |
SELECT
語句第一行的 SELECT product_id, product_name, purchase_price
就是 SELECT
子句。查詢出的列的順序可以任意指定。
查詢多列時,需要使用逗號進行分隔。查詢結果中列的順序和 SELECT
子句中的順序相同 [2]。
1.2 查詢出表中所有的列
想要查詢出全部列時,可以使用代表所有列的星號(*
)。
語法 2 查詢全部的列
SELECT *
FROM <表名>;
例如,查詢 Product
表中全部列的語句如代碼清單 2 所示。
代碼清單 2 輸出 Product 表中全部的列
SELECT *
FROM Product;
得到的結果和代碼清單 3 中的 SELECT
語句的結果相同。
代碼清單 3 與代碼清單 2 具有相同含義的 SELECT 語句
SELECT product_id, product_name, product_type, sale_price,
purchase_price, regist_date
FROM Product;
執行結果如下所示:
product_id | product_name | product_type | sale_price | purchase_price | regist_date
------------+--------------+--------------+------------+----------------+------------
0001 | T恤衫 | 衣服 | 1000 | 500 | 2009-09-20
0002 | 打孔器 | 辦公用品 | 500 | 320 | 2009-09-11
0003 | 運動T恤 | 衣服 | 4000 | 2800 |
0004 | 菜刀 | 廚房用具 | 3000 | 2800 | 2009-09-20
0005 | 高壓鍋 | 廚房用具 | 6800 | 5000 | 2009-01-15
0006 | 叉子 | 廚房用具 | 500 | | 2009-09-20
0007 | 擦菜板 | 廚房用具 | 880 | 790 | 2008-04-28
0008 | 圓珠筆 | 辦公用品 | 100 | | 2009-11-11
法則 1
星號(
*
)代表全部列的意思。
但是,如果使用星號的話,就無法設定列的顯示順序了。這時就會按照 CREATE TABLE
語句的定義對列進行排序。
專欄
隨意使用換行符
SQL 語句使用換行符或者半形空格來分隔單詞,在任何位置進行分隔都可以,即使像下麵這樣通篇都是換行符也不會影響
SELECT
語句的執行。但是這樣可能會由於看不清楚而出錯。原則上希望大家能夠以子句為單位進行換行(子句過長時,為方便起見可以換行)。
SELECT * FROM Product ;
另外,像下麵這樣插入空行(無任何字元的行)會造成執行錯誤,請特別註意。
SELECT * FROM Product;
1.3 為列設定別名
SQL 語句可以使用 AS
關鍵字為列設定別名。請參見代碼清單 4。
代碼清單 4 為列設定別名
SELECT product_id AS id,
product_name AS name,
purchase_price AS price
FROM Product;
執行結果:
id | name | price
------+---------+-------
0001 | T恤衫 | 500
0002 | 打孔器 | 320
0003 | 運動T恤 | 2800
0004 | 菜刀 | 2800
0005 | 高壓鍋 | 5000
0006 | 叉子 |
0007 | 擦菜板 | 790
0008 | 圓珠筆 |
別名可以使用中文,使用中文時需要用 雙引號("
) 括起來 [3]。請註意不是單引號('
)。設定中文別名的 SELECT
語句請參見代碼清單 5。
代碼清單 5 設定中文別名
SELECT product_id AS "商品編號",
product_name AS "商品名稱",
purchase_price AS "進貨單價"
FROM Product;
執行結果:
商品編號 | 商品名稱 | 進貨單價
----------+----------+---------
0001 | T恤衫 | 500
0002 | 打孔器 | 320
0003 | 運動T恤 | 2800
0004 | 菜刀 | 2800
0005 | 高壓鍋 | 5000
0006 | 叉子 |
0007 | 擦菜板 | 790
0008 | 圓珠筆 |
通過執行結果來理解就更加容易了。像這樣使用別名可以讓 SELECT
語句的執行結果更加容易理解和操作。
法則 2
設定漢語別名時需要使用雙引號(
"
)括起來。
1.4 常數的查詢
SELECT
子句中不僅可以書寫列名,還可以書寫常數。
代碼清單 6 中的 SELECT
子句中的第一列 '商品'
是字元串常數,第 2 列 38
是數字常數,第 3 列 '2009-02-24'
是日期常數,它們將與 product_id
列和 product_name
列一起被查詢出來。[4]
代碼清單 6 查詢常數
SELECT '商品' AS string, 38 AS number, '2009-02-24' AS date,
product_id, product_name
FROM Product;
執行結果:
string | number | date | product_id | product_name
---------+----------+-------------+------------+--------------
商品 | 38 | 2009-02-24 | 0001 | T恤衫
商品 | 38 | 2009-02-24 | 0002 | 打孔器
商品 | 38 | 2009-02-24 | 0003 | 運動T恤
商品 | 38 | 2009-02-24 | 0004 | 菜刀
商品 | 38 | 2009-02-24 | 0005 | 高壓鍋
商品 | 38 | 2009-02-24 | 0006 | 叉子
商品 | 38 | 2009-02-24 | 0007 | 擦菜板
商品 | 38 | 2009-02-24 | 0008 | 圓珠筆
如上述執行結果所示,所有的行中都顯示出了 SELECT
子句中的常數。
此外,SELECT
子句中除了書寫常數,還可以書寫計算式。我們將在下一節中學習如何書寫計算式。
1.5 從結果中刪除重覆行
想知道 Product
表中保存了哪些商品種類(product_type
)時,如果能像圖 2 那樣刪除重覆的數據該有多好啊。

圖 2 除去重覆數據後的商品種類
如上所示,想要刪除重覆行時,可以通過在 SELECT
子句中使用 DISTINCT
來實現(代碼清單 7)。
代碼清單 7 使用 DISTINCT
刪除 product_type
列中重覆的數據
SELECT DISTINCT product_type
FROM Product;
執行結果:
product_type
---------------
廚房用具
衣服
辦公用品
法則 3
在
SELECT
語句中使用DISTINCT
可以刪除重覆行。
在使用 DISTINCT
時,NULL
也被視為一類數據。NULL
存在於多行中時,也會被合併為一條 NULL
數據。
對含有 NULL
數據的 purchase_price
(進貨單價)列使用 DISTINCT
的 SELECT
語句請參見代碼清單 8。
除了兩條 2800
的數據外,兩條 NULL
的數據也被合併為一條。
代碼清單 8 對含有 NULL
數據的列使用 DISTINCT
關鍵字
SELECT DISTINCT purchase_price
FROM Product;
執行結果:
DISTINCT
也可以像代碼清單 9 那樣在多列之前使用。此時,會將多個列的數據進行組合,將重覆的數據合併為一條。
代碼清單 9 中的 SELECT
語句,對 product_type
(商品種類)列和 regist_date
(登記日期)列的數據進行組合,將重覆的數據合併為一條。
代碼清單 9 在多列之前使用 DISTINCT
SELECT DISTINCT product_type, regist_date
FROM Product;
執行結果:
product_type | regist_date
--------------+------------
衣服 | 2009-09-20
辦公用品 | 2009-09-11
辦公用品 | 2009-11-11
衣服 |
廚房用具 | 2009-09-20
廚房用具 | 2009-01-15
廚房用具 | 2008-04-28
如上述執行結果所示,product_type
列為 '廚房用具'
,同時 regist_date
列為 '2009-09-20'
的兩條數據被合併成了一條。
DISTINCT
關鍵字只能用在第一個列名之前。因此,請大家註意不能寫成 regist_date, DISTINCT product_type
。
1.6 根據 WHERE 語句來選擇記錄
前面的例子都是將表中存儲的數據全都選取出來,但實際上並不是每次都需要選取出全部數據,大部分情況都是要選取出滿足“商品種類為衣服”“銷售單價在 1000
元以上”等某些條件的數據。
SELECT
語句通過 WHERE
子句來指定查詢數據的條件。在 WHERE
子句中可以指定“某一列的值和這個字元串相等”或者“某一列的值大於這個數字”等條件。
執行含有這些條件的 SELECT
語句,就可以查詢出只符合該條件的記錄了。[5]
在 SELECT
語句中使用 WHERE
子句的語法如下所示。
語法 3 SELECT 語句中的 WHERE 子句
SELECT <列名>, ……
FROM <表名>
WHERE <條件表達式>;
圖 3 顯示了從 Product
表中選取商品種類(product_type
)為 '衣服'
的記錄。

圖 3 選取商品種類為’衣服’的記錄
從被選取的記錄中還可以查詢出想要的列。為了更加容易理解,我們在查詢 product_type
列的同時,把 product_name
列也讀取出來。
SELECT
語句請參見代碼清單 10。
代碼清單 10 用來選取 product_type 列為 '衣服' 的記錄的 SELECT 語句
SELECT product_name, product_type
FROM Product
WHERE product_type = '衣服';
執行結果:
product_name | product_type
--------------+--------------
T恤衫 | 衣服
運動T恤 | 衣服
WHERE
子句中的“product_type = '衣服'
”就是用來表示查詢條件的表達式(條件表達式)。
等號是比較兩邊的內容是否相等的符號,上述條件就是將 product_type
列的值和 '衣服'
進行比較,判斷是否相等。Product
表的所有記錄都會被進行比較。
接下來會從查詢出的記錄中選取出 SELECT
語句指定的 product_name
列和 product_type
列,如執行結果所示,也就是首先通過 WHERE
子句查詢出符合指定條件的記錄,然後再選取出 SELECT
語句指定的列(圖 4)。

圖 4 選取行之後,再輸出列
代碼清單 10 中的語句為了確認選取出的數據是否正確,通過 SELECT
子句把作為查詢條件的 product_type
列也選取出來了,其實這並不是必須的。
如果只想知道商品名稱的話,可以像代碼清單 11 那樣只選取出 product_name
列。
代碼清單 11 也可以不選取出作為查詢條件的列
SELECT product_name
FROM Product
WHERE product_type = '衣服';
執行結果:
product_name
---------------
T恤衫
運動T恤
SQL 中子句的書寫順序是固定的,不能隨意更改。WHERE
子句必須緊跟在 FROM
子句之後,書寫順序發生改變的話會造成執行錯誤(代碼清單 12)。
代碼清單 12 隨意改變子句的書寫順序會造成錯誤
SELECT product_name, product_type
WHERE product_type = '衣服'
FROM Product;
執行結果(PostgreSQL):
ERROR: "FROM"或者其前後有語法錯誤
第3行: FROM Product;
法則 4
WHERE
子句要緊跟在FROM
子句之後。
1.7 註釋的書寫方法
最後給大家介紹一下註釋的書寫方法。註釋是 SQL 語句中用來標識說明或者註意事項的部分。
註釋對 SQL 的執行沒有任何影響。因此,無論是英文字母還是漢字都可以隨意使用。
註釋的書寫方法有如下兩種。
-
單行註釋
書寫在“
--
”之後,只能寫在同一行。[6] -
多行註釋
書寫在“
/*
”和“*/
”之間,可以跨多行。
實際的示例請參見代碼清單 13 和代碼清單 14。
代碼清單 13 單行註釋的使用示例
-- 本SELECT語句會從結果中刪除重覆行。
SELECT DISTINCT product_id, purchase_price
FROM Product;
代碼清單 14 多行註釋的使用示例
/* 本SELECT語句,
會從結果中刪除重覆行。*/
SELECT DISTINCT product_id, purchase_price
FROM Product;
任何註釋都可以插在 SQL 語句中(代碼清單 15、代碼清單 16)。
代碼清單 15 在 SQL 語句中插入單行註釋
SELECT DISTINCT product_id, purchase_price
-- 本SELECT語句會從結果中刪除重覆行。
FROM Product;
代碼清單 16 在 SQL 語句中插入多行註釋
SELECT DISTINCT product_id, purchase_price
/* 本SELECT語句,
會從結果中刪除重覆行。*/
FROM Product;
這些 SELECT
語句的執行結果與沒有使用註釋時完全一樣。
註釋能夠幫助閱讀者更好地理解 SQL 語句,特別是在書寫複雜的 SQL 語句時,希望大家能夠儘量多加簡明易懂的註釋。
註釋不僅可以寫在 SELECT
語句中,而且可以寫在任何 SQL 語句當中,寫多少都可以。
法則 5
註釋是 SQL 語句中用來標識說明或者註意事項的部分。
分為單行註釋和多行註釋兩種。
二、算術運算符和比較運算符
本節重點
運算符就是對其兩邊的列或者值進行運算(計算或者比較大小等)的符號。
使用算術運算符可以進行四則運算。
括弧可以提升運算的優先順序(優先進行運算)。
包含
NULL
的運算,其結果也是NULL
。比較運算符可以用來判斷列或者值是否相等,還可以用來比較大小。
判斷是否為
NULL
,需要使用IS NULL
或者IS NOT NULL
運算符。
2.1 算術運算符
SQL 語句中可以使用計算表達式。代碼清單 17 中的 SELECT
語句,把各個商品單價的 2 倍(sale_price
的 2 倍)以 "sale_price_x2
" 列的形式讀取出來。
代碼清單 17 SQL 語句中也可以使用運算表達式
SELECT product_name, sale_price,
sale_price * 2 AS "sale_price_x2"
FROM Product;
執行結果:
product_name | sale_price | sale_price_x2
---------------+-------------+----------------
T恤衫 | 1000 | 2000
打孔器 | 500 | 1000
運動T恤 | 4000 | 8000
菜刀 | 3000 | 6000
高壓鍋 | 6800 | 13600
叉子 | 500 | 1000
擦菜板 | 880 | 1760
圓珠筆 | 100 | 200
sale_price_x2
列中的 sale_price * 2
就是計算銷售單價的 2 倍的表達式。
以 product_name
列的值為 'T 恤衫'
的記錄行為例,sale_price
列的值 1000
的 2 倍是 2000
,它以 sale_price_x2
列的形式被查詢出來。
同樣,'打孔器'
記錄行的值 500
的 2 倍 1000
,'運動 T 恤'
記錄行的值 4000
的 2 倍 8000
,都被查詢出來了。運算就是這樣以行為單位執行的。
SQL 語句中可以使用的四則運算的主要運算符如表 1 所示。
表 1 SQL 語句中可以使用的四則運算的主要運算符
含義 | 運算符 |
---|---|
加法運算 | + |
減法運算 | - |
乘法運算 | * |
除法運算 | / |
四則運算所使用的運算符(+
、-
、*
、/
)稱為算術運算符。運算符就是使用其兩邊的值進行四則運算或者字元串拼接、數值大小比較等運算,並返回結果的符號。
加法運算符(+
)前後如果是數字或者數字類型的列名的話,就會返回加法運算後的結果。SQL 中除了算術運算符之外還有其他各種各樣的運算符。
法則 6
SELECT 子句中可以使用常數或者表達式。
當然,SQL 中也可以像平常的運算表達式那樣使用括弧 ()
。括弧中運算表達式的優先順序會得到提升,優先進行運算。
例如在運算表達式 (1 + 2) * 3
中,會先計算 1 + 2
的值,然後再對其結果進行 * 3
運算。
括弧的使用並不僅僅局限於四則運算,還可以用在 SQL 語句的任何表達式當中。具體的使用方法今後會慢慢介紹給大家。
2.2 需要註意 NULL
像代碼清單 2-17 那樣,SQL 語句中進行運算時,需要特別註意含有 NULL 的運算。請大家考慮一下在 SQL 語句中進行如下運算時,結果會是什麼呢?
A:5 + NULL
B:10 - NULL
C:1 * NULL
D:4 / NULL
E:NULL / 9
F:NULL / 0
正確答案全部都是 NULL
。大家可能會覺得奇怪,為什麼會這樣呢?實際上所有包含 NULL
的計算,結果肯定是 NULL
。即使像 F 那樣用 NULL
除以 0
時這一原則也適用。
通常情況下,類似 5/0
這樣除數為 0
的話會發生錯誤,只有 NULL
除以 0
時不會發生錯誤,並且結果還是 NULL
。
儘管如此,很多時候我們還是希望 NULL
能像 0
一樣,得到 5 + NULL = 5
這樣的結果。
不過也不要緊,SQL 中也為我們準備了可以解決這類情況的方法(將會在 SQL 常用的函數 中進行介紹)。
專欄
FROM 子句真的有必要嗎?
在第 1 節中我們介紹過
SELECT
語句是由SELECT
子句和FROM
子句組成的。可實際上
FROM
子句在SELECT
語句中並不是必不可少的,只使用SELECT
子句進行計算也是可以的。代碼清單 A 只包含 SELECT 子句的 SELECT 語句
SQL Server PostgreSQL MySQL
SELECT (100 + 200) * 3 AS calculation;
執行結果:
calculation ------------- 900
實際上,通過執行
SELECT
語句來代替計算器的情況基本上是不存在的。不過在極少數情況下,還是可以通過使用沒有FROM
子句的SELECT
語句來實現某種業務的。例如,不管內容是什麼,只希望得到一行臨時數據的情況。
但是也存在像 Oracle 這樣不允許省略
SELECT
語句中的FROM
子句的 RDBMS,請大家註意。在 Oracle 中,
FROM
子句是必需的,這種情況下可以使用DUAL
這個臨時表。另外,DB2 中可以使用SYSIBM.SYSDUMMY1
這個臨時表。
2.3 比較運算符
在第 1 節學習 WHERE
子句時,我們使用符號 =
從 Product
表中選取出了商品種類(product_type
)為字元串 '衣服'
的記錄。
下麵讓我們再使用符號 =
選取出銷售單價(sale_price
)為 500
元(數字 500)的記錄(代碼清單 18)。
代碼清單 18 選取出 sale_price 列為 500 的記錄
SELECT product_name, product_type
FROM Product
WHERE sale_price = 500;
執行結果:
product_name | product_type
---------------+--------------
打孔器 | 辦公用品
叉子 | 廚房用具
像符號 =
這樣用來比較其兩邊的列或者值的符號稱為比較運算符,符號 =
就是比較運算符。在 WHERE
子句中通過使用比較運算符可以組合出各種各樣的條件表達式。
接下來,我們使用“不等於”這樣代表否定含義的比較運算符 <>
[7],選取出 sale_price
列的值不為 500
的記錄(代碼清單 19)。
代碼清單 19 選取出 sale_price 列的值不是 500 的記錄
SELECT product_name, product_type
FROM Product
WHERE sale_price <> 500;
執行結果:
product_name | product_type
---------------+--------------
T恤衫 | 衣服
運動T恤 | 衣服
菜刀 | 廚房用具
高壓鍋 | 廚房用具
擦菜板 | 廚房用具
圓珠筆 | 辦公用品
SQL 中主要的比較運算符如表 2 所示,除了等於和不等於之外,還有進行大小比較的運算符。
表 2 比較運算符
運算符 | 含義 |
---|---|
= |
和 ~ 相等 |
<> |
和 ~ 不相等 |
>= |
大於等於 ~ |
> |
大於 ~ |
<= |
小於等於 ~ |
< |
小於 ~ |
這些比較運算符可以對字元、數字和日期等幾乎所有數據類型的列和值進行比較。
例如,從 Product
表中選取出銷售單價(sale_price
) 大於等於 1000
元的記錄,或者登記日期(regist_date
)在 2009 年 9 月 27 日
之前的記錄,可以使用比較運算符 >=
和 <
,在 WHERE
子句中生成如下條件表達式(代碼清單 20、代碼清單 21)。
代碼清單 20 選取出銷售單價大於等於 1000 元的記錄
SELECT product_name, product_type, sale_price
FROM Product
WHERE sale_price >= 1000;
執行結果:
product_name | product_type | sale_price
---------------+--------------+--------------
T恤衫 | 衣服 | 1000
運動T恤 | 衣服 | 4000
菜刀 | 廚房用具 | 3000
高壓鍋 | 廚房用具 | 6800
代碼清單 21 選取出登記日期在 2009 年 9 月 27 日 之前的記錄
SELECT product_name, product_type, regist_date
FROM Product
WHERE regist_date < '2009-09-27';
執行結果:
product_name | product_type | regist_date
---------------+--------------+-----------
T恤衫 | 衣服 | 2009-09-20
打孔器 | 辦公用品 | 2009-09-11
菜刀 | 廚房用具 | 2009-09-20
高壓鍋 | 廚房用具 | 2009-01-15
叉子 | 廚房用具 | 2009-09-20
擦菜板 | 廚房用具 | 2008-04-28
小於某個日期就是在該日期之前的意思。想要實現在某個特定日期(包含該日期)之後的查詢條件時,可以使用代表大於等於的 >=
運算符。
另外,在使用大於等於(>=
)或者小於等於(<=
)作為查詢條件時,一定要註意不等號(<
、>
)和等號(=
)的位置不能顛倒。
一定要讓不等號在左,等號在右。如果寫成(=<
)或者(=>
)就會出錯。當然,代表不等於的比較運算符也不能寫成(><
)。
法則 7
使用比較運算符時一定要註意不等號和等號的位置。
除此之外,還可以使用比較運算符對計算結果進行比較。代碼清單 22 在 WHERE
子句中指定了銷售單價(sale_price
)比進貨單價(purchase_price
)高出 500
元以上的條件表達式。
為了判斷是否高出 500
元,需要用 sale_price
列的值減去 purchase_price
列的值。
代碼清單 22 WHERE 子句的條件表達式中也可以使用計算表達式
SELECT product_name, sale_price, purchase_price
FROM Product
WHERE sale_price - purchase_price >= 500;
執行結果:
product_name | sale_price | purchase_price
---------------+-------------+---------------
T恤衫 | 1000 | 500
運動T恤 | 4000 | 2800
高壓鍋 | 6800 | 5000
2.4 對字元串使用不等號時的註意事項
對字元串使用大於等於或者小於等於不等號時會得到什麼樣的結果呢?接下來我們使用表 3 中的 Chars
表來進行確認。
雖然該表中存儲的都是數字,但 chr
是字元串類型(CHAR
類型)的列。
表 3 Chars 表
chr(字元串類型) | - |
---|---|
1 | - |
2 | - |
3 | - |
10 | - |
11 | - |
222 | - |
可以使用代碼清單 23 中的 SQL 語句來創建 Chars
表。
代碼清單 23 創建 Chars 表並插入數據
-- DDL :創建表
CREATE TABLE Chars
(chr CHAR(3) NOT NULL,
PRIMARY KEY (chr));
SQL Server PostgreSQL
-- DML :插入數據
BEGIN TRANSACTION; -------------①
INSERT INTO Chars VALUES ('1');
INSERT INTO Chars VALUES ('2');
INSERT INTO Chars VALUES ('3');
INSERT INTO Chars VALUES ('10');
INSERT INTO Chars VALUES ('11');
INSERT INTO Chars VALUES ('222');
COMMIT;
特定的 SQL
代碼清單 23 中的 DML 語句根據 DBMS 的不同而略有差異。
在 MySQL 中執行該語句時,請大家把 ① 的部分改成“
START TRANSACTION;
”。在 Oracle 和 DB2 中執行時不需用到 ① 的部分,請刪除。
那麼,對 Chars
表執行代碼清單 24 中的 SELECT
語句(查詢條件是 chr
列大於 '2'
)會得到什麼樣的結果呢?
代碼清單 24 選取出大於 '2' 的數據的 SELECT 語句
SELECT chr
FROM Chars
WHERE chr > '2';
大家是不是覺得應該選取出比 2
大的 3
、10
、11
和 222
這 4 條記錄呢?下麵就讓我們來看看該 SELECT
語句的執行結果吧。
執行結果:
chr
-----
3
222
沒想到吧?是不是覺得 10
和 11
比 2
大,所以也應該選取出來呢?大家之所以這樣想,是因為混淆了數字和字元串,也就是說 2
和 '2'
並不一樣。
現在,chr
列被定為字元串類型,並且在對字元串類型的數據進行大小比較時,使用的是和數字比較不同的規則。
典型的規則就是按照字典順序進行比較,也就是像姓名那樣,按照條目在字典中出現的順序來進行排序。
該規則最重要的一點就是,以相同字元開頭的單詞比不同字元開頭的單詞更相近。
Chars
表 chr
列中的數據按照字典順序進行排序的結果如下所示。
1
10
11
2
222
3
'10'
和 '11'
同樣都是以 '1'
開頭的字元串,首先判定為比 '2'
小。這就像在字典中“提問”“提議”和“問題”按照如下順序排列一樣。
提問
提議
問題
或者我們以書籍的章節為例也可以。1-1 節包含在第 1 章當中,所以肯定比第 2 章更靠前。
1
1-1
1-2
1-3
2
2-1
2-2
3
進行大小比較時,得到的結果是 '1-3'
比 '2'
小('1-3' < '2'
),'3'
大於 '2-2'
('3' > '2'
)。
比較字元串類型大小的規則今後還會經常使用,所以請大家牢記 [8]。
法則 8
字元串類型的數據原則上按照字典順序進行排序,不能與數字的大小順序混淆。
2.5 不能對 NULL 使用比較運算符
關於比較運算符還有一點十分重要,那就是作為查詢條件的列中含有 NULL
的情況。
例如,我們把進貨單價(purchase_price
)作為查詢條件。請註意,商品“叉子”和“圓珠筆”的進貨單價是 NULL
。
我們先來選取進貨單價為 2800
元(purchase_price = 2800
)的記錄(代碼清單 25)。
代碼清單 25 選取進貨單價為 2800 元的記錄
SELECT product_name, purchase_price
FROM Product
WHERE purchase_price = 2800;
執行結果:
product_name | purchase_price
---------------+---------------
運動T恤 | 2800
菜刀 | 2800
大家對這個結果應該都沒有疑問吧?接下來我們再嘗試選取出進貨單價不是 2800
元(purchase_price <> 2800
)的記錄(代碼清單 26)。
代碼清單 26 選取出進貨單價不是 2800 元的記錄
SELECT product_name, purchase_price
FROM Product
WHERE purchase_price <> 2800;
執行結果:
product_name | purchase_price
---------------+---------------
T恤衫 | 500
打孔器 | 320
高壓鍋 | 5000
擦菜板 | 790
執行結果中並沒有“叉子”和“圓珠筆”。這兩條記錄由於進貨單價不明(NULL
),因此無法判定是不是 2800
元。
那如果想選取進貨單價為 NULL
的記錄的話,條件表達式該怎麼寫呢?歷經一番苦思冥想後,用“purchase_price = NULL
”試了試,還是一條記錄也取不出來。
代碼清單 27 錯誤的 SELECT 語句(一條記錄也取不出來)
SELECT product_name, purchase_price
FROM Product
WHERE purchase_price = NULL;
執行結果:
即使使用 <>
運算符也還是無法選取出 NULL
的記錄 [9]。因此,SQL 提供了專門用來判斷是否為 NULL
的 IS NULL
運算符。
想要選取 NULL
的記錄時,可以像代碼清單 28 那樣來書寫條件表達式。
代碼清單 28 選取 NULL 的記錄
SELECT product_name, purchase_price
FROM Product
WHERE purchase_price IS NULL;
執行結果:
product_name | purchase_price
---------------+---------------
叉子 |
圓珠筆 |
反之,希望選取不是 NULL
的記錄時,需要使用 IS NOT NULL
運算符(代碼清單 29)。
代碼清單 29 選取不為 NULL 的記錄
SELECT product_name, purchase_price
FROM Product
WHERE purchase_price IS NOT NULL;
執行結果:
product_name | purchase_price
---------------+---------------
T恤衫 | 500
打孔器 | 320
運動T恤 | 2800
菜刀 | 2800
高壓鍋 | 5000
擦菜板 | 790
法則 9
希望選取
NULL
記錄時,需要在條件表達式中使用IS NULL
運算符。希望選取不是
NULL
的記錄時,需要在條件表達式中使用IS NOT NULL
運算符。
除此之外,對 NULL 使用比較運算符的方法還有很多,詳細內容將會在接下來的 SQL 常用的函數 中進行介紹。
三、邏輯運算符
本節重點
通過使用邏輯運算符,可以將多個查詢條件進行組合。
通過
NOT
運算符可以生成“不是~”這樣的查詢條件。兩邊條件都成立時,使用
AND
運算符的查詢條件才成立。只要兩邊的條件中有一個成立,使用
OR
運算符的查詢條件就可以成立。值可以歸結為真(
TRUE
)和假(FALSE
)其中之一的值稱為真值。比較運算符在比較成立時返回真,不成立時返回假。但是,在 SQL 中還存在另外一個特定的真值——不確定(
UNKNOWN
)。
將根據邏輯運算符對真值進行的操作及其結果彙總成的表稱為真值表。
SQL 中的邏輯運算是包含對真、假和不確定進行運算的三值邏輯。
3.1 NOT 運算符
在第 2 節中我們介紹過,想要指定“不是~”這樣的否定條件時,需要使用 <>
運算符。除此之外還存在另外一個表示否定,並且使用範圍更廣的運算符 NOT
。
NOT
不能單獨使用,必須和其他查詢條件組合起來使用。例如,選取出銷售單價(sale_price
)大於等於 1000
元的記錄的 SELECT
語句如下所示(代碼清單 30)。
代碼清單 30 選取出銷售單價大於等於 1000 元的記錄
SELECT product_name, product_type, sale_price
FROM Product
WHERE sale_price >= 1000;
執行結果:
product_name | product_type | sale_price
---------------+--------------+------------
T恤衫 | 衣服 | 1000
運動T恤 | 衣服 | 4000
菜刀 | 廚房用具 | 3000
高壓鍋 | 廚房用具 | 6800
向上述 SELECT
語句的查詢條件中添加 NOT
運算符之後的結果如下所示(代碼清單 31)。
代碼清單 31 向代碼清單 30 的查詢條件中添加 NOT 運算符
SELECT product_name, product_type, sale_price
FROM Product
WHERE NOT sale_price >= 1000;
執行結果:
product_name | product_type | sale_price
---------------+--------------+-------------
打孔器 | 辦公用品 | 500
叉子 | 廚房用具 | 500
擦菜板 | 廚房用具 | 880
圓珠筆 | 辦公用品 | 100
明白了嗎?通過否定銷售單價大於等於 1000
元(sale_price >= 1000
)這個查詢條件,就可以選取出銷售單價小於 1000
元的商品。
也就是說,代碼清單 31 中 WHERE
子句指定的查詢條件,與代碼清單 32 中 WHERE
子句指定的查詢條件(sale_price < 1000
)是等價的 [10](圖 5)。
代碼清單 32 WHERE 子句的查詢條件和代碼清單 31 中的查詢條件是等價的
SELECT product_name, product_type
FROM Product
WHERE sale_price < 1000;

圖 5 使用 NOT 運算符時查詢條件的變化
通過以上的例子大家可以發現,不使用 NOT
運算符也可以編寫出效果相同的查詢條件。不僅如此,不使用 NOT
運算符的查詢條件更容易讓人理解。
使用 NOT
運算符時,我們不得不每次都在腦海中進行“大於等於 1000 元以上這個條件的否定就是小於 1000 元”這樣的轉換。
雖然如此,但是也不能完全否定 NOT
運算符的作用。在編寫複雜的 SQL 語句時,經常會看到 NOT
的身影。
這裡只是希望大家瞭解 NOT
運算符的書寫方法和工作原理,同時提醒大家不要濫用該運算符。
法則 10
NOT 運算符用來否定某一條件,但是不能濫用。
3.2 AND 運算符和 OR 運算符
到目前為止,我們看到的每條 SQL 語句中都只有一個查詢條件。但在實際使用當中,往往都是同時指定多個查詢條件對數據進行查詢的。
例如,想要查詢“商品種類為廚房用具、銷售單價大於等於 3000
元”或“進貨單價大於等於 5000
元或小於 1000
元”的商品等情況。
在 WHERE
子句中使用 AND
運算符或者 OR
運算符,可以對多個查詢條件進行組合。
AND
運算符在其兩側的查詢條件都成立時整個查詢條件才成立,其意思相當於“並且”。
OR
運算符在其兩側的查詢條件有一個成立時整個查詢條件都成立,其意思相當於“或者” [11]。
例如,從 Product
表中選取出“商品種類為廚房用具(product_type = '廚房用具'
),並且銷售單價大於等於 3000
元(sale_price >= 3000
)的商品”的查詢條件中就使用了 AND
運算符(代碼清單 33)。
代碼清單 33 在 WHERE 子句的查詢條件中使用 AND 運算符
SELECT product_name, purchase_price
FROM Product
WHERE product_type = '廚房用具'
AND sale_price >= 3000;
執行結果:
product_name | purchase_price
---------------+---------------
菜刀 | 2800
高壓鍋 | 5000
該查詢條件的文氏圖如圖 6 所示。左側的圓圈代表符合查詢條件“商品種類為廚房用具”的商品,右側的圓圈代表符合查詢條件“銷售單價大於等於 3000
元”的商品。
兩個圓重合的部分(同時滿足兩個查詢條件的商品)就是通過 AND
運算符能夠選取出的記錄。
文氏圖
將集合(事物的聚集)的關係通過更加容易理解的圖形進行可視化展示。

圖 6 AND 運算符的工作效果圖
選取出“商品種類為廚房用具(product_type = '廚房用具'
),或者銷售單價大於等於 3000
元(sale_price >= 3000
)的商品”的查詢條件中使用了 OR
運算符(代碼清單 34)。
代碼清單 34 在 WHERE 子句的查詢條件中使用 OR 運算符
SELECT product_name, purchase_price
FROM Product
WHERE product_type = '廚房用具'
OR sale_price >= 3000;
執行結果:
product_name | purchase_price
---------------+---------------
運動T恤 | 2800
菜刀 | 2800
高壓鍋 | 5000
叉子 |
擦菜板 | 790
還是讓我們來看看查詢條件的文氏圖吧(圖 7)。
包含在左側的圓圈(商品種類為廚房用具的商品)或者右側的圓圈(銷售單價大於等於 3000
元的商品)中的部分(兩個查詢條件中滿足任何一個的商品)就是通過 OR
運算符能夠取出的記錄。

圖 7 OR 運算符的工作效果圖
通過文氏圖可以方便地確認由多個條件組合而成的複雜的 SQL 語句的查詢條件,大家可以多多加以利用。
法則 11
多個查詢條件進行組合時,需要使用
AND
運算符或者OR
運算符。
3.3 通過括弧強化處理
接下來我們嘗試書寫稍微複雜一些的查詢條件。例如,使用下麵的查詢條件對 Product
表進行查詢的 SELECT
語句,其 WHERE
子句的條件表達式該怎麼寫呢?
“商品種類為辦公用品”
並且
“登記日期是 2009 年 9 月 11 日或者 2009 年 9 月 20 日”
滿足上述查詢條件的商品(product_name
)只有“打孔器”。
把上述查詢條件原封不動地寫入 WHERE
子句中,得到的 SELECT
語句似乎就可以滿足需求了(代碼清單 35)。
代碼清單 35 將查詢條件原封不動地寫入條件表達式
SELECT product_name, product_type, regist_date
FROM Product
WHERE product_type = '辦公用品'
AND regist_date = '2009-09-11'
OR regist_date = '2009-09-20';
讓我們馬上執行上述 SELECT
語句試試看,會得到下麵這樣的錯誤結果:
product_name | product_type | regist_date
---------------+--------------+------------
T恤衫 | 衣服 | 2009-09-20
打孔器 | 辦公用品 | 2009-09-11
菜刀 | 廚房用具 | 2009-09-20
叉子 | 廚房用具 | 2009-09-20
不想要的 T 恤衫
、菜刀
和 叉子
也被選出來了,真是頭疼呀。到底為什麼會得到這樣的結果呢?
這是 AND
運算符優先於 OR
運算符所造成的。代碼清單 35 中的條件表達式會被解釋成下麵這樣。
「product_type = '辦公用品' AND regist_date = '2009-09-11'」
OR
「regist_date = '2009-09-20'」
也就是,
“商品種類為辦公用品,並且登記日期是 2009 年 9 月 11 日”
或者
“登記日期是 2009 年 9 月 20 日”
這和想要指定的查詢條件並不相符。想要優先執行 OR
運算符時,可以像代碼清單 36 那樣使用半形括弧 ()
將 OR
運算符及其兩側的查詢條件括起來。
代碼清單 36 通過使用括弧讓 OR 運算符先於 AND 運算符執行
SELECT product_name, product_type, regist_date
FROM Product
WHERE product_type = '辦公用品'
AND ( regist_date = '2009-09-11'
OR regist_date = '2009-09-20');
執行結果:
product_name | product_type | regist_date
---------------+--------------+------------
打孔器 | 辦公用品 | 2009-09-11
這樣就選取出了想要得到的“打孔器”。
法則 13
AND
運算符的優先順序高於OR
運算符。想要優先執行OR
運算符時需要使用括弧。
3.4 邏輯運算符和真值
本節介紹的三個運算符 NOT
、AND
和 OR
稱為邏輯運算符。這裡所說的邏輯就是對真值進行操作的意思。真值就是值為 真(TRUE) 或 假(FALSE) 其中之一的值 [12]。
上一節介紹的比較運算符會把運算結果以真值的形式進行返回。比較結果成立時返回真(TRUE
),比較結果不成立時返回假(FALSE
)[13]。
例如,對於 purchase_price >= 3000
這個查詢條件來說,由於 product_name
列為 '運動 T 恤'
的記錄的 purchase_price
列的值是 2800
,因此會返回假(FALSE
),而 product_name
列為 '高壓鍋'
的記錄的 purchase_price
列的值是 5000
,所以返回真(TRUE
)。
邏輯運算符對比較運算符等返回的真值進行操作。AND
運算符兩側的真值都為真時返回真,除此之外都返回假。
OR
運算符兩側的真值只要有一個不為假就返回真,只有當其兩側的真值都為假時才返回假。
NOT
運算符只是單純的將真轉換為假,將假轉換為真。真值表(truth table)就是對這類操作及其結果進行的總結(表 4)。
表 4 真值表
AND
P | Q | P AND Q |
---|---|---|
真 | 真 | 真 |
真 | 假 | 假 |
假 | 真 | 假 |
假 | 假 | 假 |
OR
P | Q | P OR Q |
---|---|---|
真 | 真 | 真 |
真 | 假 | 真 |
假 | 真 | 真 |
假 | 假 | 假 |
NOT
P | NOT P |
---|---|
真 | 假 |
假 | 真 |
請將表 4 中的 P
和 Q
想象為“銷售單價為 500
元”這樣的條件。邏輯運算的結果只有真和假兩種,對其進行排列組合將會得到 2 × 2 = 4
種結果。
在 SELECT
語句的 WHERE
子句中,通過 AND
運算符將兩個查詢條件連接起來時,會查詢出這兩個查詢條件都為真的記錄。
通過 OR
運算符將兩個查詢條件連接起來時,會查詢出某一個查詢條件為真或者兩個查詢條件都為真的記錄。
在條件表達式中使用 NOT
運算符時,會選取出查詢條件為假的記錄(反過來為真)。
雖然表 4 中的真值表只是使用一個邏輯運算符時得到的結果,但即使使用兩個以上的邏輯運算符連接三個以上的查詢條件,通過反覆進行邏輯運算求出真值,不論多複雜的條件也可以得到相應的結果。
表 5 就是根據之前例子中的查詢條件“商品種類為辦公用品”,並且“登記日期是 2009 年 9 月 11 日 或者 2009 年 9 月 20 日”(product_type = '辦公用品' AND (regist_date = '2009-09-11' OR regist_date = '2009-09-20')
)做成的真值表。
表 5 查詢條件為 P AND(Q OR R)的真值表
P AND (Q OR R)
P | Q | R | Q OR R | P AND (Q OR R) |
---|---|---|---|---|
真 | 真 | 真 | 真 | 真 |
真 | 真 | 假 | 真 | 真 |
真 | 假 | 真 | 真 | 真 |
真 | 假 | 假 | 假 | 假 |
假 | 真 | 真 | 真 | 假 |
假 | 真 | 假 | 真 | 假 |
假 | 假 | 真 | 真 | 假 |
假 | 假 | 假 | 假 | 假 |
P:商品種類為辦公用品
Q:登記日期是 2009 年 9 月 11 日
R:登記日期是 2009 年 9 月 20 日
Q OR
R:登記日期是 2009 年 9 月 11 日 或者 2009 年 9 月 20 日
P AND
(Q OR
R):商品種類為辦公用品,並且,登記日期是 2009 年 9 月 11 日 或者 2009 年 9 月 20 日
代碼清單 36 中的 SELECT
語句,查詢出了唯一滿足 P AND
(Q OR
R) 為真的記錄“打孔器”。
法則 14
通過創建真值表,無論多複雜的條件,都會更容易理解。
專欄
邏輯積與邏輯和
將表 4 的真值表中的真變為
1
、假變為0
,意外地得到了下述規則。表 A 真為 1、假為 0 的真值表
AND(邏輯積)
P Q 積 P AND Q 1 1 1×1 1 1 0 1×0 0 0 1 0×1 0 0 0 0×0 0 OR(邏輯和)
P Q 和 P OR Q 1 1 1+1 1 1 0 1+0 1 0 1 0+1 1 0 0 0+0 0 NOT
P 反轉 NOT P 1 1 → 0 0 0 0 → 1 1
NOT
運算符並沒有什麼特別的改變,但是AND
運算的結果與乘法運算(積),OR
運算的結果與加法運算(和)的結果卻是一樣的。嚴格來說,此處的
1+1=1
與通常的整數運算並不相同。只是因為真值中只存在0
和1
兩種情況,所以才有了這樣的結果。<