National Yang Ming Chiao Tung University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try a Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

1

Local AI

--

Uedu Code

--

陽明交大 AQI 69 24°C PM2.5 26
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 08

內建函式

比較與邏輯運算、字串、數學、日期時間與流程控制函式
Interaction實作 25 minute(s)

Learning Objectives

  • 說明 NULL 和任何值比較的結果都是 NULL,改用 IS NULL 或 <=> 判斷
  • 在需要區分大小寫時使用 COLLATE utf8mb4_bin 或 CAST(… AS BINARY)
  • 用 IF、CASE、IFNULL、COALESCE 依條件產生不同的值
  • 使用常用的字串函式,分辨 LENGTH(位元組)與 CHAR_LENGTH(字元)
  • 說明 ROUND 對 DECIMAL 與 FLOAT 的差別,以及除法結果的小數位數
  • 用日期函式計算日期與天數,並避開日期沒加引號的陷阱

在 SELECT 裡使用函式

MySQL 內建數百個函式和運算子,可以在查詢時直接處理資料:把姓名轉成大寫、算出派工距今幾天、 依職稱顯示不同的文字。資料留在資料庫裡處理,程式端就不必把所有資料取回來再逐筆計算。

試用函式時,SELECT 可以不寫 FROM,直接算出一個值:

SELECT 1 + 1, UPPER('mysql'), NOW();

在 mysql 用戶端輸入 HELP 函式名稱(例如 HELP LIKE、HELP DATE_ADD)可以查看說明, 完整清單在 MySQL 8.0 參考手冊的 Functions and Operators 一章。

本課的範例沿用 employee 資料庫。播放器裡的 NOW() 固定為 2026-09-01 09:00:00、伺服器時區是 UTC, 所以每次執行的結果相同;在真實的伺服器上執行,會得到當下的時間。

比較運算子與 NULL

比較運算子的結果是 1(真)、0(假)或 NULL(不知道):

運算子 意思
=、<>(或 !=) 相等、不相等
<、<=、>、>= 大小比較
n BETWEEN a AND b a <= n AND n <= b,包含兩端
n IN (a, b, c) n 是否等於清單裡的某一個值,清單也可以是子查詢
n IS NULL、n IS NOT NULL、ISNULL(n) 是否為 NULL
<=> 可以比較 NULL 的相等:NULL <=> NULL 是 1

NULL 代表「不知道的值」,所以任何值和 NULL 比較,答案都是「不知道」:NULL = NULL 的結果是 NULL,不是 1。 WHERE 只留下條件為 1 的列,WHERE x = NULL 因此永遠找不到資料,要寫 WHERE x IS NULL。

邏輯運算子 AND、OR、NOT、XOR 也遵守同樣的規則(三值邏輯):

  • 0 AND NULL 是 0:只要有一邊是假,不必知道另一邊
  • 1 AND NULL 是 NULL:要看另一邊,但另一邊不知道
  • 1 OR NULL 是 1、0 OR NULL 是 NULL、NOT NULL 是 NULL

課程投影片的 &&、||、! 是 MySQL 自己的寫法,MySQL 8.0 執行時會出現警告 1287(已棄用,將來會移除),請改寫成 AND、OR、NOT。

區分大小寫的比較

第 06 課提過,MySQL 8 預設的定序 utf8mb4_0900_ai_ci 比較字串時不分大小寫,所以 name = 'marketing' 也找得到 Marketing。 需要區分大小寫(例如比對代碼、密碼雜湊)時,舊教材的寫法是在字串前面加 BINARY:

SELECT * FROM department WHERE name = BINARY 'marketing';

這個寫法仍然有效,但從 MySQL 8.0.27 起會出現警告 1287:'BINARY expr' is deprecated。改用下面兩種寫法:

-- 指定區分大小寫的定序(_bin 依字元的編碼逐一比較)
SELECT * FROM department WHERE name = 'marketing' COLLATE utf8mb4_bin;

