1. 程式人生 > >5 復雜查詢

5 復雜查詢

優點 結果 頻繁 時也 scalar 如果 規則 聚合 更新

5 復雜查詢

5-1 視圖

究竟視圖是什麽呢?如果用一句話概述的話,就是“從SQL的角度來看視圖就是一張表”。實際上,在SQL語句中並不需要區分哪些是表,哪些是視圖。

那麽視圖和表到底右什麽不同呢?區別只有一個,那就是“是否保存了實際的數據”。

通常,我們在創建表時,會通過INSERT語句將數據保存到數據庫之中。而數據庫中的數據實際上會被保存到計算機的存儲設備(通常是硬盤)中。因此,我們通過SELECT語句查詢數據時,實際上就是從存儲設備(硬盤)中讀取數據,進行各種計算之後,再將結果返回給用戶這樣一個過程。

但是使用視圖時並不會將數據保存到存儲設備之中,而且也不會將數據保存到其他任何地方。實際上視圖保存的是SELECT語句。我們從視圖中讀取數據時,視圖會在內部執行該SELECT語句並創建出一張臨時表。

視圖的優點大體有兩點:第一點是由於視圖無需保存數據,因此可以節省存儲設備的容量。第二個優點就是可以將頻繁使用的select語句保存成視圖,這樣就不用每次都重新書寫了。而且還可以大大提高效率。而且視圖中的數據會隨著原表的變化自動更新。

法則5-1

表中存儲的是實際數據,而視圖中保存的是從表中取出數據所使用的SELECT語句。

法則5-2

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

創建視圖的方法

/*

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

*/

DELETE FROM shohin;

SELECT * FROM shohin;

INSERT INTO shohin VALUES (‘0001‘, ‘T恤衫‘, ‘衣服‘, 1000, 500, ‘2009-09-20‘);

INSERT INTO shohin VALUES (‘0002‘, ‘打孔器‘, ‘辦公用品‘, 500, 300, ‘2009-09-11‘);

INSERT INTO shohin VALUES (‘0003‘, ‘運動T恤‘, ‘衣服‘, 4000, 2800, NULL);

INSERT INTO shohin VALUES (‘0004‘, ‘菜刀‘, ‘廚房用具‘, 3000, 2800, ‘2009-09-20‘);

INSERT INTO shohin VALUES (‘0005‘, ‘高壓鍋‘, ‘廚房用具‘, 6800, 5000, ‘2009-01-15‘);

INSERT INTO shohin VALUES (‘0006‘, ‘叉子‘, ‘廚房用具‘, 500, NULL, ‘2009-09-20‘);

INSERT INTO shohin VALUES (‘0007‘, ‘擦菜飯‘, ‘廚房用具‘, 880, 790, ‘2008-04-28‘);

INSERT INTO shohin VALUES (‘0008‘, ‘圓珠筆‘, ‘辦公用品‘, 100, NULL, ‘2009-11-11‘);

CREATE VIEW shohinSum (shohin_bunrui, cnt_shohin) AS

SELECT shohin_bunrui, COUNT(*) FROM shohin GROUP BY shohin_bunrui;

SELECT * FROM shohinSum;

在FROM子句中使用視圖的查詢,通常有如下兩個步驟:

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

也就是說,使用視圖的查詢通常需要執行2條以上的SELECT語句。

多重視圖。

使用視圖的查詢

CREATE VIEW shohinSumJim (shohin_bunrui, cnt_shohin) AS SELECT shohin_bunrui, cnt_shohin

FROM shohinSum WHERE shohin_bunrui = ‘辦公用品‘;

雖然語法上沒有錯誤,但是我們還是應該盡量避免在視圖的基礎上創建視圖。這是因為對於多數DBMS來說,多重視圖會降低SQL的性能。

法則5-3

應該避免在視圖的基礎上創建視圖

為什麽不能使用ORDER BY子句呢?這是因為視圖和表一樣,數據行都是沒有順序的。實際上,有些DBMS在定義視圖的語句中是可以使用ORDER BY子句的,但是這並不是通用的語法。因此,在定義視圖時請不要使用ORDER BY。

法則5-4

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

法則5-5

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

刪除視圖

-- DROP VIEW 視圖名稱(<視圖列名1>, <視圖列名2>, ......);

DROP VIEW shohinSum;

5-2 子查詢

我們先來復習一下視圖的概念,視圖並不是用來保存數據的,而是通過保存讀取數據的SELECT語句的方法來為用戶提供便利的工具。反之,子查詢就是將用來定義視圖的SELECT語句直接用於FROM子句當中。

子查詢和視圖

CREATE VIEW shohinSum (shohin_bunrui, cnt_shohin) AS SELECT shohin_bunrui, COUNT(*) FROM shohin GROUP BY shohin_bunrui;

-- 下面代碼也能實現此功能 這裏的內層 SELECT子句為 FROM子句中的select子句

SELECT shohin_bunrui, cnt_shohin FROM (SELECT shohin_bunrui, COUNT(*) AS cnt_shohin FROM shohin GROUP BY shohin_bunrui) AS shohinSum;

