以 JSON 類型高效處理 MySQL 中的陣列式資料

目次

1. Introduction

需要在 MySQL 處理陣列式資料

在資料庫中,資料通常以關聯式設計儲存。然而,根據應用需求,有時將多個值存於同一欄位會更方便。此時,像「陣列」這樣的資料結構就能派上用場。

例如,考慮以下情境:

  • 存儲使用者選擇的多個標籤。
  • 為產品保存多個圖片 URL。
  • 將歷史或日誌合併到一個欄位。

利用 JSON 類型的優勢

MySQL 並沒有直接的「陣列型別」,但透過 JSON 類型,即可處理陣列式資料。JSON 類型高度靈活,具備以下優點:

  • 支援巢狀資料結構。
  • 可在查詢中直接進行資料操作。
  • 能在單一欄位管理多種資料格式。

本文將介紹如何使用 JSON 類型在 MySQL 中有效處理陣列式資料。

2. Basics of handling arrays with MySQL’s JSON type

MySQL 的 JSON 類型是什麼?

JSON(JavaScript Object Notation)是一種輕量級且簡單的資料交換格式。自 MySQL 5.7 版本起,支援原生 JSON 類型,可直接將 JSON 格式資料儲存並操作於資料庫中。

範例:以下資料可以存於 JSON 類型欄位:

{
  "tags": ["PHP", "MySQL", "JSON"],
  "status": "published"
}

JSON 類型的優點與使用案例

使用 JSON 類型的主要優點為:

  1. 靈活的資料結構:可處理可變長度資料,而不必改動關聯式資料表結構。
  2. 高效的資料操作:利用 MySQL 內建函式(例如 JSON_EXTRACTJSON_ARRAY 等)即可輕鬆操作資料。
  3. 可實現無模式設計:隨著應用規格變更,不必頻繁修改資料表結構。

使用案例:

  • 為產品資訊指派多個分類。
  • 儲存使用者自訂設定。
  • 在網頁應用程式中處理巢狀 JSON 資料。

3. Basic operations on JSON arrays

建立 JSON 陣列

在 MySQL 中,您可以使用 JSON_ARRAY 函式輕鬆建立 JSON 格式的陣列。當您想在單一欄位中存放多個值時,陣列非常實用。

範例使用

以下查詢建立名為 tags 的 JSON 陣列。

SELECT JSON_ARRAY('PHP', 'MySQL', 'JavaScript') AS tags;

執行結果:

["PHP", "MySQL", "JavaScript"]

實際應用

以下示例透過 INSERT 語句將 JSON 陣列儲存到資料庫中。

CREATE TABLE articles (
    id INT AUTO_INCREMENT PRIMARY KEY,
    tags JSON
);

INSERT INTO articles (tags) 
VALUES (JSON_ARRAY('PHP', 'MySQL', 'JavaScript'));

從 JSON 陣列中提取資料

要提取存於 JSON 陣列中的資料,請使用 JSON_EXTRACT 函式。它能讓您從陣列中擷取特定元素。

範例使用

以下示例擷取陣列中第二個元素(0 為基底索引)。

SELECT JSON_EXTRACT('["PHP", "MySQL", "JavaScript"]', '$[1]') AS second_tag;

執行結果:

"MySQL"

同時擷取多個元素

您也可以一次擷取多個元素。

SELECT JSON_EXTRACT('["PHP", "MySQL", "JavaScript"]', '$[0]', '$[2]') AS extracted_values;

加入、更新與刪除資料

將資料加入陣列

使用 JSON_ARRAY_APPEND 函式,您可將新資料追加至已存在的陣列中。

SET @tags = '["PHP", "MySQL"]';
SELECT JSON_ARRAY_APPEND(@tags, '$', 'JavaScript') AS updated_tags;

執行結果:

["PHP", "MySQL", "JavaScript"]

更新陣列內的資料

您可使用 JSON_SET 更新陣列內的指定元素。

SET @tags = '["PHP", "MySQL", "JavaScript"]';
SELECT JSON_SET(@tags, '$[1]', 'Python') AS updated_tags;

執行結果:

["PHP", "Python", "JavaScript"]

從陣列中移除資料

Use JSON_REMOVE to delete a specific element inside the array.

SET @tags = '["PHP", "MySQL", "JavaScript"]';
SELECT JSON_REMOVE(@tags, '$[1]') AS updated_tags;

結果:

["PHP", "JavaScript"]

4. 搜尋與過濾 JSON 陣列

搜尋包含特定資料的陣列

要判斷 JSON 陣列是否包含特定值,可使用 JSON_CONTAINS 函式。該函式會回傳指定的 JSON 陣列是否包含給定的值。

範例使用

以下範例中,我們檢查 JSON 陣列是否包含「MySQL」。

SELECT JSON_CONTAINS('["PHP", "MySQL", "JavaScript"]', '"MySQL"') AS is_present;

結果:

1  (if present)
0  (if not present)

應用範例:條件搜尋

