選択率とは、述語条件を経た後に返される行数と、述語条件を経ずに返される行数の比率を指します。明らかに、選択率の値域は0から1です。値が小さいほど選択性が良いことを示し、選択率が1の場合は選択性が最も悪いことを意味します。OceanBaseデータベースのオプティマイザーは、統計情報と選択率計算ルールに基づいて述語の選択率を計算し、その後行数を推定します。
例えば、以下の例を見てください:
テーブル
t1を作成します。obclient> CREATE TABLE t1(c1 INT, c2 INT, c3 INT);テーブル
t1にテストデータを挿入します。obclient> INSERT INTO t1 SELECT mod(level, 10), mod(level, 100), mod(level, 1000) FROM DUAL CONNECT BY LEVEL <= 1000;実行結果は次のとおりです:
Query OK, 1000 rows affected Records: 1000 Duplicates: 0 Warnings: 0ユーザー
TESTのT1テーブルの統計情報を収集します。obclient> CALL DBMS_STATS.GATHER_TABLE_STATS('TEST','T1');ユーザー
TESTのT1テーブルの列統計情報を確認します。obclient> SELECT NUM_DISTINCT, LOW_VALUE, HIGH_VALUE FROM SYS.DBA_TAB_COL_STATISTICS WHERE owner = 'TEST' AND table_name = 'T1';実行結果は次のとおりです:
+--------------+-----------+------------+ | NUM_DISTINCT | LOW_VALUE | HIGH_VALUE | +--------------+-----------+------------+ | 10 | 0 | 9 | | 101 | 0 | 99 | | 1031 | 0 | 999 | +--------------+-----------+------------+ 3 rows in set
テーブル
t1を作成します。obclient> CREATE TABLE t1(c1 INT, c2 INT, c3 INT);テーブル
t1にテストデータを挿入します。変数を設定します。
obclient> SET @i = 0;データを挿入します。
obclient> INSERT INTO t1 (c1, c2, c3) SELECT MOD(@i := @i + 1, 10), MOD(@i, 100), MOD(@i, 1000) FROM information_schema.tables a, information_schema.tables b, information_schema.tables c LIMIT 1000;実行結果は次のとおりです:
Query OK, 1000 rows affected Records: 1000 Duplicates: 0 Warnings: 0t1のデータ件数を確認します。obclient> SELECT COUNT(*) FROM t1;実行結果は次のとおりです:
+----------+ | COUNT(*) | +----------+ | 1000 | +----------+ 1 row in set
データベース
db_testのt1テーブルの統計情報を収集します。obclient> CALL DBMS_STATS.GATHER_TABLE_STATS('db_test','t1');データベース
db_testのt1テーブルの列統計情報を確認します。obclient> SELECT NUM_DISTINCT, LOW_VALUE, HIGH_VALUE FROM oceanbase.DBA_TAB_COL_STATISTICS WHERE owner = 'db_test' AND table_name = 't1';実行結果は次のとおりです:
+--------------+-----------+------------+ | NUM_DISTINCT | LOW_VALUE | HIGH_VALUE | +--------------+-----------+------------+ | 10 | 0 | 9 | | 103 | 0 | 99 | | 1012 | 0 | 999 | +--------------+-----------+------------+ 3 rows in set
統計情報を収集した後、関連するビューを照会することで、c1 列の NDV が 10 であることが確認できます。したがって、クエリ Q1:SELECT * FROM t1 WHERE c1 = 10 において、c1 = 10 の選択率は 1/10 = 0.1 となります。推定される行数は 1000 * 1/10 = 100 です。以下の実行計画を用いて検証します:
Q1:
obclient> EXPLAIN SELECT * FROM t1 WHERE c1 = 10;
実行結果は次のとおりです:
+------------------------------------------------------------------------------------+
| Query Plan |
+------------------------------------------------------------------------------------+
| =============================================== |
| |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| |
| ----------------------------------------------- |
| |0 |TABLE FULL SCAN|t1 |100 |60 | |
| =============================================== |
| Outputs & filters: |
| ------------------------------------- |
| 0 - output([t1.c1], [t1.c2], [t1.c3]), filter([t1.c1 = 10]), rowset=256 |
| access([t1.c1], [t1.c2], [t1.c3]), partitions(p0) |
| is_index_back=false, is_global_index=false, filter_before_indexback[false], |
| range_key([t1.__pk_increment]), range(MIN ; MAX)always true |
+------------------------------------------------------------------------------------+
11 rows in set
同様に、クエリ Q2:SELECT * FROM t1 WHERE c1 = 10 AND c2 = 10 について、cardinality_estimation_model が partial の場合、Q2 の2つの述語の選択率は 1/101 * sqrt( 1/10) で、約 0.0031 となります。推定行数は切り上げて 4 となります。以下の実行計画を用いて検証します:
Q2:
obclient> EXPLAIN SELECT * FROM t1 WHERE c1 = 10 AND c2 = 10;
実行結果は次のとおりです:
+------------------------------------------------------------------------------------------+
| Query Plan |
+------------------------------------------------------------------------------------------+
| =============================================== |
| |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| |
| ----------------------------------------------- |
| |0 |TABLE FULL SCAN|T1 |4 |63 | |
| =============================================== |
| Outputs & filters: |
| ------------------------------------- |
| 0 - output([T1.C1], [T1.C2], [T1.C3]), filter([T1.C2 = 10], [T1.C1 = 10]), rowset=16 |
| access([T1.C1], [T1.C2], [T1.C3]), partitions(p0) |
| is_index_back=false, is_global_index=false, filter_before_indexback[false,false], |
| range_key([T1.__pk_increment]), range(MIN ; MAX)always true |
+------------------------------------------------------------------------------------------+
11 rows in set
選択率による行数推定は、現在のところOceanBaseデータベースのオプティマイザーにおける行数推定の主要な手法です。プラン生成時には、関連する演算子の述語条件が統計情報および選択率計算ルールに基づいて対応する選択率が計算され、最終的に全体の選択率に基づいて行数が推定されます。