-- 把字串轉成位元組再比較
SELECT * FROM department WHERE CAST(name AS BINARY) = 'marketing';

在 WHERE 的欄位上套用函式或改定序,MySQL 可能無法使用這個欄位的索引;如果某個欄位經常需要區分大小寫, 建表時直接把欄位的定序設為 utf8mb4_bin 比較好。

NULL、三值邏輯與大小寫

前三個敘述沒有 FROM,直接算出結果。注意 3 NOT IN (1, 2, NULL) 的結果是 NULL:MySQL 無法確定 3 不等於那個 NULL。後面四個查詢比較 marketing 的大小寫:加上 BINARY 之後找不到資料,而且多了一個警告;COLLATE utf8mb4_bin 也區分大小寫,但沒有警告。

想一想:NULL 與比較

1.

下面的敘述會傳回什麼?

SELECT NULL = NULL;
2.

下面的敘述會傳回什麼?

SELECT 3 NOT IN (1, 2, NULL);
3.

在 MySQL 8.0.44 上要找出名稱「完全等於」marketing(小寫)的部門,而且不出現棄用警告,應該怎麼寫?

第 1 次作答

IF、CASE、IFNULL 與 COALESCE

流程控制函式依條件產生不同的值:

函式 作用
IF(條件, 值1, 值2) 條件為真時傳回值1,否則傳回值2
CASE WHEN 條件1 THEN 值1 WHEN 條件2 THEN 值2 ELSE 值3 END 依序檢查條件,傳回第一個成立的值
CASE 欄位 WHEN 值a THEN … WHEN 值b THEN … ELSE … END 欄位等於某個值時傳回對應的結果
IFNULL(x, 預設值) x 是 NULL 時改用預設值
COALESCE(a, b, c, …) 傳回第一個不是 NULL 的值,是 SQL 標準的寫法

CASE 沒有 ELSE 而且條件都不成立時,結果是 NULL。

課程投影片的陷阱:日期沒加引號。 實習 Lab01 有這樣的查詢:

SELECT workdate, CASE
         WHEN workdate < 2000-01-01 THEN 'archived'
         WHEN workdate < 2003-01-01 THEN 'old'
         ELSE 'current'
       END
FROM assignment;

2000-01-01 沒有加引號,MySQL 把它當成算式 2000 − 1 − 1,也就是整數 1998;2003-01-01 則是 2001。 1998 不是合法的日期,MySQL 改把 workdate 當成數字 19990630 來比較,19990630 < 1998 永遠不成立, 所以每一列都是 current。在真實的 MySQL 8.0.44 上,派工日期是 1999、2001、2003 年的三列會得到:

+------------+---------+
| workdate   | age     |
+------------+---------+
| 1999-06-30 | current |
| 2001-11-05 | current |
| 2003-01-20 | current |
+------------+---------+
3 rows in set, 2 warnings (0.00 sec)

兩個警告是 1292 Incorrect date value: '1998' for column 'workdate',以及同樣訊息的 '2001',這是唯一的提示。 日期一律寫成字串 '2000-01-01'。下面的範例先新增兩筆派工,再示範正確的寫法。

依條件產生不同的值

第三個敘述把兩種寫法放在一起:沒加引號的 2000-01-01 是整數 1998,加了引號才是日期。第四個查詢用加了引號的日期,三筆派工才分成 archived、old、current。倒數第二個查詢用 IFNULL 把「沒有派工」的 NULL 換成 0。

字串函式與 LIKE

