學習SQL【6】-複雜查詢

到目前為止,我們學習了表的創建、查詢和更新等資料庫的基本操作方法。現在我們將會在這些基本方法的基礎上,學習一些實際應用的方法。

一:視圖

1:視圖和表

表中存儲的是實際數據,而視圖中保存的是從表中獲取數據所使用的SELECT語句。從SQL的角度來看,視圖和表是一樣的,只是視圖並不存儲數據,而是存儲SELECT語句。

視圖的優點:

視圖的優點大體上有兩點。

● 第一點是由於視圖無需保存數據,因此可以節省存儲設備的容量。

● 第二點是可以將頻繁使用的SELECT語句保存成視圖,這樣就不用每次重新書寫了。

所以應該將經常使用的SELECT語句做成視圖。

2:創建視圖的方法

創建視圖需要使用CREATE VIEW語句,其語法如下:

CREATE VIEW 視圖名稱 <視圖列名1>, <視圖列名2>,... AS <SELECT語句>

注釋:

SELECT語句需要書寫在AS關鍵字之後,SELECT語句中列的排列順序和視圖中列的排列順序相同。

接下來,我們仍然使用最開始創建的Product表為基礎進行下面的演示。Product表的結構和內容如下所示:

product_id | product_name | product_type | sale_price | purchase_price | regist_date------------+--------------+--------------+------------+----------------+------------- 0001 | T | 衣服 | 1000 | 500 | 2017-09-20 0002 | 打孔器 | 辦公用品 | 500 | 320 | 2017-09-11 0003 | 運動T | 衣服 | 4000 | 2800 | 0004 | 菜刀 | 廚房用具 | 3000 | 2800 | 2017-09-20 0005 | 高壓鍋 | 廚房用具 | 6800 | 5000 | 2017-01-15 0006 | 叉子 | 廚房用具 | 500 | | 2017-09-20 0007 | 擦菜板 | 廚房用具 | 880 | 790 | 2016-04-28 0008 | 圓珠筆 | 辦公用品 | 100 | | 2017-11-11(8 行記錄)

下面就讓我們試著創建視圖吧:

--ProductSum視圖 CREATE VIEW ProductSum (product_type, cnt_product) AS SELECT product_type, COUNT(*) FROM Product GROUP BY product_type;

這樣我們就創建了一個名為ProductSum的視圖。

視圖和表一樣,可以書寫在SELECT語句的FROM子句中。

--使用視圖 SELECT product_type, cnt_product FROM ProductSum;

執行結果:

product_type | cnt_product--------------+------------- 衣服 | 2 辦公用品 | 2 廚房用具 | 4(3 行記錄)

使用視圖的查詢的步驟:

① 首先執行定義視圖的SELECT語句。

② 根據得到的結果,再執行在FROM子句中使用視圖的SELECT語句。

還可以創建多重視圖,即是在視圖的基礎上再創建視圖。

--視圖ProductSumJimCREATE VIEW ProductSumJim (product_type, cnt_product) ASSELECT product_type, cnt_product FROM ProductSum WHERE product_type = 辦公用品;

確認創建好的視圖:

SELECT * FROM ProductSumJim;

執行結果:

product_type | cnt_product--------------+------------- 辦公用品 | 2(1 行記錄)

注釋:

● 對大多數DBMS來說,多重視圖會降低SQL性能,所以我們應該避免使用多重視圖。

● 定義視圖不要使用ORDER BY子句。

● 視圖和表需要同時進行更新,因此通過匯總得到的視圖無法進行更新。

不是通過匯總得到的視圖就可以進行更新:

CREATE VIEW ProductJim (product_id, product_name, product_type, sale_price, purchase_price, regist_date) ASSELECT * FROM Product WHERE product_type = 辦公用品;

向視圖中添加數據行:

INSERT INTO ProductJim VALUES (0009, 印章, 辦公用品, 95, 10, 2017-11-30);

確認數據是否已經添加到視圖中:

SELECT * FROM ProductJim;

執行結果:

product_id | product_name | product_type | sale_price | purchase_price | regist_date------------+--------------+--------------+------------+----------------+------------- 0002 | 打孔器 | 辦公用品 | 500 | 320 | 2017-09-11 0008 | 圓珠筆 | 辦公用品 | 100 | | 2017-11-11 0009 | 印章 | 辦公用品 | 95 | 10 | 2017-11-30(3 行記錄)

確認數據是否添加到原表中:

SELECT * FROM Product;

執行結果:

product_id | product_name | product_type | sale_price | purchase_price | regist_date------------+--------------+--------------+------------+----------------+------------- 0001 | T | 衣服 | 1000 | 500 | 2017-09-20 0002 | 打孔器 | 辦公用品 | 500 | 320 | 2017-09-11 0003 | 運動T | 衣服 | 4000 | 2800 | 0004 | 菜刀 | 廚房用具 | 3000 | 2800 | 2017-09-20 0005 | 高壓鍋 | 廚房用具 | 6800 | 5000 | 2017-01-15 0006 | 叉子 | 廚房用具 | 500 | | 2017-09-20 0007 | 擦菜板 | 廚房用具 | 880 | 790 | 2016-04-28 0008 | 圓珠筆 | 辦公用品 | 100 | | 2017-11-11 0009 | 印章 | 辦公用品 | 95 | 10 | 2017-11-30(9 行記錄)

3:刪除視圖

刪除視圖需要使用DROP VIEW語句

例如,刪除視圖ProductSum:

DROP VIEW ProductSum;

