iT邦幫忙

2022 iThome 鐵人賽

DAY 16
1

查詢語句有時候會碰到一點麻煩,例如資料表的結構可能無法只透過查詢獲得平均值,這時候就需要透過子查詢的技巧來處理這個問題。(通常是技巧之一,解決方案可能還有像是VIEW、WINDOWS等等的方式,這取決於資料結構或是個人喜好又或是團隊規範而定,子查詢正是其中一種方法)

建立本日範例資料表

CREATE TABLE ebooks (
	id SERIAL PRIMARY KEY,
	name VARCHAR(300) NOT NULL,
	price INT NOT NULL,
	discount NUMERIC(3,2) NOT NULL,
	subject VARCHAR(300) NOT NULL
);

CREATE TABLE ebook_orders (
	id SERIAL PRIMARY KEY,
	user_name VARCHAR(300) NOT NULL,
	ebook_id INT REFERENCES ebooks (id)
);

建立本日範例資料

INSERT INTO ebooks (name,price,discount,subject)
VALUES ('PostgreSQL新手入門',450, 0.79,'資料庫'),
('SQL語法查詢新手入門',480,0.85,'資料庫'),
('PHP新手入門',560, 1,'網站開發'),
('Go新手入門',470, 1,'網站開發'),
('Node新手入門',440, 0.79,'網站開發'),
('VUE新手入門',470, 0.9,'前端開發'),
('REACT新手入門',680, 0.79,'前端開發'),
('TypeScript新手入門',560, 1,'前端開發'),
('轉職新手入門',380, 1, '職涯發展'),
('切版新手入門',580, 0.6, '網頁設計'),
('資安這條路新手入門',450, 0.79, '資訊安全'),
('白帽新手入門',360, 1, '資訊安全'),
('設計模式新手入門',780, 1, '軟體架構'),
('UML新手入門',440, 0.79, '軟體工程'),
('單元測試新手入門',520, 1, '軟體測試'),
('TDD新手入門',400, 0.6, '軟體測試');

INSERT INTO ebook_orders (user_name,ebook_id)
VALUES ('王大空',1),
('林小明',1),
('陳小瓜',1),
('黃小佩',2),
('張大通',3),
('簡大志',4),
('劉三妹',10),
('柯哲文',6);

子查詢要放在哪裡?

子查詢一共有四個地方可以放,放在不同地方有不同的限制,下例是個極端的例子,不太有什麼意義,只是我們可以理解一下子查詢的用法大概長什麼意思,請往下繼續看。

SELECT 
	e1.name
	-- 子查詢出來的單一值可加入要顯示出來的欄位資料
	(SELECT COUNT(name) FROM ebooks)

-- 子查詢出來的值形成新的資料表
FROM (SELECT * FROM ebooks ) AS e1

-- 子查詢出來的值形成新的資料表
JOIN (SELECT * FROM ebooks ) AS e2 ON e1.id = e2.id

-- 子查詢出來的值用於欄位判斷
WHERE e1.id IN (SELECT id FROM ebooks);

子查詢 IN SELECT

限制 : 在SELECT語句中使用子查詢,其值必需為單一值。
使用情境 : 需要快速取得某個值但不想寫太複雜的JOIN或其他SQL語句時候。
舉例說明 : 希望搜尋出來的資料能夠顯示該書籍比平均價格便宜多少錢

實用度 : (5/3)

SELECT name, 
	   price,
	   ((SELECT AVG(price) FROM ebooks)-price) AS 比平均價便宜多少
FROM ebooks;

子查詢 IN FROM

限制 : 必需幫子查詢設置別名。
使用情境 : 需要事先對資料表進行篩選或計算時。
舉例說明 : 希望先對原始表格計算出特價後的價格,再把資料給出來。

實用度 : (5/4)

SELECT *
FROM (
	SELECT name AS 名稱, 
	(price * discount) AS 特價,
	subject AS 分類
	FROM ebooks
) AS 書籍特價
WHERE 特價 < 450
ORDER BY 特價;

子查詢 IN JOIN

限制 : 必需幫子查詢設置別名。
使用情境 : 當你有一些過濾條件希望在JOIN前就表明而不是在JOIN之後。
舉例說明 : 找出書籍編號1的所有訂單者是誰,想握個手手。

實用度 : (5/2)

SELECT *
FROM ebooks AS e
JOIN (
	SELECT * 
	FROM ebook_orders AS eo
	WHERE eo.ebook_id = 1
) AS eb1
ON eb1.ebook_id = e.id;

但是JOIN的情境沒有使用子查詢反而更加簡潔。

SELECT *
FROM ebooks AS e
JOIN ebook_orders AS eo
ON eo.ebook_id = e.id
WHERE eo.ebook_id = 1;

子查詢 IN WHERE

限制 : 必需幫子查詢設置別名。
使用情境 : 有些情況下比JOIN還要簡潔,有些時候則不,常互相替代運用。
舉例說明 : 找出前端書籍。

實用度 : (5/5)

SELECT * 
FROM ebook_orders AS eo
WHERE ebook_id IN (
	SELECT id
	FROM ebooks
	WHERE subject IN ('前端開發','網頁設計')
);

JOIN VS SUBQUERY 各有千秋

SELECT eo.id, user_name, ebook_id
FROM ebooks AS e
JOIN ebook_orders AS eo
ON eo.ebook_id = e.id
WHERE e.subject IN ('前端開發','網頁設計');

子查詢的秘密招式(ALL / SOME / ANY)

也不是秘密啦,阿就是ALL/SOME/ANY,不過網上中文資源跟討論較少,給人一種神祕的感覺,嗯…在掰下去大家拳頭都硬了,直接進入主題。

	SELECT price 
	FROM ebooks 
	WHERE subject = '前端開發';
price
470
680
560

ALL

全部條件都要滿足才滿足

SELECT name, price
FROM ebooks
WHERE price > ALL (
	SELECT price 
	FROM ebooks 
	WHERE subject = '前端開發'
);
name price
設計模式新手入門 780

SOME / ANY (等義詞)

任一條件滿足即滿足

SELECT name, price, subject 
FROM ebooks
WHERE price > SOME (
	SELECT price 
	FROM ebooks 
	WHERE subject = '前端開發'
);
name price
SQL語法查詢新手入門 480
PHP新手入門 560
REACT新手入門 680
TypeScript新手入門 560
切版新手入門 580
設計模式新手入門 780
單元測試新手入門 520

上一篇
Day 20 聚合函數
下一篇
Day 22 日期與時間
系列文
資料庫新手入門--以PostgreSQL為例30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言