転置インデックスは、テキストをトークンに分割して、これらのトークンを含むドキュメントをすばやく特定するための一般的な情報検索手法です。ApsaraDB for SelectDB は転置インデックスをサポートしています。文字列カラムに対する全文検索、および数値および日付カラムに対する等価または範囲クエリに使用できます。これにより、大量のデータをすばやくフィルタリングして、一致する行を見つけることができます。このトピックでは ApsaraDB for SelectDB の転置インデックス機能およびその作成と使用方法について説明します。
インデックスの仕組み
ApsaraDB for SelectDB では、テーブル内の各行が 1 つのドキュメントに対応し、各カラムがそのドキュメント内のフィールドに対応します。転置インデックスを利用すると、キーワードを含む行を高速に特定でき、WHERE 句によるフィルター処理の効率が向上します。
通常のインデックスとは異なり、転置インデックスは独立した「転置ファイル」にデータを格納します。これらのファイルは、主データファイル(セグメント)との間に論理的な関係のみがあり、物理的な統合は行われません。この設計により、転置インデックスの更新または削除時に主データファイルを再書き込みする必要がなく、処理オーバーヘッドが大幅に低減されます。
ユースケース
文字列型カラムにおける全文検索の高速化。
文字列型、数値型、日時型カラムにおける
=, !=, >, >=, <, <=を用いたフィルター処理の高速化。
メリット
豊富な論理演算の組み合わせをサポート。
OR 演算および
NOT演算をインデックス層にプッシュダウン可能。AND、OR、
NOTを複数の条件にわたって任意に組み合わせ可能。
柔軟かつ高速なインデックス管理。
テーブル作成時に転置インデックスを作成可能。
既存のテーブルに転置インデックスを追加可能。
テーブルから既存の転置インデックスを削除します。
制限事項
浮動小数点精度の問題により、FLOAT 型および DOUBLE 型はサポートされていません。代わりに DECIMAL 型をご利用ください。DECIMAL 型は転置インデックスをサポートしています。
MAP、STRUCT、JSON、HLL、BITMAP、QUANTILE_STATE、AGG_STATE などの複雑なデータ型はサポートされていません。JSON 型を利用する場合は、VARIANT 型をご利用ください。
数値型カラムには転置インデックスを作成できますが、english、chinese、unicode などのトークナイザーパーサーを指定することはできません。
Merge-on-Write が有効化された DUPLICATE テーブルおよび UNIQUE テーブルでは、任意のカラムに転置インデックスを作成できます。一方、AGGREGATE テーブルおよび Merge-on-Write が無効な UNIQUE テーブルでは、キーとなるカラム(key columns)にのみ転置インデックスを作成できます。これらのモデルでは、非キーとなるカラムには転置インデックスを作成できません。これは、マージ処理前に全データを読み込む必要があるため、早期フィルター処理にインデックスを利用できないためです。
インデックスの作成
転置インデックスは、テーブル作成時または既存のテーブルに対して後から作成するという 2 つの方法で作成できます。
テーブル作成時にインデックスを作成
これは同期操作です。テーブル作成完了と同時にインデックスが利用可能になります。
転置インデックスの制限は、データモデルによって異なります。
Aggregate モデル:キーとなるカラム(key columns)にのみ転置インデックスを作成できます。
Unique モデル:Merge on Write を有効化する必要があります。有効化後は、任意のカラムに転置インデックスを作成できます。
Duplicate モデル:任意のカラムに転置インデックスを作成できます。
構文
CREATE TABLE [IF NOT EXISTS] [db_name.]<table_name>
(
<column_definition_list>,
[<index_definition_list>]
)
table_properties;パラメーター
テーブル作成時のパラメーター
パラメーター | 必須 | 説明 |
db_name | いいえ | 対象データベースの名前。 |
table_name | はい | 対象テーブルの名前。 |
column_definition_list | はい | 列定義のリスト。詳細については、「CREATE-TABLE」をご参照ください。 |
table_properties | はい | データモデル、パーティション、バケットなど、テーブルのプロパティ。詳細については、「データモデル」をご参照ください。 |
index_definition_list | いいえ | インデックス定義のリスト。 |
index_definition_list の構文
テーブル作成時に複数のインデックスを定義できます。書式: index_definition[, index_definition][, index_definition]...。
index_definition の定義
INDEX <index_name>(<column_name>) <index_type> [PROPERTIES("<key>" = "<value>")] [COMMENT '<comment>']index_definition のパラメーター
必須パラメーター
パラメーター名 | 説明 |
index_name | インデックスの名前。 |
column_name | インデックス対象のカラム名。 |
index_type | インデックスの種類。必ず |
任意パラメーター
PROPERTIES
PROPERTIES は、カラムのトークン化の有無を指定します。カンマ区切りで、"<key>" = "<value>" 形式のキーと値のペアを 1 つ以上指定できます。テキスト文字列がどのようにトークン化されるか不明な場合は、TOKENIZE 関数で確認してください。詳細については、「トークン化関数」をご参照ください。
キー | 値 |
parser | トークナイザーを指定します。デフォルトではトークナイザーは適用されません。数値型カラムではこのプロパティはサポートされていません。
|
parser_mode | トークン化の粒度を指定します。 すべてのトークナイザーは、デフォルトで粗粒度モード(coarse-grained mode)を使用します。粗粒度モードでは、長いトークンが優先されます。たとえば、
トークン化の動作が不明な場合は、「トークン化関数」をご参照ください。 |
support_phrase | MATCH_PHRASE を用いたフレーズクエリの高速化を有効化します。デフォルト値は false です。
|
char_filter | トークン化前の文字列を前処理します。char_replace のみがサポートされています。 char_replace は、pattern 内の各文字を replacement 内の対応する文字に置き換えます。
|
ignore_above | トークン化されない文字列インデックス(
|
lower_case | トークンを小文字に変換し、大文字小文字を区別しないマッチングを実現します。
|
stopwords | トークン化に影響を与えるストップワードのリストを指定します。
|
dict_compression | 転置インデックス辞書に対して ZSTD 辞書圧縮を有効化します。
説明 バージョン 4.1.0 以降でのみサポートされます。 |
COMMENT
パラメーター名 | 説明 |
comment | インデックスの説明。 |
テーブルおよびインデックス作成の例
-- comment カラムに idx_comment という名前の転置インデックスを作成
-- USING INVERTED でインデックスの種類を転置インデックスに設定
-- PROPERTIES("parser" = "english") で英語トークン化を指定。"chinese" および "unicode" もサポート。"parser" を省略するとトークン化は無効化されます。
CREATE TABLE hackernews_1m
(
`id` BIGINT,
`deleted` TINYINT,
`type` String,
`author` String,
`timestamp` DateTimeV2,
`comment` String,
`dead` TINYINT,
`parent` BIGINT,
`poll` BIGINT,
`children` Array<BIGINT>,
`url` String,
`score` INT,
`title` String,
`parts` Array<INT>,
`descendants` INT,
INDEX idx_comment (`comment`) USING INVERTED PROPERTIES("parser" = "english") COMMENT 'comment の転置インデックス'
)
DUPLICATE KEY(`id`)
DISTRIBUTED BY HASH(`id`) BUCKETS 10;既存のテーブルにインデックスを追加
これは非同期操作です。SHOW ALTER TABLE COLUMN; を使用して進捗状況を確認できます。
構文
ALTER TABLE <table_name> ADD INDEX <index_name>(<column_name>) <index_type> [PROPERTIES("<key>" = "<value>")];パラメーター
テーブル作成時と同じです。
例
トークン化を行わないインデックスを追加します。
ALTER TABLE user_tb ADD INDEX index_userId(user_id) USING INVERTED ;「english」トークン化メソッドを使用するインデックスを追加します。
ALTER TABLE user_tb ADD INDEX index_city(city) USING INVERTED PROPERTIES("parser" = "english");インデックスの確認
インデックス変更の進捗状況の確認
ALTER または DROP を用いたインデックス変更は非同期です。以下のステートメントで進捗状況を確認できます。
SHOW ALTER TABLE COLUMN;テーブルのすべてのインデックスを表示
構文
SHOW INDEXES FROM <table_name>;例
SHOW INDEXES FROM user_tb;インデックスの削除
インデックスの削除は非同期操作です。進捗状況の確認については、「インデックスの確認」をご参照ください。
インデックスを削除すると、クエリのパフォーマンスが低下する可能性があります。慎重に実行してください。
構文
-- 構文 1
DROP INDEX <index_name> ON <table_name>;
-- 構文 2
ALTER TABLE <table_name> DROP INDEX <index_name>;例
DROP INDEX index_userId ON user_tb;
ALTER TABLE user_tb DROP INDEX index_city;転置インデックスの利用
全文検索
構文
SELECT * FROM <table_name> WHERE <column_name> <conditional_logic> '<keywords>';パラメーター
パラメーター | 必須 | 説明 |
table_name | はい | 対象テーブルの名前。 |
column_name | はい | 対象カラムの名前。 |
conditional_logic | はい | マッチングロジック:キーワードと論理演算子の自由な組み合わせ。 論理演算子:AND、OR、 全文検索キーワード:
|
keywords | はい | 対象キーワード。 複数のキーワードは半角スペースで区切ります。 例: |
例
-- log_tb の logmsg カラムに keyword1 を含むすべての行を検索します。
SELECT * FROM log_tb WHERE logmsg MATCH_ANY 'keyword1';
-- log_tb の logmsg カラムに keyword1 または keyword2 を含むすべての行を検索します。
SELECT * FROM log_tb WHERE logmsg MATCH_ANY 'keyword1 keyword2';
-- log_tb の logmsg カラムに keyword1 および keyword2 の両方を含むすべての行を検索します。
SELECT * FROM log_tb WHERE logmsg MATCH_ALL 'keyword1 keyword2';
-- log_tb の logmsg カラムに keyword1 および keyword2 をこの順序で含むすべての行を検索します。
SELECT * FROM log_tb WHERE logmsg MATCH_PHRASE 'keyword1 keyword2';数値型および日付型カラムにおける等価および範囲クエリ
クエリ構文は標準 SQL と同一です。
例
-- 等価、範囲、IN、NOT IN クエリ
SELECT * FROM user_tb WHERE id = 123;
SELECT * FROM user_tb WHERE ts > '2023-01-01 00:00:00';
SELECT * FROM user_tb WHERE op_type IN ('add', 'delete');クエリパフォーマンスの比較
100 万行の hackernews データセットを用いて、転置インデックスあり/なしのクエリパフォーマンスをテストしました。
環境の準備
ステップ 1:対象テーブルの作成
データベースを作成します。
CREATE DATABASE test_inverted_index;新規データベースに切り替えます。
USE test_inverted_index;対象テーブルを作成します。
CREATE TABLE hackernews_1m ( `id` BIGINT, `deleted` TINYINT, `type` String, `author` String, `timestamp` DateTimeV2, `comment` String, `dead` TINYINT, `parent` BIGINT, `poll` BIGINT, `children` Array<BIGINT>, `url` String, `score` INT, `title` String, `parts` Array<INT>, `descendants` INT, INDEX idx_comment (`comment`) USING INVERTED PROPERTIES("parser" = "english") COMMENT 'comment の転置インデックス' ) DUPLICATE KEY(`id`) DISTRIBUTED BY HASH(`id`) BUCKETS 10; -- comment カラムに idx_comment という名前の転置インデックスを作成 -- USING INVERTED でインデックスの種類を転置インデックスに設定 -- PROPERTIES("parser" = "english") で英語トークン化を指定。"chinese" および "unicode" もサポート。"parser" を省略するとトークン化は無効化されます
ステップ 2:データのインポート
データを対象テーブルにインポートします。
データファイルをダウンロードします。
wget https://qa-build.oss-cn-beijing.aliyuncs.com/regression/index/hacknernews_1m.csv.gzStream Load を使用してデータをインポートします。
ApsaraDB for SelectDB のインスタンスの詳細ページでは、ApsaraDB for SelectDB インスタンスの接続アドレス(ホスト)およびポート番号(ポート)を確認できます。Stream Load の詳細については、「Stream Load」をご参照ください。
curl --location-trusted -u root: -H "compress_type:gz" -T hacknernews_1m.csv.gz http://<host>:<port>/api/test_inverted_index/hackernews_1m/_stream_load { "TxnId": 2, "Label": "a8a3e802-2329-49e8-912b-04c800a461a6", "TwoPhaseCommit": "false", "Status": "Success", "Message": "OK", "NumberTotalRows": 1000000, "NumberLoadedRows": 1000000, "NumberFilteredRows": 0, "NumberUnselectedRows": 0, "LoadBytes": 130618406, "LoadTimeMs": 8988, "BeginTxnTimeMs": 23, "StreamLoadPutTimeMs": 113, "ReadDataTimeMs": 4788, "WriteDataTimeMs": 8811, "CommitAndPublishTimeMs": 38 }count()を実行して、インポートが正常に完了したことを確認します。SELECT count() FROM hackernews_1m; +---------+ | count() | +---------+ | 1000000 | +---------+ 1 row in set (0.02 sec)
パフォーマンス比較
トークン化された転置インデックスと転置インデックスなしの結果は異なります。トークン化ではトークンの正規化(例:小文字への変換)が行われるため、転置インデックスを用いたクエリの結果は若干高くなる場合があります。
いくつかの例ではパフォーマンス差が小さいのは、データセットが小さいためです。より大規模なデータセットでは、差はさらに大きくなります。
全文検索
comment カラムに
OLAPを含む行数をカウントします。`comment` カラムに
OLAPを含む行数を `LIKE` 演算子でカウントすると、0.18 秒かかります。SELECT count() FROM hackernews_1m WHERE comment LIKE '%OLAP%'; +---------+ | count() | +---------+ | 34 | +---------+ 1 row in set (0.18 sec)MATCH_ANYを用いた転置インデックスに基づく全文検索で、comment カラムにOLAPを含む行数をカウントすると、0.02 秒かかりました。これは `LIKE` を用いたカウントと比較して 9 倍の高速化です。SELECT count() FROM hackernews_1m WHERE comment MATCH_ANY 'OLAP'; +---------+ | count() | +---------+ | 35 | +---------+ 1 row in set (0.02 sec)
comment カラムに
OLTPを含む行数をカウントします。`LIKE` を用いて comment カラムに
OLTPを含む行数をカウントすると、0.07 秒かかります。SELECT count() FROM hackernews_1m WHERE comment LIKE '%OLTP%'; +---------+ | count() | +---------+ | 48 | +---------+ 1 row in set (0.07 sec)MATCH_ANYを用いた転置インデックスに基づく全文検索で、comment カラムにOLTPを含む行数をカウントすると、0.01 秒かかりました。これは `LIKE` を用いたカウントと比較して 7 倍の高速化です。SELECT count() FROM hackernews_1m WHERE comment MATCH_ANY 'OLTP'; +---------+ | count() | +---------+ | 51 | +---------+ 1 row in set (0.01 sec)
comment カラムに
OLAPおよびOLTPの両方を含む行数をカウントします。`LIKE` を用いた場合:0.13 秒。
SELECT count() FROM hackernews_1m WHERE comment LIKE '%OLAP%' AND comment LIKE '%OLTP%'; +---------+ | count() | +---------+ | 14 | +---------+ 1 row in set (0.13 sec)MATCH_ALLを用いた転置インデックスに基づく全文検索では、0.01 秒かかりました。これは `LIKE` を用いた統計と比較して 12 倍の高速化です。SELECT count() FROM hackernews_1m WHERE comment MATCH_ALL 'OLAP OLTP'; +---------+ | count() | +---------+ | 15 | +---------+ 1 row in set (0.01 sec)
comment カラムに
OLAPまたはOLTPを含む行数をカウントします。`LIKE` を用いた場合:0.12 秒。
SELECT count() FROM hackernews_1m WHERE comment LIKE '%OLAP%' OR comment LIKE '%OLTP%'; +---------+ | count() | +---------+ | 68 | +---------+ 1 row in set (0.12 sec)全文インデックスを用いた統計では、0.01 秒で完了しました。これは `LIKE` クエリと比較して 12 倍の高速化です。
SELECT count() FROM hackernews_1m WHERE comment MATCH_ANY 'OLAP OLTP'; +---------+ | count() | +---------+ | 71 | +---------+ 1 row in set (0.01 sec)
等価および範囲クエリ
DateTime 型カラムにおける範囲クエリのパフォーマンス。
転置インデックスを作成する前、timestamp >
2007-08-23 04:17:00の行数をカウントします。所要時間:0.03 秒。SELECT count() FROM hackernews_1m WHERE timestamp > '2007-08-23 04:17:00'; +---------+ | count() | +---------+ | 999081 | +---------+ 1 row in set (0.03 sec)timestamp カラムに転置インデックスを作成します。
CREATE INDEX idx_timestamp ON hackernews_1m(timestamp) USING INVERTED; Query OK, 0 rows affected (0.03 sec)インデックス作成の進捗状況を確認します。FinishTime と CreateTime の差分から、100 万行に対するインデックス構築に要した時間はわずか 1 秒であることがわかります。
SHOW ALTER TABLE COLUMN; +-------+---------------+-------------------------+-------------------------+---------------+---------+---------------+---------------+---------------+----------+------+----------+---------+ | JobId | TableName | CreateTime | FinishTime | IndexName | IndexId | OriginIndexId | SchemaVersion | TransactionId | State | Msg | Progress | Timeout | +-------+---------------+-------------------------+-------------------------+---------------+---------+---------------+---------------+---------------+----------+------+----------+---------+ | 10030 | hackernews_1m | 2023-02-10 19:44:12.929 | 2023-02-10 19:44:13.938 | hackernews_1m | 10031 | 10008 | 1:1994690496 | 3 | FINISHED | | NULL | 2592000 | +-------+---------------+-------------------------+-------------------------+---------------+---------+---------------+---------------+---------------+----------+------+----------+---------+ 1 row in set (0.00 sec)インデックス作成後、同じクエリ文を用いて timestamp カラムが
2007-08-23 04:17:00より大きい行数をカウントすると、0.01 秒かかりました。これは転置インデックス作成前のクエリと比較して 2 倍の高速化です。SELECT count() FROM hackernews_1m WHERE timestamp > '2007-08-23 04:17:00'; +---------+ | count() | +---------+ | 999081 | +---------+ 1 row in set (0.01 sec)
数値型カラムにおける等価クエリのパフォーマンス。
転置インデックスを作成する前、parent = 11189 の行数をカウントします。
SELECT count() FROM hackernews_1m WHERE parent = 11189; +---------+ | count() | +---------+ | 2 | +---------+ 1 row in set (0.01 sec)トークン化を行わない parent カラムに転置インデックスを作成します。
-- 数値型の場合、USING INVERTED ではトークナイザーを指定する必要はありません -- ALTER TABLE t ADD INDEX は、インデックス追加の第 2 の構文です ALTER TABLE hackernews_1m ADD INDEX idx_parent(parent) USING INVERTED; Query OK, 0 rows affected (0.01 sec)インデックス作成の進捗状況を確認します。
SHOW ALTER TABLE COLUMN; +-------+---------------+-------------------------+-------------------------+---------------+---------+---------------+---------------+---------------+----------+------+----------+---------+ | JobId | TableName | CreateTime | FinishTime | IndexName | IndexId | OriginIndexId | SchemaVersion | TransactionId | State | Msg | Progress | Timeout | +-------+---------------+-------------------------+-------------------------+---------------+---------+---------------+---------------+---------------+----------+------+----------+---------+ | 10030 | hackernews_1m | 2023-02-10 19:44:12.929 | 2023-02-10 19:44:13.938 | hackernews_1m | 10031 | 10008 | 1:1994690496 | 3 | FINISHED | | NULL | 2592000 | | 10053 | hackernews_1m | 2023-02-10 19:49:32.893 | 2023-02-10 19:49:33.982 | hackernews_1m | 10054 | 10008 | 1:378856428 | 4 | FINISHED | | NULL | 2592000 | +-------+---------------+-------------------------+-------------------------+---------------+---------+---------------+---------------+---------------+----------+------+----------+---------+インデックス作成後、同じクエリ文を用いて数値型カラム parent が 11189 と等しい行数をカウントします。
SELECT count() FROM hackernews_1m WHERE parent = 11189; +---------+ | count() | +---------+ | 2 | +---------+ 1 row in set (0.01 sec)
文字列型カラムにおける等価クエリのパフォーマンス。
転置インデックスを作成する前、author = 'faster' の行数をカウントします。所要時間:0.03 秒。
SELECT count() FROM hackernews_1m WHERE author = 'faster'; +---------+ | count() | +---------+ | 20 | +---------+ 1 row in set (0.03 sec)トークン化を行わない author カラムに転置インデックスを作成します。
-- USING INVERTED のみを指定します。文字列全体を 1 つのトークンとして扱います。 ALTER TABLE hackernews_1m ADD INDEX idx_author(author) USING INVERTED; Query OK, 0 rows affected (0.01 sec)インデックス作成の進捗状況を確認します。
-- 100 万件の author 値に対するインデックス構築にはわずか 1.5 秒しかかかりません。 SHOW ALTER TABLE COLUMN; +-------+---------------+-------------------------+-------------------------+---------------+---------+---------------+---------------+---------------+----------+------+----------+---------+ | JobId | TableName | CreateTime | FinishTime | IndexName | IndexId | OriginIndexId | SchemaVersion | TransactionId | State | Msg | Progress | Timeout | +-------+---------------+-------------------------+-------------------------+---------------+---------+---------------+---------------+---------------+----------+------+----------+---------+ | 10030 | hackernews_1m | 2023-02-10 19:44:12.929 | 2023-02-10 19:44:13.938 | hackernews_1m | 10031 | 10008 | 1:1994690496 | 3 | FINISHED | | NULL | 2592000 | | 10053 | hackernews_1m | 2023-02-10 19:49:32.893 | 2023-02-10 19:49:33.982 | hackernews_1m | 10054 | 10008 | 1:378856428 | 4 | FINISHED | | NULL | 2592000 | | 10076 | hackernews_1m | 2023-02-10 19:54:20.046 | 2023-02-10 19:54:21.521 | hackernews_1m | 10077 | 10008 | 1:1335127701 | 5 | FINISHED | | NULL | 2592000 | +-------+---------------+-------------------------+-------------------------+---------------+---------+---------------+---------------+---------------+----------+------+----------+---------+転置インデックス作成後、同じクエリ文を用いて文字列型カラム author が faster と等しい行数をカウントします。この操作には 0.01 秒かかり、転置インデックス作成前のクエリと比較して 2 秒の高速化が達成されました。
-- インデックス作成後、文字列の等価マッチングは顕著に高速化されます。 SELECT count() FROM hackernews_1m WHERE author = 'faster'; +---------+ | count() | +---------+ | 20 | +---------+ 1 row in set (0.01 sec)
トークン化関数
トークン化関数は、連続したテキストを個別のトークンまたはフレーズに分割します。これは、転置インデックスの構築および利用の中心となる機能です。トークン化の品質および方法は、インデックスの品質およびパフォーマンスに直接影響します。
テキスト文字列がどのようにトークン化されるか不明な場合は、TOKENIZE 関数で確認してください。TOKENIZE 関数は、parser および parser_mode の 2 つの主要なパラメーターを受け取ります。以下にそれぞれの説明を示します。
パラメーター | 説明 |
| トークナイザーを指定します。デフォルトではトークナイザーは適用されません。
|
| トークン化のパターンを指定します。トークン化の粒度はこのパターンに依存します。 すべてのトークナイザーは、デフォルトで粗粒度モード(coarse-grained mode)を使用します。粗粒度モードでは、長いトークンが優先されます。たとえば、
|
以下に例を示します。
-- 英語トークン化の結果。
SELECT TOKENIZE('I love CHINA','"parser"="english"');
+------------------------------------------------+
| tokenize('I love CHINA', '"parser"="english"') |
+------------------------------------------------+
| ["i", "love", "china"] |
+------------------------------------------------+
1 row in set (0.02 sec)
-- 中国語トークナイザーによる細粒度トークン化の結果。
SELECT TOKENIZE('武汉长江大桥','"parser"="chinese","parser_mode"="fine_grained"');
+-----------------------------------------------------------------------------------+
| tokenize('武汉长江大桥', '"parser"="chinese","parser_mode"="fine_grained"') |
+-----------------------------------------------------------------------------------+
| ["武汉", "武汉长江大桥", "长江", "长江大桥", "大桥"] |
+-----------------------------------------------------------------------------------+
1 row in set (0.02 sec)
-- 中国語トークナイザーによる粗粒度トークン化の結果。
SELECT TOKENIZE('武汉市长江大桥','"parser"="chinese","parser_mode"="coarse_grained"');
+----------------------------------------------------------------------------------------+
| tokenize('武汉市长江大桥', '"parser"="chinese","parser_mode"="coarse_grained"') |
+----------------------------------------------------------------------------------------+
| ["武汉市", "长江大桥"] |
+----------------------------------------------------------------------------------------+
1 row in set (0.02 sec)
-- 多言語混合トークン化の結果。
SELECT TOKENIZE('I love CHINA 我爱我的祖国','"parser"="unicode"');
+-------------------------------------------------------------------+
| tokenize('I love CHINA 我爱我的祖国', '"parser"="unicode"') |
+-------------------------------------------------------------------+
| ["i", "love", "china", "我", "爱", "我", "的", "祖", "国"] |
+-------------------------------------------------------------------+
1 row in set (0.02 sec)