選択率とは、述語条件を経て返される行数と述語条件を経ないで返される行数の比率を指します。明らかに、選択率の値域は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データベースオプティマイザーによる行数推定の主要な手法です。計画生成時には、関連する演算子の述語条件はすべて統計情報に基づいて計算され、その選択率の計算ルールに基づいて対応する選択率が計算され、最終的に全体の選択率に基づいて行数が推定されます。