但是在PostgreSQL中,由於視圖ProductSum存在關聯視圖ProductSumJim,因此會發生如下錯誤:

錯誤: 無法刪除 視圖 productsum 因為有其它對象倚賴它描述: 視圖 productsumjim 倚賴於 視圖 productsum提示: 使用 DROP .. CASCADE 把倚賴對象一併刪除.

這時可以使用CASCADE選項來刪除關聯視圖:

DROP VIEW ProductSum CASCADE;

我們再次將Product表恢復到初始狀態(8行),因此我們要刪掉剛才添加進的第九行:

DELETE FROM Product WHERE product_id = 0009;

二:子查詢

1:子查詢與視圖

一言以蔽之,子查詢就是一次性視圖(SELECT語句)。與視圖不同,子查詢在SELECT語句執行完畢之後就會消失。

子查詢的特點:將用來定義視圖的SELECT語句直接用於FROM子句中。

例如:

--在FROM子句中直接書寫定義視圖的SELECT語句 SELECT product_type, cnt_product FROM (SELECT product_type, COUNT(*) AS cnt_product FROM Product GROUP BY product_type) AS ProductSum;

兩種方法得到的結果完全相同。

product_type | cnt_product--------------+------------- 衣服 | 2 辦公用品 | 2 廚房用具 | 4(3 行記錄)

注釋:子查詢作為內層查詢會首先執行。

增加子查詢的層數:

由於子查詢的層數原則上沒有限制,因此可以在子查詢的FROM子句中再繼續使用子查詢語句。

--增加子查詢的嵌套層數 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;

執行結果:

product_type | cnt_product--------------+------------- 廚房用具 | 4(1 行記錄)

但是隨著子查詢的層數增加,SQL語句會變得愈發地難以讀懂,所以應該避免使用多層嵌套的子查詢語句。

2:子查詢的名稱

原則上子查詢必須設定名稱。為子查詢設定名稱時需要使用關鍵字AS。

3:標量子查詢

標量就是單一的意思,而標量子查詢則有一個特殊的限制,那就是必須而且只能返回1行1列的結果。

也就是說標量子查詢是返回單一值的子查詢。

在WHERE子句中使用標量子查詢:

比如,我們需要查出銷售單價高於平均銷售單價的商品:

先計算出平均銷售單價:

--計算平均銷售單價的標量子查詢 SELECT AVG(sale_price) FROM Product;

執行結果:

avg----------------------- 2097.5000000000000000(1 行記錄)

然後完整的SQL代碼如下所示:

--選取出銷售單價高於全部商品的平均單價的商品 SELECT product_id, product_name, sale_price FROM Product WHERE sale_price > (SELECT AVG(sale_price) FROM Product);

執行結果:

product_id | product_name | sale_price------------+--------------+------------ 0003 | 運動T | 4000 0004 | 菜刀 | 3000 0005 | 高壓鍋 | 6800(3 行記錄)

4:標量子查詢的書寫位置

能夠使用常數或者列名的地方,無論是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;

執行結果:

product_id | product_name | sale_price | avg_price------------+--------------+------------+----------------------- 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(8 行記錄)

在HAVING子句中使用標量子查詢:

SELECT product_type, AVG(sale_price) FROM Product GROUP BY product_type HAVING AVG(sale_price) > (SELECT AVG(sale_price) FROM Product);

執行結果:

product_type | avg--------------+----------------------- 衣服 | 2500.0000000000000000 廚房用具 | 2795.0000000000000000(2 行記錄)

三:關聯子查詢

1:普通子查詢與關聯子查詢的區別

按此前所學,使用子查詢就能選出銷售單價高於全部商品平均銷售單價的商品,這次我們稍微更改一下需求,選取出各種商品中高於該類商品平均銷售單價的商品。

如果我們使用標量子查詢的方法就會發生錯誤:

SELECT product_type, product_name, sale_price FROM Product WHERE sale_price > (SELECT AVG(sale_price) FROM Product GROUP BY product_type);

發生錯誤的原因就是該子查詢會返回3行結果,並不是標量子查詢,而在WHERE子句中使用子查詢時,必須是標量子查詢。

這個時候就可以使用關聯子查詢來解決上述問題。

--通過關聯子查詢按照商品種類對平均銷售單價進行比較 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);

執行結果:

product_type | product_name | sale_price--------------+--------------+------------ 辦公用品 | 打孔器 | 500 衣服 | 運動T | 4000 廚房用具 | 菜刀 | 3000 廚房用具 | 高壓鍋 | 6800(4 行記錄)

這裡的關鍵就是在子查詢中添加WHERE子句的條件。該條件的意思就是,在同一商品種類中對各個商品的銷售單價和平均單價進行比較。

因此,在細分的組內進行比較時,需要使用關聯子查詢。

2:關聯子查詢也是用來對集合進行切分的

換個角度來看,其實關聯子查詢也和GROUP BY子句一樣,可以對集合進行切分。

3:關聯條件一定要寫在子查詢內

關聯名稱就是像P1,P2這樣作為表的別名的名稱,它存在一個有效的範圍,也就是它的作用域。具體來說,子查詢內部設定的關聯名稱,只能在該子查詢內部使用。

每天學習一點點,每天進步一點點。

推薦閱讀:

七周成為數據分析師:SQL,從熟練到掌握
Mysql優化
分庫分表基礎
資料庫 原理解析
資料庫管理系統(一): 並發控制簡介

TAG:SQL | 資料庫 | 自學編程 |