1. 程式人生 > 其它 >SQL基礎-第5章 複雜查詢

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 子句中使用檢視的查詢,通常有如下兩個步驟:

  1. 首先執行定義檢視的 SELECT 語句
  2. 根據得到的結果,再執行在 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;