函式 範例 結果
CONCAT(a, b, …) CONCAT(name, ' (', job, ')') Ben Smith (DBA);任一參數是 NULL,結果就是 NULL
LENGTH(s) LENGTH('資料庫') 9:位元組數,utf8mb4 的中文字通常佔 3 個位元組
CHAR_LENGTH(s) CHAR_LENGTH('資料庫') 3:字元數
SUBSTRING(s, 起點, 長度) SUBSTRING('Research', 5) arch:位置從 1 開始,省略長度就取到最後
LEFT(s, n)、RIGHT(s, n) LEFT('Research', 3) Res
UPPER(s)、LOWER(s) UPPER('nora') NORA
TRIM(s)、LTRIM、RTRIM TRIM(' hi ') hi:去掉前後的空白
REPLACE(s, 舊, 新) REPLACE('Telco Inc', 'Inc', 'Ltd') Telco Ltd
STRCMP(a, b) STRCMP('cat', 'dog') -1:a 排在 b 前面;相同是 0,排在後面是 1

檢查中文姓名、標題長度時要用 CHAR_LENGTH;VARCHAR(30) 的 30 也是字元數。 STRCMP 依照定序比較,所以 STRCMP('Cat', 'cat') 是 0。

LIKE 做萬用字元比對:% 代表任意個字元(包括 0 個),_ 代表剛好一個字元。 投影片備註提到的 ? 在 MySQL 的 LIKE 裡沒有特殊意義,就是問號本身。

WHERE name LIKE '%research%'   -- 包含 research(預設不分大小寫)
WHERE name LIKE '_o%'          -- 第二個字是 o
WHERE code LIKE '100\%'        -- 要比對 % 本身,前面加反斜線

LIKE '%…' 開頭就是萬用字元時,無法利用索引,資料量大時要注意(第 17 課查詢最佳化會再說明)。

字串函式

第二個敘述比較英文與中文的 LENGTH、CHAR_LENGTH:英文字一個字元一個位元組,中文字一個字元三個位元組。第五個查詢的 WHERE 那一步,觀察哪兩位員工符合 _o%(第二個字是 o)或 %h(最後一個字是 h)。

想一想:流程控制與字串

1.

下面的敘述會傳回什麼?

SELECT 2000-01-01;
2.

下面的敘述會傳回什麼?(字元集是 utf8mb4)

SELECT LENGTH('資料庫');
3.

下面的敘述會傳回什麼?

SELECT CONCAT('Uedu', NULL);
第 1 次作答

數學函式與除法

函式 範例 結果
ROUND(n)、ROUND(n, d) ROUND(1.2345, 2) 1.23:四捨五入到 d 位小數
FLOOR(n) FLOOR(-2.7) -3:不大於 n 的最大整數
CEILING(n)(或 CEIL) CEILING(2.1) 3:不小於 n 的最小整數
MOD(n, m)(或 n % m) MOD(10, 3) 1:餘數
n DIV m 10 DIV 3 3:整數商
ABS、POWER、SQRT POWER(2, 10) 1024

ROUND 對兩種數字的規則不同。 整數與 DECIMAL 是精確數值,.5 一律遠離 0 進位:ROUND(2.5) 是 3。 FLOAT、DOUBLE 是近似數值,交給系統的 C 函式庫處理,在多數系統上是「進位到偶數」:ROUND(2.5e0) 是 2。 assignment.hours 是 FLOAT,所以 8.5 小時用 ROUND 會得到 8。金額、成績這類要精確計算的資料,請用 DECIMAL 型別。

除法 / 的結果有小數。 兩個整數相除,結果是有 4 位小數的 DECIMAL:7 / 2 是 3.5000、10 / 3 是 3.3333。 這個 4 來自系統變數 div_precision_increment(預設 4),小數位數是被除數的位數再加 4,例如 1.0 / 3 是 0.33333。 除以 0 在 SELECT 裡得到 NULL 並出現警告 1365;在 INSERT、UPDATE 裡,MySQL 8 預設的嚴格模式會直接拒絕:ERROR 1365 (22012): Division by 0。

日期與時間函式

