SQL FAQ
1. 有兩張表 a(3k 筆)、b(4k 筆),
select a.Color, b.Size from a cross join b 的結果有幾筆?
- 3k * 4k = 12k
- https://www.essentialsql.com/cross-join-introduction/
- http://www.sqlservertutorial.net/sql-server-basics/sql-server-cross-join/
- (cross join 示意圖)

2. SQL 裡 full outer Join 和 Union 差在哪?
-
Full outer Join:把兩張表接起來,給你 1) 對得上的紀錄 + 2) 右表對不上的紀錄 + 3) 左表對不上的紀錄
- 例如 table A (a,b,c)、table B (c,d)。
-
Full outer join:回傳 table A、table B 的所有欄位(不重複)
- ->
select A*, B.* from A full outer join B on A.c = B.c
- ->
text
# output :
a | b | c1 | c2 | d
a1 b1 c11 c21 d1
a2 b2 c12 c22 d2
a3 b3 c13 c23 d3
.......

- Union:把兩個查詢的結果合併成單一個結果集。
- 例如 table A (a,b,c)、table B (c,d)。
- ->
select A.a, A.b from A union select B.c, B.d from B
text
# output :
col1 | col2
a1 b1
a2 b2
c1 d2
c2 d2
..........

- 簡單說:
- Full outer join:接所有
欄位(不管 A、B 有沒有相同欄位) - Union :接所有
列(前提是 A、B 有相同欄位) - Cross join :A 與 B 所有欄位的
組合
- Full outer join:接所有
- https://www.quora.com/What-is-the-difference-between-full-outer-join-and-union-in-SQL
- https://www.solutionfactory.in/posts/Difference-between-Join-And-Union-in-SQL
3. 找出某一季的訂單?(不要寫死)
sql
-- postgre
SELECT extract(QUARTER
FROM order_timestamp) AS QUARTER,
count(*)
FROM orders
GROUP BY 1;
sql
-- mysql
SELECT QUARTER(order_timestamp) AS QUARTER,
count(*)
FROM orders
GROUP BY 1;
4. 一個查詢就同時取出值與帶條件的最大值?
sql
-- postgre
SELECT commit_timestamp,
max(commit_timestamp) max_commit_time,
(SELECT max(commit_timestamp)
FROM git_commit
WHERE commit_timestamp >= '2019-01-01'
AND commit_timestamp <= '2019-12-31' ) AS max_commit_time_in_timeslot
FROM git_commit
GROUP BY 1
LIMIT 10 ;
5. 取出大於平均值的資料?
sql
-- postgre
-- V1
WITH user_commit_count AS
(SELECT user_id,
count(*) AS commit_count
FROM git_commit
GROUP BY 1),
avg_commit AS
(SELECT avg(commit_count)::int AS avg_commit_count
FROM user_commit_count)
SELECT user_id,
commit_count,
(SELECT *
FROM avg_commit)
FROM user_commit_count
WHERE commit_count >
(SELECT avg_commit_count
FROM avg_commit)
LIMIT 30;
6. 產生 1 -> 100 的整數清單(遞迴 CTE)?
sql
-- postgre
WITH RECURSIVE my_num AS
(SELECT 1 AS seqnum
UNION ALL SELECT seqnum + 1
FROM my_num
WHERE seqnum < 100 )
SELECT seqnum
FROM my_num;
7. 給定 movie、actor 兩張表(多對多關係),請設計資料模型與查詢,回報某個 movie-id/movie-name 有幾位演員?
- 多對多關係需要一張
關聯(橋接)表。絕對不要存一串用逗號隔開的 actor id —— 那沒辦法建索引、沒辦法 join、也沒辦法加約束。
sql
CREATE TABLE movie (
movie_id INT PRIMARY KEY,
name VARCHAR(200) NOT NULL,
release_yr SMALLINT
);
CREATE TABLE actor (
actor_id INT PRIMARY KEY,
name VARCHAR(200) NOT NULL
);
-- the junction table: one row per (movie, actor) pair
CREATE TABLE movie_actor (
movie_id INT NOT NULL REFERENCES movie(movie_id),
actor_id INT NOT NULL REFERENCES actor(actor_id),
role VARCHAR(200), -- attributes OF THE RELATIONSHIP live here
PRIMARY KEY (movie_id, actor_id) -- composite PK = dedupe + the forward index
);
-- serves the reverse lookup "which films did this actor appear in?"
-- (a separate statement, so this DDL runs on PostgreSQL as well as MySQL)
CREATE INDEX idx_movie_actor_actor ON movie_actor (actor_id, movie_id);
sql
-- number of actors for a given movie id
SELECT COUNT(*) AS actor_cnt
FROM movie_actor
WHERE movie_id = 123; -- index-only: no join needed
-- ... by movie name
SELECT m.movie_id, m.name, COUNT(ma.actor_id) AS actor_cnt
FROM movie m
LEFT JOIN movie_actor ma ON ma.movie_id = m.movie_id -- LEFT JOIN -> movies with 0 actors still report 0
WHERE m.name = 'The Matrix'
GROUP BY m.movie_id, m.name;
- 面試官在聽的幾個點
- 複合主鍵
(movie_id, actor_id)同時做到去重與正向查詢的索引;反向查詢需要它自己的索引 - 用
LEFT JOIN+COUNT(ma.actor_id)(不是COUNT(*)),這樣沒有演員的電影算 0 而不是 1 - 電影名稱不唯一 —— id 才是真正的 key,名稱只是查詢上的方便
- 如果這個數字一直被讀,就把它
反正規化成movie.actor_cnt,用觸發器或應用程式維護,並接受寫入的代價
- 複合主鍵
8. 怎麼解「多對多」的資料庫設計問題?
9. union 和 union all 哪個快?
union all比較快,因為它不會去處理可能的重複。
9’. union 和 union all 差在哪?
Union會把重複的紀錄去掉(同樣的紀錄只出現一次)Union all全部都回傳,包含重複的資料(直接合併)- https://dataschool.com/learn-sql/what-is-the-difference-between-union-and-union-all/
- http://sqlqna.blogspot.com/2013/08/union-union-all.html
10. SQL 裡使用變數的例子?
sql
# mysql
SELECT
@x AS x,
@y AS y,
@x + 1 AS x_Plus_1,
@x := @x + 1 AS updated_x
FROM
(SELECT @x := 0, @y := 1) INIT;
11. 刪掉表裡重複的紀錄?
sql
DELETE a
FROM TABLE a,
TABLE b
WHERE a.id = b.id
AND a.timestamp > b.timestamp
- 追問:如果是「整列都重複」的情況呢?
sql
-- build the table
CREATE TABLE IF NOT EXISTS test( id int, age int);
TRUNCATE TABLE test;
INSERT INTO test VALUES (1,1);
INSERT INTO test VALUES (2,2);
INSERT INTO test VALUES (3,3);
INSERT INTO test VALUES (3,3);
INSERT INTO test VALUES (3,3);
INSERT INTO test VALUES (3,3);
SELECT * FROM test;
-- delete duplicated
-- V1
DELETE
FROM test
WHERE id IN
(SELECT id
FROM
(SELECT
id,
age,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY id) AS order_
FROM test) sub
WHERE order_ > 2 )
-- delete duplicated
-- V2
DELETE FROM test
WHERE id IN (
SELECT calc_id FROM (
SELECT MAX(id) AS calc_id
FROM test
GROUP BY id, age
HAVING COUNT(id) > 1
) temp
);
12. SQL 怎麼處理 NULL?
IS NULL與= NULL
sql
-- select data where SALARY is not null
SELECT ID, NAME, AGE, ADDRESS, SALARY
FROM CUSTOMERS
WHERE SALARY IS NOT NULL;
-- select data where SALARY is null
SELECT ID, NAME, AGE, ADDRESS, SALARY
FROM CUSTOMERS
WHERE SALARY IS NULL;
- COALESCE
sql
-- COALESCE
SELECT StudentId, StudentName, Department,
Semester_I, Semester_II, Semester_III,
COALESCE(Semester_I, Semester_II, Semester_III, 0) AS COALESCE_Result
FROM StudentDetails
-- above SQL is as same as below
-- i.e. COALESCE(a, b, c, 0)
-- -> if a is not null then a
-- -> if b is not null then b
-- -> if c is not null then c
-- -> else 0
SELECT StudentId, StudentName, Department,
Semester_I, Semester_II, Semester_III,
COALESCE(Semester_I, Semester_II, Semester_III, 0) AS COALESCE_Result,
CASE
WHEN Semester_I IS NOT NULL THEN Semester_I
WHEN Semester_II IS NOT NULL THEN Semester_II
WHEN Semester_III IS NOT NULL THEN Semester_III
ELSE 0
END CASE_Result
FROM StudentDetails
- 和 Null 相加
sql
-- Method 1 : IFNULL
SELECT IFNULL(NULL, 0) + value;
-- Method 2 : COALESCE
SELECT COALESCE(NULL, value);
- 任何值 + NULL = 任何值?
sql
SELECT NULL + 100;
-- NULL
- NULL != NULL、NULL = NULL 的結果是什麼?
sql
SELECT NULL != NULL;
-- NULL
SELECT NULL = NULL;
-- NULL
- https://www.codeproject.com/Articles/1017058/Handle-NULL-in-SQL-Server
- https://www.tutorialspoint.com/sql/sql-null-values.htm
13. 什麼時候用子查詢?怎麼用?
-
什麼時候
- 子查詢用來取出資料,讓主查詢拿它當條件,進一步限縮要撈的資料。
- 子查詢可以出現在:
- SELECT 子句
- FROM 子句
- WHERE 子句
- 內層查詢會先於外層執行,這樣內層的結果才能傳給外層。
-
怎麼用
sql
-- sub-query pattern
SELECT column_name [, column_name ]
FROM table1 [, table2 ]
WHERE column_name OPERATOR
(SELECT column_name [, column_name ]
FROM table1 [, table2 ]
[WHERE])
- https://www.tutorialspoint.com/sql/sql-sub-queries.htm
- https://www.w3resource.com/sql/subqueries/understanding-sql-subqueries.php
- https://www.sqlservertutorial.net/sql-server-basics/sql-server-subquery/
14. SQL join 與子查詢?
-> 看情況。(效能 vs 可讀性)
- https://www.quora.com/Which-is-faster-joins-or-subqueries
- https://stackoverflow.com/questions/2577174/join-vs-sub-query
- https://stackoverflow.com/questions/3856164/sql-joins-vs-sql-subqueries-performance
15. 說明/示範 SQL 的視窗函數(window function)?
->
- 視窗函數是對「與目前這一列有某種關聯的一組列」做計算
- 語法樣板
sql
SELECT
aggre_func() OVER (PARTITION BY ... ORDER BY ...),
ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...),
RANK() OVER (PARTITION BY ... ORDER BY ...),
LAG(timestamp, 1) OVER (PARTITION BY ... ORDER BY ...) AS prev_record,
RANK() OVER (PARTITION BY timestamp, ORDER BY count(product_id)) AS rank # NOTE : rank can order by count
...
- 範例
sql
-- example 1
SELECT duration_seconds,
SUM(duration_seconds) OVER (ORDER BY start_time) AS running_total
FROM tutorial.dc_bikeshare_q1_2012
-- example 2
SELECT start_terminal,
duration_seconds,
SUM(duration_seconds) OVER
(PARTITION BY start_terminal ORDER BY start_time)
AS running_total
FROM tutorial.dc_bikeshare_q1_2012
WHERE start_time < '2012-01-08'
-- example 3
SELECT start_terminal,
duration_seconds,
SUM(duration_seconds) OVER
(PARTITION BY start_terminal) AS running_total,
COUNT(duration_seconds) OVER
(PARTITION BY start_terminal) AS running_count,
AVG(duration_seconds) OVER
(PARTITION BY start_terminal) AS running_avg
FROM tutorial.dc_bikeshare_q1_2012
WHERE start_time < '2012-01-08'
- ROW_NUMBER()
- ROW_NUMBER() 就跟它的名字一樣 —— 顯示某一列的編號。它從 1 開始,依照視窗語句裡 ORDER BY 的順序編號。
- 範例
sql
SELECT start_terminal,
start_time,
duration_seconds,
ROW_NUMBER() OVER (PARTITION BY start_terminal
ORDER BY start_time)
AS row_number
FROM tutorial.dc_bikeshare_q1_2012
WHERE start_time < '2012-01-08'
- RANK() 與 DENSE_RANK()
- RANK():和 ROW_NUMBER() 稍有不同。例如你依 start_time 排序時,某些站點可能有兩趟車的出發時間完全一樣。這種情況下它們會拿到相同的名次,而 ROW_NUMBER() 會給它們不同的號碼。在下面的查詢裡可以看到 start_terminal 31000 的第 4、5 筆 —— 它們都拿到名次 4,而下一筆會拿到 6:
- RANK() 會讓相同的列都拿到名次 2,然後跳過 3 和 4,下一筆就是 5
- DENSE_RANK() 一樣讓相同的列都拿到 2,但下一列會是 3 —— 名次不會被跳過。
- 範例
sql
SELECT start_terminal,
duration_seconds,
RANK() OVER (PARTITION BY start_terminal
ORDER BY start_time)
AS rank
FROM tutorial.dc_bikeshare_q1_2012
WHERE start_time < '2012-01-08'
- LAG 與 LEAD()
- 用 LAG 或 LEAD 可以做出「從其他列取值」的欄位 —— 你只要指定要從哪個欄位取、以及要往前/往後幾列。LAG 往前取,LEAD 往後取
- 範例
sql
-- example 1
SELECT start_terminal,
duration_seconds,
LAG(duration_seconds, 1) OVER
(PARTITION BY start_terminal ORDER BY duration_seconds) AS lag,
LEAD(duration_seconds, 1) OVER
(PARTITION BY start_terminal ORDER BY duration_seconds) AS lead
FROM tutorial.dc_bikeshare_q1_2012
WHERE start_time < '2012-01-08'
ORDER BY start_terminal, duration_seconds
-- example 2
SELECT start_terminal,
duration_seconds,
duration_seconds -LAG(duration_seconds, 1) OVER
(PARTITION BY start_terminal ORDER BY duration_seconds)
AS difference
FROM tutorial.dc_bikeshare_q1_2012
WHERE start_time < '2012-01-08'
ORDER BY start_terminal, duration_seconds
- NTILE()
- 視窗函數也能算出某一列落在哪一個百分位(或四分位,或任何其他切分)。語法是 NTILE(桶數)。這時 ORDER BY 決定要依哪個欄位來切
- 範例
sql
SELECT start_terminal,
duration_seconds,
NTILE(4) OVER
(PARTITION BY start_terminal ORDER BY duration_seconds)
AS quartile,
NTILE(5) OVER
(PARTITION BY start_terminal ORDER BY duration_seconds)
AS quintile,
NTILE(100) OVER
(PARTITION BY start_terminal ORDER BY duration_seconds)
AS percentile
FROM tutorial.dc_bikeshare_q1_2012
WHERE start_time < '2012-01-08'
ORDER BY start_terminal, duration_seconds
- 也可以把視窗定義成別名(定義 window alias)
- 範例
sql
SELECT start_terminal,
duration_seconds,
NTILE(4) OVER ntile_window AS quartile,
NTILE(5) OVER ntile_window AS quintile,
NTILE(100) OVER ntile_window AS percentile
FROM tutorial.dc_bikeshare_q1_2012
WHERE start_time < '2012-01-08'
WINDOW ntile_window AS
(PARTITION BY start_terminal ORDER BY duration_seconds)
ORDER BY start_terminal, duration_seconds
- https://mode.com/sql-tutorial/sql-window-functions/
- https://www.postgresql.org/docs/8.4/functions-window.html
16. SQL 的 Count(*) 與 Count(1)?
-
差別
- 對
非 null計數Count(col)只會數該欄位裡非 Null的值,忽略 NULL。(欄位裡有值的筆數)
- 對
全部計數Count(1)不管怎樣都會數所有的值(含 NULL),不忽略 NULL。(全部紀錄數,含 Null)- Count(
*) 和 Count(1)相同(考慮表裡所有欄位),也不忽略 NULL
- 對
-
效能
- 若該欄位是主鍵,count(col) 比 count(1) 快
- 若該欄位不是主鍵,count(1) 比 count(col) 快
- 若表上沒有主鍵,count(1) 比 count(col) 快
- 若表上有主鍵,count(col) 最快
-
參考
-
範例
sql
-- build table
CREATE table if not exists user(id int, age int);
truncate table user;
INSERT INTO user values (1, 10);
INSERT INTO user values (1, 10);
INSERT INTO user values (1, 10);
INSERT INTO user values (1, 10);
INSERT INTO user values (1, 10);
INSERT INTO user values (NULL, 10);
INSERT INTO user values (1, NULL);
-- select
SELECT * FROM user;
-- mysql> SELECT * FROM user;
-- +------+------+
-- | id | age |
-- +------+------+
-- | 1 | 10 |
-- | 1 | 10 |
-- | 1 | 10 |
-- | 1 | 10 |
-- | 1 | 10 |
-- | NULL | 10 |
-- | 1 | NULL |
-- +------+------+
-- 7 rows in set (0.00 sec)
-- select
SELECT COUNT(id), COUNT(*) ,COUNT(1) FROM user;
-- mysql> SELECT COUNT(id), COUNT(*) ,COUNT(1) FROM user;
-- +-----------+----------+----------+
-- | COUNT(id) | COUNT(*) | COUNT(1) |
-- +-----------+----------+----------+
-- | 6 | 7 | 7 |
-- +-----------+----------+----------+
-- 1 row in set (0.00 sec)
17. SQL 的 Where 與 Having?
->
-
having作用在彙總後的資料上 -
除了 SELECT,WHERE 也可以搭配 UPDATE 與 DELETE;HAVING 則只能用在 SELECT 查詢裡。
-
WHERE用來過濾列,套用在每一列上;而HAVING用來過濾 SQL 裡的群組。 -
語法層面的一個差別是:WHERE 寫在 GROUP BY 之前,HAVING 寫在 GROUP BY 之後。
-
當帶彙總函數的 SELECT 同時用到 WHERE 與 HAVING 時,WHERE 會先套用在個別列上,只有通過條件的列才會拿去分組。分完組之後,再由 HAVING 依條件過濾群組。
-
範例
sql
-- example 1
SELECT d.DEPT_NAME, count(e.EMP_NAME) as NUM_EMPLOYEE, avg(e.EMP_SALARY) as AVG_SALARY FROM Employee e,
Department d WHERE e.DEPT_ID=d.DEPT_ID AND EMP_SALARY > 5000 GROUP BY d.DEPT_NAME HAVING AVG_SALARY > 7000;
-- example 2
update DEPARTMENT set DEPT_NAME="NewSales" WHERE DEPT_ID=1 ;
- https://www.geeksforgeeks.org/having-vs-where-clause-in-sql/
- https://javarevisited.blogspot.com/2013/08/difference-between-where-vs-having-clause-SQL-databse-group-by-comparision.html
18. SQL 的 case when.. then.. else end CASE 運算式?
->
- 語法樣板
sql
CASE expression
WHEN expression_1 THEN result_1
WHEN expression_2 THEN result_2
...
WHEN expression_n THEN result_n
[ ELSE else_result ]
END
- 範例
sql
SELECT
title,
rating,
CASE
WHEN (rating >= 1 AND rating < 2) THEN 'Not so good'
WHEN (rating >= 2 AND rating < 3) THEN 'Limited useful information'
WHEN (rating >= 3 AND rating < 4) THEN 'Good book, but nothing special'
WHEN (rating >= 4 AND rating < 5) THEN 'Incredbly special'
WHEN rating = 5 THEN 'Life changing. Must Read.'
ELSE
'No rating yet'
END AS comment
FROM
books
ORDER BY
title;
- https://www.db2tutorial.com/db2-basics/db2-case-expression/
- https://chartio.com/resources/tutorials/how-to-use-if-then-logic-in-sql-server/
19. 什麼時候用 right、left、inner、full outer join?