若要查詢資料庫中包含特定值的 JSON 陣列列,您可以在 WHERE 子句中使用 JSON_CONTAINS

SELECT * 
FROM articles
WHERE JSON_CONTAINS(tags, '"MySQL"');

此查詢會檢索 tags 欄位包含「MySQL」的列。

獲取陣列長度

要取得 JSON 陣列中的元素數量,請使用 JSON_LENGTH 函式。此功能對資料分析或條件篩選很有用。

範例使用

以下範例中,我們取得陣列中的元素數量。

SELECT JSON_LENGTH('["PHP", "MySQL", "JavaScript"]') AS array_length;

結果:

3

實作範例:擷取符合條件的列

要擷取元素數量等於或大於某個值的列,請在 WHERE 子句中使用 JSON_LENGTH

SELECT * 
FROM articles
WHERE JSON_LENGTH(tags) >= 2;

此查詢會選擇 tags 欄位包含兩個或更多元素的列。

與條件查詢結合的實作範例

您可以結合多個條件以進行更進階的搜尋。以下查詢會找出 tags 陣列包含「JavaScript」且具有三個或以上元素的列。

SELECT * 
FROM articles
WHERE JSON_CONTAINS(tags, '"JavaScript"') 
  AND JSON_LENGTH(tags) >= 3;

5. 實務使用案例:透過真實情境學習 JSON 陣列

如何將產品分類儲存為 JSON 陣列

在電商網站中,產品可能屬於多個分類。此時使用 JSON 陣列儲存分類資訊會更有效率。

範例:為產品儲存分類資料

以下示範建立一個名為 categories 的 JSON 欄位,並儲存多個分類。

CREATE TABLE products (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    categories JSON
);

INSERT INTO products (name, categories) 
VALUES ('Laptop', JSON_ARRAY('Electronics', 'Computers')),
       ('Smartphone', JSON_ARRAY('Electronics', 'Mobile Devices'));

此資料結構即使產品屬於多個分類,也能簡潔儲存。

查詢屬於特定分類的產品

利用 JSON 類型,您可以輕鬆搜尋屬於特定分類的產品。

查詢範例

以下查詢會找出「電子產品」分類中的所有產品。

SELECT name 
FROM products
WHERE JSON_CONTAINS(categories, '"Electronics"');

結果:

Laptop
Smartphone

此查詢允許您按分類靈活地檢索產品清單。

範例:按價格區間篩選

以下示範如何以 JSON 儲存價格資訊,並根據價格區間查詢產品。

資料範例

以下使用 JSON 類型為每個項目儲存產品價格資訊。

CREATE TABLE products_with_prices (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    details JSON
);

INSERT INTO products_with_prices (name, details)
VALUES ('Laptop', '{"price": 150000, "categories": ["Electronics", "Computers"]}'),
       ('Smartphone', '{"price": 80000, "categories": ["Electronics", "Mobile Devices"]}');

查詢範例

若要搜尋價格為 100,000 或以上的產品,您可使用 JSON_EXTRACT

SELECT name 
FROM products_with_prices
WHERE JSON_EXTRACT(details, '$.price') >= 100000;

結果:

Laptop

使用 JSON_TABLE 進行擴展與查詢範例

當您想以關聯式方式查詢 JSON 數據時,可以使用 JSON_TABLE。該函式允許您將 JSON 陣列擴展為虛擬表。

範例使用

以下是一個示例,將 JSON 陣列擴展,並將每個分類顯示為獨立列。

SELECT * 
FROM JSON_TABLE(
    '["Electronics", "Computers", "Mobile Devices"]',
    '$[*]' COLUMNS(
        category_name VARCHAR(100) PATH '$'
    )
) AS categories_table;

結果:

category_name
--------------
Electronics
Computers
Mobile Devices

6. 使用 JSON 類型時的注意事項

效能優化重點

雖然 JSON 類型非常靈活,但若未妥善設計,可能會對資料庫效能產生負面影響。以下列出關鍵的效能優化要點。

1. 建立索引

在 MySQL 中,您無法直接對 JSON 欄位本身設定索引,但可以創建虛擬欄位並對特定鍵建立索引。

範例:透過虛擬欄位建立索引

在此示例中,我們對 JSON 數據中的 price 鍵進行索引。

ALTER TABLE products_with_prices
ADD COLUMN price INT AS (JSON_EXTRACT(details, '$.price')) STORED,
ADD INDEX idx_price (price);

透過使用虛擬欄位,可以大幅提升對 JSON 類型數據的搜尋效能。

2. 避免過於複雜的 JSON 結構

深度巢狀的 JSON 結構會降低查詢的可讀性並影響效能。在設計資料時,盡量採用盡可能簡單的 JSON 結構。

良好範例:

{
  "categories": ["Electronics", "Computers"],
  "price": 150000
}

應避免的結構:

{
  "product": {
    "details": {
      "price": 150000,
      "categories": ["Electronics", "Computers"]
    }
  }
}

