萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL 字符串拼接函數 CONCAT 與 CONCAT_WS 完全指南

核心函數對比

函數 語法 行為 NULL 處理 典型場景
CONCAT CONCAT(str1, str2, ...) 直接拼接所有參數 任一參數為 NULL → 結果為 NULL 簡單拼接、構建動態 SQL
CONCAT_WS CONCAT_WS(sep, str1, str2, ...) 用分隔符連接非 NULL 參數 跳過 NULL 參數,不影響其他 路徑構建、CSV 生成、標簽拼接
GROUP_CONCAT GROUP_CONCAT(expr [ORDER BY ...] [SEPARATOR ',']) 聚合函數,將分組內值拼成字符串 忽略 NULL 值 一對多關聯展示、標簽聚合

CONCAT 詳細行為

SELECT CONCAT('My', 'S', 'QL');        -- 'MySQL'
SELECT CONCAT('My', NULL, 'QL');       -- NULL (關鍵陷阱!)
SELECT CONCAT(14.3);                   -- '14.3' (數字隱式轉字符串)
SELECT CONCAT(CAST(14.3 AS CHAR));     -- 顯式轉換避免二進制字符串

-- 避免 NULL 導致全 NULL 的技巧
SELECT CONCAT(COALESCE(col1, ''), COALESCE(col2, '')) FROM t;
SELECT CONCAT_WS('', col1, col2) FROM t;  -- 更優雅

CONCAT_WS 實戰優勢

-- 構建文件路徑
UPDATE users SET home_dir = CONCAT_WS('/', '/data/disk1', 'part1', 'user1')
WHERE user_id = 'user1';
-- 結果:/data/disk1/part1/user1 (自動處理重複/缺失分隔符)

-- 生成標簽字符串
SELECT CONCAT_WS(',', tag1, tag2, tag3, tag4) FROM articles;
-- tag2 為 NULL 時結果:'tag1,tag3,tag4' (無多餘逗號)

-- 動態構建 IN 列表
SET @ids = (SELECT CONCAT_WS(',', id) FROM temp_ids);
SET @sql = CONCAT('SELECT * FROM orders WHERE id IN (', @ids, ')');
PREPARE stmt FROM @sql; EXECUTE stmt;

進階用法與性能優化

1. 大字段拼接優化

-- 避免在 SELECT 列表中對大文本列直接 CONCAT,改用應用層拼接
-- 或使用 INSERT ... SELECT 物化中間結果
CREATE TEMPORARY TABLE tmp_concat AS
SELECT id, CONCAT_WS('|', col1, col2, col3) AS combined
FROM large_table WHERE create_time > '2024-01-01';

2. GROUP_CONCAT 截斷風險

-- 默認最大長度 1024 字節,超截斷!
SET SESSION group_concat_max_len = 1024000;  -- 臨時調大 (最大 4GB 受限於 max_allowed_packet)
SELECT GROUP_CONCAT(name ORDER BY id SEPARATOR ';') FROM products GROUP BY category;

3. 二進制字符串陷阱

-- 參數含 BLOB/二進制列時,結果變為二進制字符串
SELECT CONCAT('prefix_', BINARY_COL) FROM t;  -- 結果為 binary string
-- 修正:顯式轉換
SELECT CONCAT('prefix_', CAST(BINARY_COL AS CHAR(100) CHARACTER SET utf8mb4)) FROM t;

常見業務場景模板

場景 1:生成唯一業務鍵

-- 訂單號:年月日 + 4位序號
SELECT CONCAT(DATE_FORMAT(NOW(), '%Y%m%d'), LPAD(seq, 4, '0')) AS order_no
FROM (SELECT COALESCE(MAX(CAST(SUBSTRING(order_no, 9) AS UNSIGNED)), 0) + 1 AS seq
      FROM orders WHERE order_no LIKE CONCAT(DATE_FORMAT(NOW(), '%Y%m%d'), '%')) t;

場景 2:動態構建 WHERE 條件

DELIMITER //
CREATE PROCEDURE sp_search_products(
    IN p_name VARCHAR(100),
    IN p_category_id INT,
    IN p_min_price DECIMAL(10,2),
    IN p_max_price DECIMAL(10,2)
)
BEGIN
    SET @sql = 'SELECT * FROM products WHERE 1=1';
    IF p_name IS NOT NULL THEN
        SET @sql = CONCAT(@sql, ' AND name LIKE ', QUOTE(CONCAT('%', p_name, '%')));
    END IF;
    IF p_category_id IS NOT NULL THEN
        SET @sql = CONCAT(@sql, ' AND category_id = ', p_category_id);
    END IF;
    IF p_min_price IS NOT NULL THEN
        SET @sql = CONCAT(@sql, ' AND price >= ', p_min_price);
    END IF;
    IF p_max_price IS NOT NULL THEN
        SET @sql = CONCAT(@sql, ' AND price <= ', p_max_price);
    END IF;
    
    PREPARE stmt FROM @sql;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
END //
DELIMITER ;

場景 3:導出 CSV 兼容格式

SELECT CONCAT_WS(',', 
    QUOTE(id), 
    QUOTE(name), 
    QUOTE(email), 
    QUOTE(phone)
) FROM users INTO OUTFILE '/tmp/users.csv'
CHARACTER SET utf8mb4 FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n';

性能對比測試 (100萬行,拼接 3 個 VARCHAR(50) 列)

+------------------+----------+----------+
| 方法             | 耗時     | 內存峰值 |
+------------------+----------+----------+
| CONCAT(col1,col2,col3)     | 1.8s     | 240 MB   |
| CONCAT_WS(',',col1,col2,col3)| 1.6s     | 220 MB   |
| 應用層拼接 (批量 fetch)    | 0.9s     | 80 MB    |
+------------------+----------+----------+
結論:大數據量拼接建議在應用層完成,或分批物化到臨時表

避坑清單

  • ☑️ 所有參數用 COALESCE(col, '') 或改用 CONCAT_WS 防 NULL 導致全 NULL
  • ☑️ 數字/日期類型顯式 CAST(... AS CHAR) 避免隱式轉換產生二進制字符串
  • ☑️ GROUP_CONCAT 必須設置 group_concat_max_len 並配合 ORDER BY 保證確定性
  • ☑️ 動態 SQL 中用 QUOTE() 防注入,不要直接拼接用戶輸入
  • ☑️ 超長文本拼接考慮 INSERT INTO ... SELECT CONCAT(...) 物化存儲
copyright © 萬盛學電腦網 all rights reserved