-
OUTER join:在 inner join 的基礎上,把對不上的列也放進最終結果
-
LEFT outer:把寫在 join 條件左邊那張表裡對不上的列也帶進來
- LEFT outer join = INNER JOIN + 左表對不上的列
-
RIGHT outer:把寫在 join 條件右邊那張表裡對不上的列也帶進來
- RIGHT OUTER join = INNER JOIN + 右表對不上的列
20. SQL 的彙總函數
->
text
1) Count()
2) Sum()
3) Avg()
4) Min()
5) Max()
sql
SELECT COUNT()
SELECT Count(Distinct Salary)
SELECT Avg(salary) -- Sum(salary) / count(salary) = 310/5
SELECT Avg(Distinct salary) -- = sum(Distinct salary) / Count(Distinct Salary)
21. 說明/示範 SQL 的階層查詢?
- 「階層查詢」=走訪一張
自我參照的表(員工 → 主管、分類 → 上層分類、留言 → 上層留言)。可攜的工具是遞迴 CTE。
sql
CREATE TABLE employee (
emp_id INT PRIMARY KEY,
name VARCHAR(100),
manager_id INT REFERENCES employee(emp_id) -- self reference; NULL for the CEO
);
sql
-- everyone up to 10 levels under manager 1, with their depth in the tree
WITH RECURSIVE org AS (
-- 1) ANCHOR: where the walk starts
SELECT emp_id, name, manager_id, 1 AS lvl, CAST(name AS CHAR(1000)) AS path
FROM employee
WHERE emp_id = 1
UNION ALL
-- 2) RECURSIVE part: join the table back onto the rows found so far
SELECT e.emp_id, e.name, e.manager_id, o.lvl + 1, CONCAT(o.path, ' > ', e.name)
FROM employee e
JOIN org o ON e.manager_id = o.emp_id
WHERE o.lvl < 10 -- depth guard: a cycle otherwise loops forever
)
SELECT lvl, path FROM org ORDER BY path;
text
lvl | path
----+---------------------------
1 | Ada
2 | Ada > Brian
3 | Ada > Brian > Chen
2 | Ada > Dara
- 注意事項
- 用
UNION ALL(不是UNION)—— 每一輪都去重很貴,而且在這裡通常是錯的 - 把 join 反過來寫(
o.manager_id = e.emp_id)就能改成往上走到根節點 - 一定要限制遞迴 —— 上面那個深度守衛就是用來擋掉環的,同時也把結果限制在 10 層。
如果想偵測環而不是限制深度,就用 id 組出路徑(
,1,4,9,), 然後測NOT (id_path LIKE '%,' || e.emp_id || ',%'); 用名字比對會在兩個人同名、或一個名字包含另一個名字時出錯。 PostgreSQL 14+ 與 SQL Server 也有內建的CYCLE子句 - 各家也有自己的捷徑 —— Oracle 的
CONNECT BY PRIOR、SQL Server 的hierarchyid—— 但遞迴 CTE 是 ANSI 標準,MySQL 8+、Postgres、SQL Server 與 SQLite 都能跑 - 讀取為主時的替代方案:
物化路徑(存/1/4/9/)、巢狀集合,或閉包表(每一對祖先-後代一列)—— 全都是拿寫入成本換O(1)的子樹讀取 - 也見下面第 23 題
- 用
22. 資料庫建模的題目?
- https://www.toptal.com/data-modeling/interview-questions
- https://mindmajix.com/data-modeling-interview-questions
- https://www.indeed.com/career-advice/interviewing/data-modeling-interview-questions
23. SQL 遞迴 CTE
sql
WITH recursive cte(columns_name)
AS (
initial_query
UNION ALL
recursive_query
)
SELECT *
FROM cte
24. 說明 <> 的意思?
sql
# https://docs.microsoft.com/en-us/sql/t-sql/language-elements/not-equal-to-transact-sql-traditional?view=sql-server-ver15
# Not Equal To (Transact SQL)
# Compares two expressions (a comparison operator). When you compare nonnull expressions, the result is TRUE if the left operand is not equal to the right operand; otherwise, the result is FALSE. If either or both operands are NULL, see the topic SET ANSI_NULLS (Transact-SQL).
expression <> expression
25. char 與 varchar?
- https://stackoverflow.com/questions/1885630/whats-the-difference-between-varchar-and-char#:~:text=CHAR is fixed length and,to store the actual text.&text=The char is a fixed,variable-length character data type.
- CHAR 是固定長度,VARCHAR 是可變長度。CHAR 每一筆都用掉同樣的儲存空間,VARCHAR 則只用實際存放文字所需的空間。
- VARCHAR:
- 存放可變長度的字串,是最常見的字串型別。它通常比固定長度型別省空間,因為它只用需要的空間(值比較短就用比較少空間)。例外是用 ROW_FORMAT=FIXED 建的 MyISAM 表,它每一列在磁碟上都佔固定空間,因此可能浪費。VARCHAR 因為省空間而有助於效能。
- CHAR:
- 是固定長度:MySQL 一律為指定的字元數配足空間。存放 CHAR 值時,MySQL 會把尾端空白去掉。(MySQL 4.1 與更舊的版本裡 VARCHAR 也是如此 —— CHAR 與 VARCHAR 邏輯上完全一樣,只差在儲存格式。)比較時會視需要用空白補齊。
26. lead 與 lag?
- https://riptutorial.com/sql/example/27455/lag-and-lead
- LEAD 函數提供結果集中目前這一列「之後」那些列的資料。例如在 SELECT 裡,你可以拿目前這列的值和下一列的值做比較。
- 範例:
sql
# LC 1790 : Biggest Window Between Visits
WITH cte1 AS
(SELECT user_id,
coalesce(lead(visit_date) OVER (PARTITION BY user_id
ORDER BY visit_date), '2021-01-01') AS lead_visit_date,
visit_date
FROM UserVisits),
cte2 AS
(SELECT user_id,
max(datediff(lead_visit_date, visit_date)) AS max_diff
FROM cte1
GROUP BY user_id)
SELECT *
FROM cte2
ORDER BY user_id
27. 說明外鍵(foreign key, fk)?
- https://www.w3schools.com/sql/sql_foreignkey.asp
- https://www.cockroachlabs.com/docs/stable/foreign-key.html
- https://b-l-u-e-b-e-r-r-y.github.io/post/ForeignKey/
- 也叫做
FOREIGN KEY 約束 - 外鍵約束用來阻止那些會破壞表與表之間連結的操作。
- 帶外鍵的那張表叫子表,帶主鍵的那張表叫被參照表或父表。
- 任何 CRUD 操作都會受外鍵約束限制
- -> 擋掉那些會破壞(有外鍵的)表之間資料一致性的非法操作
- -> 也就是說,我們得對
所有帶同一個外鍵約束的表一起做操作
28. 說明 data mart 與 data warehouse 的差別?


