LIMIT 述语は、データ出力の行数を制限するために使用されます。これはMySQLの LIMIT 述語と同じ機能です。
OceanBaseデータベースのMySQLモードで LIMIT を含むSQL文を処理する際、SQLオプティマイザーは常に LIMIT 述語を生成しますが、一部の特殊なシナリオでは割り当てません。例えば、LIMIT をベーステーブルに降格できる場合は、割り当てる必要はありません。
一方、OceanBaseデータベースのOracleモードでは、以下の2つのシナリオで LIMIT 述語が割り当てられます:
ROWNUMがSQLオプティマイザーによって書き換えられて生成された場合Oracle12cの
FETCH機能との互換性のため
MySQLモードにおけるLIMIT演算子を含むSQLのシナリオ
例1:OceanBaseデータベースのMySQLモードにおけるLIMIT演算子を含むSQLのシナリオ
obclient> CREATE TABLE t1(c1 INT, c2 INT);
Query OK, 0 rows affected
obclient> CREATE TABLE t2(c1 INT, c2 INT);
Query OK, 0 rows affected
obclient> INSERT INTO t1 VALUES(1, 1);
Query OK, 1 rows affected
obclient> INSERT INTO t1 VALUES(2, 2);
Query OK, 1 rows affected
obclient> INSERT INTO t1 VALUES(3, 3);
Query OK, 1 rows affected
obclient> INSERT INTO t2 VALUES(1, 1);
Query OK, 1 rows affected
obclient> INSERT INTO t2 VALUES(2, 2);
Query OK, 1 rows affected
obclient> INSERT INTO t2 VALUES(3, 3);
Query OK, 1 rows affected
Q1:
obclient> EXPLAIN SELECT t1.c1 FROM t1,t2 LIMIT 1 OFFSET 1;
Query Plan:
=====================================================
|ID|OPERATOR |NAME|EST. ROWS|COST |
-----------------------------------------------------
|0 |LIMIT | |1 |39 |
|1 | NESTED-LOOP JOIN CARTESIAN| |2 |39 |
|2 | TABLE SCAN |t1 |1 |36 |
|3 | TABLE SCAN |t2 |100000 |59654|
=====================================================
Outputs & filters:
-------------------------------------
0 - output([t1.c1]), filter(nil), limit(1), offset(1)
1 - output([t1.c1]), filter(nil),
conds(nil), nl_params_(nil)
2 - output([t1.c1]), filter(nil),
access([t1.c1]), partitions(p0)
3 - output([t2.__pk_increment]), filter(nil),
access([t2.__pk_increment]), partitions(p0)
Q2:
obclient> EXPLAIN SELECT * FROM t1 LIMIT 2;
Query Plan:
| ===================================
|ID|OPERATOR |NAME|EST. ROWS|COST|
-----------------------------------
|0 |TABLE SCAN|t1 |2 |37 |
===================================
Outputs & filters:
-------------------------------------
0 - output([t1.c1], [t1.c2]), filter(nil),
access([t1.c1], [t1.c2]), partitions(p0),
limit(2), offset(nil)
上記の例では、Q1クエリの実行計画表示におけるoutputs & filtersに、LIMIT演算子の出力情報が詳細に示されています。
情報名 |
意味 |
|---|---|
| output | この演算子が出力する式。 |
| filter | この演算子のフィルター条件。例では LIMIT 演算子に filter が設定されていないため、nil となります。 |
| limit | 出力する行数を制限する定数。 |
| offset | 現在の位置からのオフセット行数で、定数です。 |
Q2クエリの実行計画表示では、SQLにLIMITが含まれているものの、LIMIT演算子は割り当てられず、関連する式がTABLE SCAN演算子にプッシュダウンされています。例のSQLにはoffsetが含まれていないため、生成された計画ではnilとなっています。このようなLIMITのプッシュダウンは、SQLオプティマイザーの最適化手法の一つです。詳細については、TABLE SCANを参照してください。
OracleモードにおけるCOUNT演算子を含むSQLのLIMIT演算子への書き換え
OracleモードにおいてCOUNTを含むSQLをLIMITに書き換えるシナリオについては、COUNT演算子の章で既に説明しています。詳細については、COUNTを参照してください。
OracleモードにおけるFETCHを含むSQLのシナリオ
例2:OceanBaseデータベースのOracleモードにおけるFETCHを含むSQLのシナリオ
obclient> CREATE TABLE T1(c1 INT, c2 INT);
Query OK, 0 rows affected
obclient> CREATE TABLE T2(c1 INT, c2 INT);
Query OK, 0 rows affected
obclient> INSERT INTO t1 VALUES(1, 1);
Query OK, 1 rows affected
obclient> INSERT INTO t1 VALUES(2, 2);
Query OK, 1 rows affected
obclient> INSERT INTO t1 VALUES(3, 3);
Query OK, 1 rows affected
obclient> INSERT INTO t2 VALUES(1, 1);
Query OK, 1 rows affected
obclient> INSERT INTO t2 VALUES(2, 2);
Query OK, 1 rows affected
obclient> INSERT INTO t2 VALUES(3, 3);
Query OK, 1 rows affected
Q3:
obclient> EXPLAIN SELECT * FROM t1,t2 OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY;
Query Plan:
| =====================================================
|ID|OPERATOR |NAME|EST. ROWS|COST |
-----------------------------------------------------
|0 |LIMIT | |1 |238670 |
|1 | NESTED-LOOP JOIN CARTESIAN| |2 |238669 |
|2 | TABLE SCAN |T1 |1 |36 |
|3 | MATERIAL | |100000 |238632 |
|4 | TABLE SCAN |T2 |100000 |64066|
=====================================================
Outputs & filters:
-------------------------------------
0 - output([T1.C1], [T1.C2], [T2.C1], [T2.C2]), filter(nil), limit(?), offset(?)
1 - output([T1.C1], [T1.C2], [T2.C1], [T2.C2]), filter(nil),
conds(nil), nl_params_(nil)
2 - output([T1.C1], [T1.C2]), filter(nil),
access([T1.C1], [T1.C2]), partitions(p0)
3 - output([T2.C1], [T2.C2]), filter(nil)
4 - output([T2.C1], [T2.C2]), filter(nil),
access([T2.C1], [T2.C2]), partitions(p0)
Q4:
obclient> EXPLAIN SELECT * FROM t1 FETCH NEXT 1 ROWS ONLY;
Query Plan:
| ===================================
|ID|OPERATOR |NAME|EST. ROWS|COST|
-----------------------------------
|0 |TABLE SCAN|T1 |1 |37 |
===================================
Outputs & filters:
-------------------------------------
0 - output([T1.C1], [T1.C2]), filter(nil),
access([T1.C1], [T1.C2]), partitions(p0),
limit(?), offset(nil)
Q5:
obclient> EXPLAIN SELECT * FROM t2 ORDER BY c1 FETCH NEXT 10 PERCENT ROW WITH TIES;
Query Plan:
| =======================================
|ID|OPERATOR |NAME|EST. ROWS|COST |
---------------------------------------
|0 |LIMIT | |10000 |573070|
|1 | SORT | |100000 |559268|
|2 | TABLE SCAN|T2 |100000 |64066 |
=======================================
Outputs & filters:
-------------------------------------
0 - output([T2.C1], [T2.C2]), filter(nil), limit(nil), offset(nil), percent(?), with_ties(true)
1 - output([T2.C1], [T2.C2]), filter(nil), sort_keys([T2.C1, ASC])
2 - output([T2.C1], [T2.C2]), filter(nil),
access([T2.C1], [T2.C2]), partitions(p0)
上記の例では、Q3とQ4のクエリの実行計画表示は、以前のMySQLモードのQ1とQ2のクエリと基本的に同じです。これは、Oracle 12cのFETCH機能がMySQLのLIMIT機能に類似しているためです。両者の違いは、Q5の実行計画表示に示されています。
実行計画表示のoutputs & filtersには、LIMIT演算子の出力情報が詳細に列挙されています:
情報名 |
意味 |
|---|---|
| output | この演算子が出力する式。 |
| filter | この演算子のフィルター条件。例では LIMIT 演算子に filter が設定されていないため、nil となります。 |
| limit | 出力する行数を制限する定数です。 |
| offset | 現在の位置からのオフセット行数で、定数です。 |
| percent | データの総行数に対する割合で出力する定数です。 |
| with_ties | ソート後に最後の行が同値である場合、それらをまとめて出力するかどうかを指定します。例えば、最後の行を出力するよう指定しても、ソート後に2行の値が1となる場合があります。このとき、with_tiesを指定すれば、これら2行も一緒に出力されます。 |
上記のLIMIT演算子に追加された計画表示プロパティは、すべてOracleモードのFETCH機能特有のものであり、MySQLモードの計画には影響しません。Oracle12cのFETCH構文の詳細については、Oracle 12c Fetch Rowsを参照してください。