SQL FAQ

SQL 更新於 Oct 9, 2026

1. 有兩張表 a(3k 筆)、b(4k 筆),

select a.Color, b.Size from a cross join b 的結果有幾筆?

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
		  	   ..........

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 差在哪?

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 

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])

14. SQL join 與子查詢?

-> 看情況。(效能 vs 可讀性)

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

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 ; 

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;

19. 什麼時候用 right、left、inner、full outer 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. 資料庫建模的題目?

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?

26. lead 與 lag?

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)?

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?

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 !!!