29. Rank() 的範例?
sql
# LC 1831
WITH cte AS
(SELECT transaction_id,
RANK() OVER (PARTITION BY DATE(DAY)
ORDER BY amount DESC) AS rank
FROM transactions)
SELECT transaction_id
FROM cte
WHERE rank = 1
ORDER BY transaction_id
30. EXISTS 的範例?
sql
# V1
SELECT * FROM table_a
WHERE EXISTS
(SELECT * FROM table_b WHERE table_b.id=table_a.id);
# is equal to below
# V2
SELECT * FROM table_a
WHERE id
in (SELECT id FROM table_b);
31. 說明 cross join?
- https://www.fooish.com/sql/cross-join.html
- Cross join 會回傳
給定欄位與表之間所有可能的列組合 - 注意:下面幾個 SQL 彼此等價
sql
# sql 1
SELECT table_column1, table_column2...
FROM table_name1
CROSS JOIN table_name2;
# sql 2
SELECT table_column1, table_column2...
FROM table_name1, table_name2;
# sql 3
SELECT table_column1, table_column2...
FROM table_name1
JOIN table_name2;
32. Left join 的範例?
- 注意:要用
IS NULL(而不是「= null」)
sql
# LC 0183
### NOTE :
# -> SHOULD BE o.CustomerId is NULL
# -> RATHER THAN o.CustomerId = NULL
SELECT
c.Name AS Customers
FROM
Customers c
left join
Orders o
on c.Id = o.CustomerId
WHERE
o.CustomerId is NULL -- note this condition
33. 多欄位的 where in
sql
-- LC 184 Department Highest Salary
/* V0 */
SELECT d.name AS Department,
e.name AS Employee,
e.salary AS Salary
FROM Employee e
INNER JOIN Department d ON e.DepartmentId = d.Id
-- NOTE : below where has 2 columns !!!
WHERE (e.DepartmentId,
e.salary) in
(SELECT DepartmentId,
max(Salary) AS salary
FROM Employee GROUP
BY DepartmentId)
34. 中等偏難的資料分析師 SQL 面試題
- https://quip.com/2gwZArKuWk7W
- 自我 join 練習題
- MoM(逐月)百分比變化:算出月活躍使用者(MAU)的逐月百分比變化。
text
-- data
| user_id | date |
|---------|------------|
| 1 | 2018-07-01 |
| 234 | 2018-07-02 |
| 3 | 2018-07-02 |
| 1 | 2018-07-02 |
| ... | ... |
| 234 | 2018-10-04 |
sql
WITH mau AS
(
SELECT
/*
* Typically, interviewers allow you to write psuedocode for date functions
* i.e. will NOT be checking if you have memorized date functions.
* Just explain what your function does as you whiteboard
*
* DATE_TRUNC() is available in Postgres, but other SQL date functions or
* combinations of date functions can give you a identical results
* See https://www.postgresql.org/docs/9.0/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC
*/
DATE_TRUNC('month', date) month_timestamp,
COUNT(DISTINCT user_id) mau
FROM
logins
GROUP BY
DATE_TRUNC('month', date)
)
SELECT
/*
* You don't literally need to include the previous month in this SELECT statement.
*
* However, as mentioned in the "Tips" section of this guide, it can be helpful
* to at least sketch out self-joins to avoid getting confused which table
* represents the prior month vs current month, etc.
*/
a.month_timestamp previous_month,
a.mau previous_mau,
b.month_timestamp current_month,
b.mau current_mau,
ROUND(100.0*(b.mau - a.mau)/a.mau,2) AS percent_change
FROM
mau a
JOIN
/*
* Could also have done `ON b.month_timestamp = a.month_timestamp + interval '1 month'`
*/
mau b ON a.month_timestamp = b.month_timestamp - interval '1 month'
- 視窗函數練習題
- 其他中等/困難的 SQL 練習題
35. SQL 依不同欄位分別做遞減與遞增排序
sql
-- LC 580 Count Student Number in Departments
# V1
# https://www.datageekinme.com/general/leetcode/leetcode-sql-580-count-student-number-in-departments/
select
a.dept_name,
coalesce(count(student_id), 0) student_number
from
department a
left join
student b
on
(a.dept_id = b.dept_id)
group by a.dept_name
order by student_number desc, a.dept_name asc; --- NOTE this !!!