如何有效使用索引

在使用虛擬欄位建立索引時,必須考慮以下要點:

  1. 虛擬欄位必須設定為 STORED
  2. 使用 JSON_EXTRACT 將特定鍵抽取到虛擬欄位。

例如,為了抽取 categories 鍵值並對其建立索引:

ALTER TABLE products
ADD COLUMN main_category VARCHAR(255) AS (JSON_EXTRACT(categories, '$[0]')) STORED,
ADD INDEX idx_main_category (main_category);

資料驗證的重要性

儘管 JSON 類型資料具有彈性,但也可能導致儲存不正確的格式。為維護資料完整性,請採用以下方式:

1. 使用 CHECK 約束

自 MySQL 8.0 起,您可以使用 CHECK 約束來驗證 JSON 資料的結構或內容。

ALTER TABLE products_with_prices
ADD CONSTRAINT check_price CHECK (JSON_EXTRACT(details, '$.price') >= 0);

2. 在應用程式層級進行驗證

插入資料時,應在應用程式層級驗證 JSON 格式。在 PHP 或 Python 等程式語言中,您可以使用標準函式庫進行 JSON 驗證。

7. MySQL 中使用陣列式資料的常見問答

Q1: MySQL 是否提供陣列類型?

A1: MySQL 並未提供直接的「陣列類型」。不過,您可以使用 JSON 類型處理陣列式資料。透過 JSON 類型,您可在一個欄位儲存多個值並透過查詢進行操作。

範例:

SELECT JSON_ARRAY('value1', 'value2', 'value3') AS example_array;

結果:

["value1", "value2", "value3"]

Q2: 您能為 JSON 類型資料設定索引嗎?

A2: 您無法直接對 JSON 類型本身設定索引。然而,您可以將特定鍵或值抽取為虛擬欄位,然後對該欄位設定索引。

範例:

ALTER TABLE products_with_prices
ADD COLUMN price INT AS (JSON_EXTRACT(details, '$.price')) STORED,
ADD INDEX idx_price (price);

%

Q3:JSON 資料是否有限制大小?

A3: MySQL 的 JSON 類型最多可儲存 4 GB 的資料。然而使用非常大的 JSON 資料可能會降低效能,因而正確的資料設計是必要的。

建議事項:

  • 只儲存必要的最小資料。
  • 避免過度巢狀的 JSON 結構。

Q4:如何更新 JSON 陣列中的特定元素?

A4: 您可以使用 JSON_SET 函數來更新 JSON 陣列中的特定元素。

範例:

SET @tags = '["PHP", "MySQL", "JavaScript"]';
SELECT JSON_SET(@tags, '$[1]', 'Python') AS updated_tags;

結果:

["PHP", "Python", "JavaScript"]

Q5:JSON 類型與傳統表格設計之比較

A5: JSON 類型提供高度彈性,但與傳統關聯式設計在本質上有差異。

ItemJSON type傳統表格設計
彈性

高(不需要更改資料表結構)

已修正(需要更改結構)

效能

在某些操作中較差

優化

查詢複雜度

需要 JSON 函式

Simple

索引

部分支援虛擬欄位

完全支援

8. 摘要

使用 MySQL 的 JSON 類型處理陣列式資料的優點

本文說明了 MySQL 中處理陣列式資料的 JSON 類型。主要涵蓋的重點包括:

  1. 為什麼使用 JSON 類型
    MySQL 缺乏直接的陣列類型,但透過使用 JSON 類型,您可以在單一欄位中儲存多個值並執行彈性的資料操作。

  2. 基本 JSON 操作

  • 學會如何建立 JSON 陣列、抽取資料、更新及刪除值。
  • 使用 JSON_ARRAYJSON_EXTRACTJSON_SET 等函式高效操作陣列式資料。
  1. 搜尋與過濾
  • 使用 JSON_CONTAINS 搜尋陣列內的特定值。
  • 使用 JSON_LENGTH 取得陣列長度並執行條件過濾。
  1. 實務用例
    我們學到了具體的應用情境,例如管理產品類別與按價格過濾。

  2. 注意事項與優化

  • 討論如何透過虛擬欄位索引 JSON 資料,以及驗證 JSON 資料的重要性。

利用 JSON 類型的下一步

透過在 MySQL 中使用 JSON 類型,您可以比傳統關聯式資料庫設計更彈性地管理資料。然而,正確的設計與效能考量仍然是關鍵。

下一階段要學習的主題:

  • 使用複合索引:設計將 JSON 類型欄位與常規欄位結合的索引。
  • 進階 JSON 函式:使用 JSON_MERGEJSON_OBJECT 等函式進行更複雜的操作。
  • 應用層資料操作:使用 PHP、Python 等語言高效操作 MySQL JSON 資料。

最終總結

透過本文,您已學會如何使用 MySQL 的 JSON 類型高效處理陣列式資料。將此知識應用於實務,可協助您設計更具彈性與可擴充性的資料庫系統。