MySQL 提供 SUBSTRING (別名 SUBSTR、MID) 與 SUBSTRING_INDEX 兩大字符串截取函數,前者按位置/長度切片,後者按分隔符切分。本文詳細說明語法變體、參數行為、負數索引語義、實戰範例與效能注意事項。
SUBSTRING(str, pos, len)
SUBSTRING(str FROM pos FOR len) -- 標準 SQL 風格
SUBSTRING(str, pos)
SUBSTRING(str FROM pos)
SUBSTR(str, pos, len)
MID(str, pos, len)
SELECT SUBSTRING('mysql database', 2); -- 'ysql database' (第 2 字元到結尾)
SELECT SUBSTRING('mysql database', 2, 3); -- 'ysq' (第 2 字元起 3 字元)
SELECT SUBSTRING('Quadratically', 5); -- 'ratically'
SELECT SUBSTRING('foobarbar' FROM 4); -- 'barbar'
SELECT SUBSTRING('Quadratically', 5, 6); -- 'ratica'
SELECT SUBSTRING('Sakila', -3); -- 'ila' (倒數 3 字元)
SELECT SUBSTRING('Sakila', -5, 3); -- 'aki' (倒數 5 起 3 字元)
SELECT SUBSTRING('Sakila' FROM -4 FOR 2); -- 'ki'
-- 取得域名 (從 '@' 後到結尾)
SELECT SUBSTRING(email, LOCATE('@', email) + 1) FROM users;
-- 取得檔案副檔名 (最後一個 '.' 後)
SELECT SUBSTRING(filename, LOCATE('.', REVERSE(filename)) + 1) FROM files; -- 錯誤示範,正確見下文
-- 正確取副檔名:SUBSTRING_INDEX 更適合
SUBSTRING_INDEX(str, delim, count)
SELECT SUBSTRING_INDEX('www.example.com', '.', 2); -- 'www.example' (第 2 個 '.' 之前)
SELECT SUBSTRING_INDEX('www.example.com', '.', -2); -- 'example.com' (倒數 2 個 '.' 之後)
-- 經典應用:解析 URL、域名、路徑、參數
SELECT SUBSTRING_INDEX('https://api.example.com/v1/users?id=1', '://', -1); -- 'api.example.com/v1/users?id=1'
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(url, '://', -1), '/', 1); -- 域名 'api.example.com'
-- 解析 CSV 欄位 (第 3 欄)
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(csv_line, ',', 3), ',', -1);
pos、len 以**字元**為單位而非字節,效能略低於二進制字符集。LONGTEXT 大字段頻繁截取會產生大量臨時對象,建議在應用層處理或使用 SUBSTRING_INDEX 定位後再截取。WHERE SUBSTRING(col,1,3)='abc' 不走索引,需建立函數索引 (MySQL 8.0+) 或冗餘計算列。| 場景 | 推薦函數 | 理由 |
|---|---|---|
| 固定位置/長度切片 | SUBSTRING | 語意直觀、標準 SQL、支援負索引 |
| 按分隔符切分 (域名、路徑、CSV、標籤) | SUBSTRING_INDEX | 單次呼叫完成定位+切片、可正向/反向計數 |
| 正則複雜擷取 | REGEXP_SUBSTR (8.0+) | 表達力最強,但效能最低 |
最佳實踐: 簡單切片用
SUBSTRING,分隔符結構化資料用SUBSTRING_INDEX,複雜模式下推至應用層 (Go/Python/PHP 正則庫更強、更快、可單測)。避免在 SQL 中疊加多層嵌套字符串函數,既難讀又難優化。