/*

首先執行from子句中的select語句(子查詢)

SELECT shohin_bunrui, COUNT(*) AS cnt_shohin FROM shohin GROUP BY shohin_bunrui;

根據上面的結果執行外層的select語句

SELECT shohin_bunrui, cnt_shohin FROM shohinSum;

*/

SELECT shohin_bunrui, cnt_shohin FROM shohinSum;

SELECT shohin_bunrui, cnt_shohin FROM (SELECT * FROM (SELECT shohin_bunrui, COUNT(*) AS cnt_shohin FROM shohin GROUP BY shohin_bunrui) AS shohinSum WHERE cnt_shohin = 4) AS shohinSum2;

法則5-6

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

子查詢的名稱:原則上子查詢必須設定名稱,因此請大家盡量從處理內容的角度出發為子查詢設定恰當的名稱。為子查詢設定名稱時需要使用AS關鍵字,該關鍵字有時也可以省略。

標量就是單一的意思,在數據庫之外的領域也經常使用。

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

標量子查詢(scalar subquery)

-- 在where子句中使用標量子查詢

-- 在where子句中不能使用聚合函數

-- 雖然這樣的select語句看上去能夠滿足我們的要求,但是由於在where子句中不能使用聚合函數,因此這樣的select語句是錯誤的。

SELECT shohin_id, shohin_mei, hanbai_tanka FROM shohin WHERE hanbai_tanka > AVG(hanbai_tanka);

-- 可以使用下面的代碼實現

-- 計算平均銷售單價的標量子查詢

SELECT AVG(hanbai_tanka) FROM shohin;

SELECT shohin_id, shohin_mei, hanbai_tanka FROM shohin WHERE hanbai_tanka > (SELECT AVG(hanbai_tanka) FROM shohin);

-- 在SELECT子句中使用標量子查詢

SELECT shohin_id, shohin_mei, hanbai_tanka, (SELECT AVG(hanbai_tanka) FROM shohin) AS avg_tanka FROM shohin;

SELECT shohin_bunrui, AVG(hanbai_tanka) FROM shohin GROUP BY shohin_bunrui;

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

SELECT shohin_bunrui, AVG(hanbai_tanka) FROM shohin GROUP BY shohin_bunrui HAVING AVG(hanbai_tanka) > (SELECT AVG(hanbai_tanka) FROM shohin);

法則5-7

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

標量子查詢的書寫位置並不僅僅局限於WHERE子句中,通常任何可以使用單一值的位置都可以使用。也就是說,能夠使用常數或者列名的地方,無論是SELECT子句,GROUP BY子句,HAVING子句,還是ORDER BY子句,幾乎所有的地方都可以使用。

特別需要註意的是該子查詢絕對不能返回多行結果。也就是說如果子查詢返回了多行結果,那麽它就不再是標量子查詢,而僅僅是一個普通的子查詢了。

5-3 關聯子查詢

在WHERE子句中使用子查詢時,該子查詢的結果必須是單一的。

普通的子查詢和關聯子查詢的區別

-- 普通的子查詢和關聯子查詢的區別

SELECT AVG(hanbai_tanka) FROM shohin GROUP BY shohin_bunrui;

-- 發生錯誤的子查詢

SELECT shohin_id, shohin_mei, hanbai_tanka FROM shohin WHERE hanbai_tanka > (SELECT AVG(hanbai_tanka)

FROM shohin GROUP BY shohin_bunrui);

-- 正確的關聯子查詢書寫方法

SELECT shohin_id, shohin_mei, hanbai_tanka FROM shohin AS S1 WHERE hanbai_tanka >

(SELECT AVG(hanbai_tanka) FROM shohin AS S2 WHERE S1.shohin_bunrui = S2.shohin_bunrui GROUP BY shohin_bunrui);

-- 錯誤的關聯子查詢書寫方法:將關聯條件移到子查詢之外 該書寫方法究竟違法了什麽規則呢?那就是關聯名稱的作用域。也就是說關聯名稱存在一個有效作用域的限制。

SELECT shohin_bunrui, shohin_mei, hanbai_tanka FROM shohin AS S1 WHERE S1.shohin_bunrui = S2.shohin_bunrui AND hanbai_tanka >

(SELECT AVG(hanbai_tanka) FROM shohin AS S2 GROUP BY shohin_bunrui);

-- 正確的關聯子查詢書寫方法

SELECT shohin_bunrui, shohin_mei, hanbai_tanka FROM shohin AS S1 WHERE hanbai_tanka >

(SELECT AVG(hanbai_tanka) FROM shohin AS S2 WHERE S1.shohin_bunrui = S2.shohin_bunrui GROUP BY shohin_bunrui);

這裏起到關鍵作用的就是在子查詢中添加的WHERE子句的條件。

請大家一定不要忘記關聯名稱具有一定的有效作用域。如前所述,SQL是按照先內層子查詢後外層查詢的順序來執行的。這樣,子查詢執行結束時會留下執行結果,作為抽出源的S2表其實已經不存在了。因此,在執行外層查詢時,由於S2表已經不存在了,就會返回“不存在使用該名稱的表”這樣的錯誤。

法則5-8

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

5 復雜查詢