SQL基礎-第5章 複雜查詢
5-1 檢視
檢視和表
檢視的優點
- 第一點是由於檢視無需儲存資料,因此可以節省儲存裝置的容量
- 表中儲存的是實際資料,而檢視中儲存的是從表中取出資料所使用的SELECT語句
- 第二個優點就是可以將頻繁使用的 SELECT 語句儲存成檢視,這樣
就不用每次都重新書寫了
-- 通過檢視等SELECT語句儲存資料
SELECT product_type, SUM(sale_price), SUM(purchase_price)
FROM Product
GROUP BY product_type;
建立檢視的方法
建立檢視的CREATE VIEW語句
CREATE VIEW 檢視名稱(<檢視列名1>, <檢視列名2>, ……) AS <SELECT語句>
使用檢視的查詢
在 FROM 子句中使用檢視的查詢,通常有如下兩個步驟:
- 首先執行定義檢視的 SELECT 語句
- 根據得到的結果,再執行在 FROM 子句中使用檢視的 SELECT 語句
應該避免在檢視的基礎上建立檢視。多重檢視會降低 SQL 的效能
CREATE VIEW ProductSum (product_type, cnt_product) AS SELECT product_type, COUNT(*) FROM Product GROUP BY product_type; SELECT product_type, cnt_product FROM ProductSum; CREATE VIEW ProductSumJim (product_type, cnt_product) AS SELECT product_type, cnt_product FROM ProductSum WHERE product_type = '辦公用品'; SELECT product_type, cnt_product FROM ProductSumJim;
檢視的限制①-——定義檢視時不能使用ORDER-BY子句
因為檢視和表一樣, 資料行都是沒有順序的
-- 不能像這樣定義檢視
CREATE VIEW ProductSum (product_type, cnt_product)
AS
SELECT product_type, COUNT(*)
FROM Product
GROUP BY product_type
ORDER BY product_type;
SELECT * FROM ProductSum;
檢視的限制②-——對檢視進行更新
沒有聚合又沒有結合的SELECT語句
-- 對 ProductSum 檢視執行如下 INSERT 語句 INSERT INTO ProductSumNew VALUES ('電器製品', 5); -- 可以更新的檢視 CREATE VIEW ProductJim (product_id, product_name, product_type, sale_price, purchase_price, regist_date) AS SELECT * FROM Product WHERE product_type = '辦公用品'; -- 向檢視中新增資料行 INSERT INTO ProductJim VALUES ('0009', '印章', '辦公用品', 95, 10, '2009-11-30'); SELECT * FROM ProductJim; SELECT * FROM Product;
刪除檢視
DROP VIEW 檢視名稱(<檢視列名1>, <檢視列名2>, ……)
-- 刪除檢視
DROP VIEW ProductSum;
5-2 子查詢
子查詢和檢視
子查詢就是將用來定義檢視的SELECT語句直接用於FROM子句當中
子查詢作為內層查詢會首先執行。
檢視ProductSum和確認用的SELECT語句
-- 根據商品種類統計商品數量的檢視
CREATE VIEW ProductSum (product_type, cnt_product)
AS
SELECT product_type, COUNT(*)
FROM Product
GROUP BY product_type;
-- 確認建立好的檢視
SELECT product_type, cnt_product
FROM ProductSum;
-- 在FROM子句中直接書寫定義檢視的SELECT語句
SELECT product_type, cnt_product
FROM ( SELECT product_type, COUNT(*) AS cnt_product
FROM Product
GROUP BY product_type ) AS ProductSum;
SELECT語句的執行順序
① 首先執行 FROM 子句中的 SELECT 語句(子查詢)
SELECT product_type, COUNT(*) AS cnt_product
FROM Product
GROUP BY product_type;
② 根據①的結果執行外層的 SELECT 語句
SELECT product_type, cnt_product
FROM ProductSum;
嘗試增加子查詢的巢狀層數
SELECT product_type, cnt_product
FROM (SELECT *
FROM (SELECT product_type, COUNT(*) AS cnt_product
FROM Product
GROUP BY product_type) AS ProductSum
WHERE cnt_product = 4) AS ProductSum2;
子查詢的名稱
為子查詢設定名稱時需要使用 AS 關鍵字
標量子查詢
標量子查詢就是返回單一值的子查詢
-- 在WHERE子句中不能使用聚合函式
SELECT product_id, product_name, sale_price
FROM Product
WHERE sale_price > AVG(sale_price);
-- 選取出銷售單價( sale_price)高於全部商品的平均單價的商品
SELECT product_id, product_name, sale_price
FROM Product
WHERE sale_price > (SELECT AVG(sale_price)
FROM Product);
標量子查詢的書寫位置
能夠使用常數或者列名的地方,無論是 SELECT 子句、 GROUP BY 子句、 HAVING 子句,還是 ORDER BY 子句,幾乎所有的地方都可以使用。
-- 在SELECT子句中使用標量子查詢
SELECT product_id,
product_name,
sale_price,
(SELECT AVG(sale_price)
FROM Product) AS avg_price
FROM Product;
-- 在HAVING子句中使用標量子查詢
SELECT product_type, AVG(sale_price)
FROM Product
GROUP BY product_type
HAVING AVG(sale_price) >
(SELECT AVG(sale_price) FROM Product);
使用標量子查詢時的注意事項
該子查詢絕對不能返回多行結果
-- 由於不是標量子查詢,因此不能在SELECT子句中使用
SELECT product_id,
product_name,
sale_price,
(SELECT AVG(sale_price)
FROM Product
GROUP BY product_type) AS avg_price
FROM Product;
5-3 關聯子查詢
普通的子查詢和關聯子查詢的區別
在細分的組內進行比較時,需要使用關聯子查詢。
-- 按照商品種類計算平均價格
SELECT AVG(sale_price)
FROM Product
GROUP BY product_type;
-- 通過關聯子查詢按照商品種類對平均銷售單價進行比較
SELECT product_type, product_name, sale_price
FROM Product AS P1
WHERE sale_price > (SELECT AVG(sale_price) FROM Product AS P2 WHERE P1.product_type = P2.product_type GROUP BY product_type);
關聯子查詢也是用來對集合進行切分的
結合條件一定要寫在子查詢中
子查詢內部設定的關聯名稱,只能在該子查詢內部使用。換句話說,就是“內部可以看到外部,而外部看不到內部”
-- 錯誤的關聯子查詢書寫方法
SELECT product_type, product_name, sale_price
FROM Product AS P1
WHERE P1.product_type = P2.product_type
AND sale_price > (SELECT AVG(sale_price)
FROM Product AS P2
GROUP BY product_type);
子查詢內的關聯名稱的有效範圍
SELECT product_type, product_name, sale_price
FROM Product AS P1
WHERE sale_price>(SELECT AVG(sale_price)
FROM Product AS P2
WHERE P1.product_type=P2.product_type
GROUP BY product_type);
練習題
5.1 創建出滿足下述三個條件的檢視(檢視名稱為 ViewPractice5_1)。使用 Product(商品)表作為參照表,假設表中包含初始狀態的 8 行資料。
- 條件 1: 銷售單價大於等於 1000 日元。
- 條件 2: 登記日期是 2009 年 9 月 20 日。
- 條件 3: 包含商品名稱、銷售單價和登記日期三列。
CREATE VIEW ViewPractice5_1 (product_name, sale_price,regist_date)
AS
SELECT product_name, sale_price, regist_date
FROM Product
WHERE sale_price >= 1000
AND regist_date = '2009-09-20';
SELECT * FROM ViewPractice5_1;
5.2 向習題 5.1 中建立的檢視 ViewPractice5_1 中插入如下資料,會得到什麼樣的結果呢?
INSERT INTO ViewPractice5_1 VALUES ('刀子', 300, '2009-11-02');
會發生錯誤。
解答:對檢視的更新歸根結底是對檢視所對應的表進行更新。因此,該 INSERT 語句實質上和下面的 INSERT 語句相同。
INSERT INTO Product (product_id, product_name, product_type, sale_price, purchase_price, regist_date)
VALUES (NULL, '刀子', NULL, 300, NULL, '2009-11-02');
5.3 請根據如下結果編寫 SELECT 語句,其中 sale_price_all 列為全部商品的平均銷售單價。
product_id | product_name | product_type | sale_price | sale_price_all |
---|---|---|---|---|
0001 | T恤衫 | 衣服 | 1000 | 2097.5000000000000000 |
0002 | 打孔器 | 辦公用品 | 500 | 2097.5000000000000000 |
0003 | 運動T恤 | 衣服 | 4000 | 2097.5000000000000000 |
0004 | 菜刀 | 廚房用具 | 3000 | 2097.5000000000000000 |
0005 | 高壓鍋 | 廚房用具 | 6800 | 2097.5000000000000000 |
0006 | 叉子 | 廚房用具 | 500 | 2097.5000000000000000 |
0007 | 擦菜板 | 廚房用具 | 880 | 2097.5000000000000000 |
0008 | 圓珠筆 | 辦公用品 | 100 | 2097.5000000000000000 |
SELECT product_id, product_name, product_type, sale_price,
(SELECT AVG(sale_price) FROM Product) as sale_price_all
FROM Product;
5.4 請根據習題 5.1 中的條件編寫一條 SQL 語句,建立一幅包含如下資料的檢視(名稱為 AvgPriceByType)。
執行結果
product_id | product_name | product_type | sale_price | avg_sale_price |
---|---|---|---|---|
0001 | T恤衫 | 衣服 | 1000 | 2500.0000000000000000 |
0002 | 打孔器 | 辦公用品 | 500 | 300.0000000000000000 |
0003 | 運動T恤 | 衣服 | 4000 | 2500.0000000000000000 |
0004 | 菜刀 | 廚房用具 | 3000 | 2795.0000000000000000 |
0005 | 高壓鍋 | 廚房用具 | 6800 | 2795.0000000000000000 |
0006 | 叉子 | 廚房用具 | 500 | 2795.0000000000000000 |
0007 | 擦菜板 | 廚房用具 | 880 | 2795.0000000000000000 |
0008 | 圓珠筆 | 辦公用品 | 100 | 300.0000000000000000 |
提示:其中的關鍵是avg_sale_price列。與習題5.3不同,這裡需要計算出的是各商品種類的平均銷售單價。這與5-3節中使用關聯子查詢所得到的結果相同。
也就是說,該列可以使用關聯子查詢進行建立。問題就是應該在什麼地方使用這個關聯子查詢
CREATE VIEW AvgPriceByType(product_id, product_name, product_type, sale_price, avg_sale_price)
AS
SELECT product_id, product_name, product_type, sale_price,
(SELECT AVG(sale_price) FROM Product p2 GROUP BY product_type HAVING p1.product_type = p2.product_type)
FROM Product p1;
CREATE VIEW AvgPriceByType
AS
SELECT product_id, product_name, product_type, sale_price,
(SELECT AVG(sale_price)
FROM Product p2
GROUP BY product_type
HAVING p1.product_type = p2.product_type) as avg_sale_price
FROM Product p1;
SELECT * FROM AvgPriceByType;