文件首頁
MySQL 8.4 參考手冊
相關文件 下載本手冊
PDF (US Ltr) - 39.9Mb
PDF (A4) - 40.0Mb
Man Pages (TGZ) - 258.5Kb
Man Pages (Zip) - 365.5Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 參考手冊  /  ...  /  次要索引和產生欄

15.1.20.9 次要索引和產生欄

InnoDB 支援虛擬產生欄上的次要索引。不支援其他索引類型。在虛擬欄上定義的次要索引有時稱為虛擬索引

可以在一個或多個虛擬欄上,或在虛擬欄和一般欄或儲存的產生欄的組合上建立次要索引。包含虛擬欄的次要索引可以定義為 UNIQUE

當在虛擬產生欄上建立次要索引時,產生的欄值會具體化在索引的記錄中。如果索引是涵蓋索引(包含查詢檢索的所有欄的索引),則產生的欄值會從索引結構中具體化的值中檢索,而不是即時計算。

在虛擬欄位上使用次要索引時,由於在 INSERTUPDATE 操作期間,於次要索引記錄中具體化虛擬欄位值時會執行計算,因此需要考量額外的寫入成本。即使有額外的寫入成本,虛擬欄位上的次要索引可能仍然優於產生的儲存欄位,因為後者會在叢集索引中具體化,導致更大的資料表,需要更多的磁碟空間和記憶體。如果未在虛擬欄位上定義次要索引,則讀取時會有額外的成本,因為每次檢查欄位的資料列時都必須計算虛擬欄位值。

索引虛擬欄位的值會進行 MVCC 記錄,以避免在回滾或清除操作期間不必要地重新計算產生的欄位值。記錄值的資料長度受限於 COMPACTREDUNDANT 列格式的索引鍵限制 767 位元組,以及 DYNAMICCOMPRESSED 列格式的 3072 位元組。

在虛擬欄位上新增或刪除次要索引是一種就地操作。

為 JSON 欄位索引提供索引的產生欄位

如其他地方所述,JSON 欄位無法直接建立索引。若要建立間接參照此類欄位的索引,您可以定義一個產生的欄位,其會提取應該被索引的資訊,然後在產生的欄位上建立索引,如下例所示

mysql> CREATE TABLE jemp (
    ->     c JSON,
    ->     g INT GENERATED ALWAYS AS (c->"$.id"),
    ->     INDEX i (g)
    -> );
Query OK, 0 rows affected (0.28 sec)

mysql> INSERT INTO jemp (c) VALUES
     >   ('{"id": "1", "name": "Fred"}'), ('{"id": "2", "name": "Wilma"}'),
     >   ('{"id": "3", "name": "Barney"}'), ('{"id": "4", "name": "Betty"}');
Query OK, 4 rows affected (0.04 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> SELECT c->>"$.name" AS name
     >     FROM jemp WHERE g > 2;
+--------+
| name   |
+--------+
| Barney |
| Betty  |
+--------+
2 rows in set (0.00 sec)

mysql> EXPLAIN SELECT c->>"$.name" AS name
     >    FROM jemp WHERE g > 2\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: jemp
   partitions: NULL
         type: range
possible_keys: i
          key: i
      key_len: 5
          ref: NULL
         rows: 2
     filtered: 100.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)

mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: /* select#1 */ select json_unquote(json_extract(`test`.`jemp`.`c`,'$.name'))
AS `name` from `test`.`jemp` where (`test`.`jemp`.`g` > 2)
1 row in set (0.00 sec)

(我們已將此範例中最後一個陳述式的輸出換行,以符合檢視區域。)

當您在包含一個或多個使用 ->->> 運算子的表達式的 SELECT 或其他 SQL 陳述式上使用 EXPLAIN 時,這些表達式會轉換為使用 JSON_EXTRACT() 以及(如果需要)JSON_UNQUOTE() 的對等表達式,如下列 SHOW WARNINGS 陳述式緊接著此 EXPLAIN 陳述式的輸出所示

mysql> EXPLAIN SELECT c->>"$.name"
     > FROM jemp WHERE g > 2 ORDER BY c->"$.name"\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: jemp
   partitions: NULL
         type: range
possible_keys: i
          key: i
      key_len: 5
          ref: NULL
         rows: 2
     filtered: 100.00
        Extra: Using where; Using filesort
1 row in set, 1 warning (0.00 sec)

mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: /* select#1 */ select json_unquote(json_extract(`test`.`jemp`.`c`,'$.name')) AS
`c->>"$.name"` from `test`.`jemp` where (`test`.`jemp`.`g` > 2) order by
json_extract(`test`.`jemp`.`c`,'$.name')
1 row in set (0.00 sec)

如需其他資訊和範例,請參閱 ->->> 運算子,以及 JSON_EXTRACT()JSON_UNQUOTE() 函式的說明。

此技術也可用於提供間接參照無法直接索引的其他類型欄位的索引,例如 GEOMETRY 欄位。

也可以使用 JSON_VALUE() 函式在 JSON 欄位上建立索引,其中該函式具有可用於最佳化採用該表達式的查詢的表達式。請參閱該函式的說明以取得更多資訊和範例。

NDB Cluster 中的 JSON 欄位和間接索引

在符合以下條件的情況下,也可以在 MySQL NDB Cluster 中使用 JSON 欄位的間接索引

  1. NDB 在內部將 JSON 欄位值作為 BLOB 處理。這表示任何具有一個或多個 JSON 欄位的 NDB 資料表都必須具有主鍵,否則無法記錄在二進位日誌中。

  2. NDB 儲存引擎不支援虛擬欄位的索引。由於產生的欄位的預設值為 VIRTUAL,因此您必須明確指定要將間接索引套用的產生欄位為 STORED

用於建立此處顯示的資料表 jempnCREATE TABLE 陳述式是先前顯示的 jemp 資料表的版本,並進行了修改使其與 NDB 相容

CREATE TABLE jempn (
  a BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  c JSON DEFAULT NULL,
  g INT GENERATED ALWAYS AS (c->"$.id") STORED,
  INDEX i (g)
) ENGINE=NDB;

我們可以使用以下 INSERT 陳述式來填入此資料表

INSERT INTO jempn (c) VALUES
  ('{"id": "1", "name": "Fred"}'),
  ('{"id": "2", "name": "Wilma"}'),
  ('{"id": "3", "name": "Barney"}'),
  ('{"id": "4", "name": "Betty"}');

現在,NDB 可以使用索引 i,如下所示

mysql> EXPLAIN SELECT c->>"$.name" AS name
    ->           FROM jempn WHERE g > 2\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: jempn
   partitions: p0,p1,p2,p3
         type: range
possible_keys: i
          key: i
      key_len: 5
          ref: NULL
         rows: 3
     filtered: 100.00
        Extra: Using pushed condition (`test`.`jempn`.`g` > 2)
1 row in set, 1 warning (0.01 sec)

mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: /* select#1 */ select
json_unquote(json_extract(`test`.`jempn`.`c`,'$.name')) AS `name` from
`test`.`jempn` where (`test`.`jempn`.`g` > 2)   
1 row in set (0.00 sec)

您應該記住,儲存的產生欄位,以及該欄位上的任何索引,都會使用 DataMemory