函式 範例(NOW() 是 2026-09-01 09:00:00) 結果
NOW()、CURDATE()、CURTIME() CURDATE() 2026-09-01
DATE_ADD(d, INTERVAL n 單位) DATE_ADD(CURDATE(), INTERVAL 30 DAY) 2026-10-01
ADDDATE(d, INTERVAL …)、ADDDATE(d, 天數) ADDDATE('1999-01-01', INTERVAL '1-6' YEAR_MONTH) 2000-07-01:加 1 年 6 個月
DATE_SUB(d, INTERVAL …) DATE_SUB('2026-09-01', INTERVAL 1 DAY) 2026-08-31
DATEDIFF(d1, d2) DATEDIFF('2026-09-01', '2003-01-20') 8625:d1 減 d2 的天數
YEAR(d)、MONTH(d)、DAY(d) MONTH('2003-01-20') 1
DATE_FORMAT(d, 格式) DATE_FORMAT('2003-01-20', '%Y/%m/%d') 2003/01/20
UNIX_TIMESTAMP(d) UNIX_TIMESTAMP('1970-01-02 00:00:00') 86400(時區為 UTC 時)
  • INTERVAL 的單位有 DAY、MONTH、YEAR、HOUR、MINUTE 等,也可以寫 NOW() + INTERVAL 2 HOUR
  • 加月份遇到月底會自動調整:'2026-01-31' + INTERVAL 1 MONTH 是 2026-02-28
  • DATE_FORMAT 常用的格式:%Y 四位數年、%m 兩位數月、%d 兩位數日、%H 時(24 小時制)、%i 分、%W 星期的英文名稱
  • UNIX_TIMESTAMP 傳回從 1970-01-01 00:00:00 UTC 起算的秒數,會依照工作階段的時區解讀日期: 同一個 '2000-07-01',時區是 UTC 時得到 962409600,在台灣時區(UTC+8)得到少 8 小時的 962380800

數學與日期函式

第一個敘述比較 DECIMAL 的 ROUND(2.5) 與 DOUBLE 的 ROUND(2.5e0);第二個敘述的除以 0 得到 NULL,結果下方有「1 warning」。第三個查詢對 FLOAT 欄位 hours 使用 ROUND。後面三個敘述是日期計算,NOW() 在播放器裡固定為 2026-09-01 09:00:00。

想一想:數字與日期

1.

下面的敘述會傳回什麼?

SELECT 7 / 2;
2.

assignment 只有一列,hours 是 FLOAT 型別,值為 8.5。下面的查詢會傳回什麼?

SELECT ROUND(hours) FROM assignment;
3.

下面的敘述會傳回什麼?

SELECT DATE_ADD('2026-01-31', INTERVAL 1 MONTH);
第 1 次作答

動手寫:員工的中文職稱

請列出每位員工的 name、職稱的中文(欄位取名 role)與姓名的字元數(欄位取名 len),依 name 排序。職稱是 Programmer 時顯示「工程師」,DBA 顯示「資料庫管理員」,其他職稱顯示「其他」。

你的輸出
預期輸出

重點整理

主題 記住這些
NULL = NULL 永遠是 NULL,用 IS NULL、<=>;NOT IN 的清單裡有 NULL 就找不到資料
邏輯運算 三值邏輯;寫成 AND、OR、NOT,符號寫法(如 &&)已棄用
大小寫 預設不分;區分時用 COLLATE utf8mb4_bin 或 CAST(… AS BINARY),BINARY 'x' 自 8.0.27 起有警告
流程控制 IF、CASE、IFNULL、COALESCE
字串 LENGTH 是位元組、CHAR_LENGTH 是字元;CONCAT 遇到 NULL 就是 NULL;LIKE 用 % 與 _
數學 ROUND 對 DECIMAL 與 FLOAT 的規則不同;整數相除有 4 位小數;DIV 是整數商
日期 日期一定要加引號;DATE_ADD/ADDDATE 搭配 INTERVAL;DATEDIFF 算天數;UNIX_TIMESTAMP 受時區影響

下一課介紹 MySQL 的儲存引擎:InnoDB、MyISAM 等引擎的差別,以及全文檢索。

這一課完成了!
Next lesson:儲存引擎
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。