1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD html 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
4 <title>pg_hint_plan</title>
5 <!-- Uncoment after the tool has been hosted somewhere.
6 <link rel="home" title="pg_hint_plan" href="index.html">
8 <link rel="stylesheet" type="text/css" href="style.css">
9 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
13 <h1 id="pg_hint_plan">pg_hint_plan 1.2</h1>
14 <div class="navigation">
15 <a href="pg_hint_plan-ja.html">pg_hint_plan</a>
21 <li><a href="#name">名前</a></li>
22 <li><a href="#synopsis">概要</a></li>
23 <li><a href="#description">機能説明</a>
24 <li><a href="#install">インストール</a></li>
25 <li><a href="#uninstall">アンインストール</a></li>
26 <li><a href="#restrictions">使用上の注意と制約</a></li>
27 <li><a href="#requirement">動作環境</a></li>
28 <li><a href="#seealso">関連項目</a></li>
29 <li><a href="hint_list-ja.html">Appendix A. ヒント句一覧</a></li>
33 <p>pg_hint_plan -- 実行計画を示すヒントをクエリに指定することで、SQL文やGUCパラメータを変えずに実行計画を制御します。</p>
35 <h2 id="synopsis">概要</h2>
36 <p>PostgreSQLのプランナははコストベースでのオプティマイズを行なっており、SQL文と統計情報を元に可能な実行計画のコストを見積もり、最もコストの低い実行計画を選択します。プランナは可能な限りよい実行計画を作成しようとしますが、例えばカラム間の相関関係などは考慮しないため、複雑なクエリでは常に最適なプランを選択するとは限りません。</p>
37 <p>pg_hint_planを用いると、ヒントでスキャン方式や結合方式を指定することで、SQL文やGUCパラメータを変更することなく実行計画を制御することができます。</p>
39 <h2 id="description">機能説明</h2>
40 <p>pg_hint_planの機能について説明するにあたり、まず文中で使用されている用語について説明します。</p>
44 <tr><th>用語</th><th>説明</th></tr>
48 <td>実行計画を制御するための情報です。</td></tr>
50 <td>実行計画を制御したいクエリに適用するヒント句を列挙したものです。</td></tr>
54 <h3 id="hint-rule">ヒントの指定方法</h3>
55 <p>ヒントは二つの方法で指定することができます。</p>
58 <p>特殊なSQLブロックコメント内にヒントを記述します。</p>
60 <p>ヒント用のテーブルにヒントを登録します。</p>
62 <p>特定のアプリケーションではヒントをコメントで指定することができないため、「テーブルでの指定」でヒントを指定します。なお、「コメントでの指定」と異なり、アプリケーションのソースコードに手を入れずに指定するヒントを変更することができます。</p>
64 <h4 id="hint-comment">コメントでの指定</h4>
65 <p>指定したいヒントを、実行計画を制御したいクエリの先頭または途中のSQLブロックコメントの中に記述します。</p>
66 <p>ヒント用コメントと通常のコメントを区別するために、ヒント用のブロックコメントは「<span class="bold">/*+</span>」で始めます。ヒントの対象は、カッコ内にオブジェクト名または別名で指定します。オブジェクト名は、スペース、タブ、または改行のいずれかで区切って指定します。</p>
67 <p>以下の例では、HashJoinとSeqScanヒント句により、pgbench_accountsテーブルに対するSeq Scanの結果をHash Joinする実行計画が選択されています。なおかつ、Setヒント句によりこのクエリの実行計画を作成する間だけrandom_page_costが2.0に変更されています。</p>
69 <b>postgres=#</b> EXPLAIN (VERBOSE, COSTS)
71 <b>postgres*# SeqScan(a)</b>
72 <b>postgres*# HashJoin(a b)</b>
73 <b>postgres*# Set(random_page_cost 2.0)</b>
75 <b>postgres-#</b> SELECT *
76 <b>postgres-#</b> FROM pgbench_accounts a
77 <b>postgres-#</b> JOIN pgbench_branches b
78 <b>postgres-#</b> ON a.bid = b.bid
79 <b>postgres-#</b> ORDER BY a.aid
80 <b>postgres-#</b> LIMIT 10;
82 ----------------------------------------------------------------------------------------------------
83 Limit (cost=6176.99..6177.01 rows=10 width=461)
84 Output: a.aid, a.bid, a.abalance, a.filler, b.bid, b.bbalance, b.filler, a.aid
85 -> Sort (cost=6176.99..6426.99 rows=100000 width=461)
86 Output: a.aid, a.bid, a.abalance, a.filler, b.bid, b.bbalance, b.filler, a.aid
88 -> <span class="strong">Hash Join</span> (cost=1.02..4016.02 rows=100000 width=461)
89 Output: a.aid, a.bid, a.abalance, a.filler, b.bid, b.bbalance, b.filler, a.aid
90 Hash Cond: (a.bid = b.bid)
91 -> <span class="strong">Seq Scan on public.pgbench_accounts a</span> (cost=0.00..2640.00 rows=100000 width=97)
92 Output: a.aid, a.bid, a.abalance, a.filler
93 -> Hash (cost=1.01..1.01 rows=1 width=364)
94 Output: b.bid, b.bbalance, b.filler
95 -> Seq Scan on public.pgbench_branches b (cost=0.00..1.01 rows=1 width=364)
96 Output: b.bid, b.bbalance, b.filler
102 <h4 id="hint-table">テーブルでの指定</h4>
103 <p>指定したいヒントを、実行計画を制御したいクエリと併せてヒント用のテーブルに登録します。</p>
104 <p>デフォルトでは無効なので、テーブルで指定したヒントは適用されません。そのため、ヒントをテーブルで指定する場合は、<a href="#hint-GUC">pg_hint_planのGUCパラメータ</a>pg_hint_plan.enable_hint_tableの設定を変更します。</p>
105 <p>ヒント用のテーブルは「<span class="bold">hint_plan.hints</span>」です。hint_plan.hintsテーブルには、以下の情報を登録します。</p>
109 <tr><th>列名</th><th>説明</th></tr>
113 <td>ユーザがヒントを識別するための番号です。連番型ですので、新しいヒントを登録するときにはこの列を指定しないでください。</td></tr>
114 <tr><td>norm_query_string</td>
115 <td>実行計画を制御したいクエリを指定します。対象のクエリに定数があるときは、下記の例のように「?」に置き換えます。キーワード間の空白の数が、登録するクエリと実行するクエリで異なると別のSQL文として扱われます。</td></tr>
116 <tr><td>application_name</td>
117 <td>ヒント適用対象のアプリケーション名を指定します。下記の例では「psql」から実行されたクエリのみがヒント適用対象となります。全てのアプリケーションにヒントを適用したいときは、空文字列を登録します。アプリケーション名は、セッションの「application_name」GUCパラメータと等しいか判断します。</td></tr>
119 <td>ヒントを指定します。SQLコメント記号を除いた内容のみを登録します。</td></tr>
122 <p>ヒントの登録情報を変更する場合は、変更したい登録情報のidを指定して登録情報を更新してください。</br>
123 ヒントの登録を解除する場合は、解除したい登録情報のidを指定してテーブルから削除してください。</p>
124 <p>以下の例では、ヒントの登録、ヒント登録情報の変更、ヒントの解除の順に、クエリの実行結果を示しています。
127 <b>postgres=#</b> INSERT INTO hint_plan.hints(norm_query_string, application_name, hints)
128 <b>postgres-#</b> VALUES (
129 <b>postgres(#</b> 'EXPLAIN (COSTS false) SELECT * FROM t1 WHERE t1.id = ?;',
130 <b>postgres(#</b> '',
131 <b>postgres(#</b> 'SeqScan(t1)'
134 <b>postgres=#</b> UPDATE hint_plan.hints
135 <b>postgres-#</b> SET hints = 'IndexScan(t1)'
136 <b>postgres-#</b> WHERE id = 1;
138 <b>postgres=#</b> DELETE FROM hint_plan.hints
139 <b>postgres-#</b> WHERE id = 1;
143 <p>なお、ヒントの登録や変更や解除はスーパーユーザでのみ可能です。一般ユーザでヒントの登録や変更や解除を実施したい場合は、スーパーユーザから一般ユーザにhint_plan.hintsテーブルの各権限を付与してください。</p>
144 <p>以下の例では、hint_plan.hintsテーブルに対してINSERT権限を持たない一般ユーザがヒントの登録を実施しようとしたときの結果を示しています。</p>
146 <b>postgres=></b> INSERT INTO hint_plan.hints(norm_query_string, application_name, hints)
147 <b>postgres-></b> VALUES (
148 <b>postgres(></b> 'EXPLAIN (COSTS false) SELECT * FROM t1 WHERE t1.id = ?;',
149 <b>postgres(></b> '',
150 <b>postgres(></b> 'SeqScan(t1)'
152 ERROR: permission denied for relation hints
155 <p>以下の例では、テーブルに登録した「コメントでの指定」の例と同じヒントと、登録したクエリの実行結果を示しています。</p>
157 <b>postgres=#</b> SELECT * FROM hint_plan.hints;
158 id | norm_query_string | application_name | hints
159 ----+----------------------------+------------------+--------------------------------------------------
160 1 | EXPLAIN (VERBOSE, COSTS) +| psql | SeqScan(a)HashJoin(a b)Set(random_page_cost 2.0)
162 | FROM pgbench_accounts a+| |
163 | JOIN pgbench_branches b+| |
164 | ON a.bid = b.bid +| |
165 | ORDER BY a.aid +| |
170 <b>postgres=#</b> SET pg_hint_plan.enable_hint_table TO on;
171 <b>postgres=#</b> EXPLAIN (VERBOSE, COSTS)
172 <b>postgres-#</b> SELECT *
173 <b>postgres-#</b> FROM pgbench_accounts a
174 <b>postgres-#</b> JOIN pgbench_branches b
175 <b>postgres-#</b> ON a.bid = b.bid
176 <b>postgres-#</b> ORDER BY a.aid
177 <b>postgres-#</b> LIMIT 10;
179 ----------------------------------------------------------------------------------------------------
180 Limit (cost=6176.99..6177.01 rows=10 width=461)
181 Output: a.aid, a.bid, a.abalance, a.filler, b.bid, b.bbalance, b.filler, a.aid
182 -> Sort (cost=6176.99..6426.99 rows=100000 width=461)
183 Output: a.aid, a.bid, a.abalance, a.filler, b.bid, b.bbalance, b.filler, a.aid
185 -> <b><u>Hash Join</u></b> (cost=1.02..4016.02 rows=100000 width=461)
186 Output: a.aid, a.bid, a.abalance, a.filler, b.bid, b.bbalance, b.filler, a.aid
187 Hash Cond: (a.bid = b.bid)
188 -> <b><u>Seq Scan on public.pgbench_accounts a</u></b> (cost=0.00..2640.00 rows=100000 width=97)
189 Output: a.aid, a.bid, a.abalance, a.filler
190 -> Hash (cost=1.01..1.01 rows=1 width=364)
191 Output: b.bid, b.bbalance, b.filler
192 -> Seq Scan on public.pgbench_branches b (cost=0.00..1.01 rows=1 width=364)
193 Output: b.bid, b.bbalance, b.filler
200 <span class="strong">指定方法の優先度</span>
201 <p>ヒントをコメントとテーブルの両方で指定した場合、テーブルで指定したヒントが適用され、コメントで指定したヒントは無視されます。</p>
202 <p>以下の例では、コメントでヒントを指定して実行計画を制御しているクエリに対して、テーブルに空文字列のヒントを登録しています。テーブルで指定したヒントが優先されるので、コメントで指定したヒントを取り消すことができます。</p>
204 postgres=# select * from hint_plan.hints;
205 id | norm_query_string | application_name | hints
206 ----+--------------------------------------------+------------------+-------
207 1 | EXPLAIN (VERBOSE, COSTS) +| psql |
213 | FROM pgbench_accounts a +| |
214 | JOIN pgbench_branches b ON a.bid = b.bid+| |
215 | ORDER BY a.aid; | |
218 postgres=# EXPLAIN (VERBOSE, COSTS)
220 postgres*# HashJoin(a b)
221 postgres*# SeqScan(a)
224 postgres-# FROM pgbench_accounts a
225 postgres-# JOIN pgbench_branches b ON a.bid = b.bid
226 postgres-# ORDER BY a.aid;
228 ----------------------------------------------------------------------------------------------------------------------
229 <span class="strong">Nested Loop</span> (cost=0.00..5750.47 rows=100000 width=461)
230 Output: a.aid, a.bid, a.abalance, a.filler, b.bid, b.bbalance, b.filler, a.aid
231 Join Filter: (a.bid = b.bid)
232 -> <span class="strong">Index Scan using pgbench_accounts_pkey on public.pgbench_accounts a</span> (cost=0.00..4249.45 rows=100000 width=97)
233 Output: a.aid, a.bid, a.abalance, a.filler
234 -> Materialize (cost=0.00..1.01 rows=1 width=364)
235 Output: b.bid, b.bbalance, b.filler
236 -> Seq Scan on public.pgbench_branches b (cost=0.00..1.01 rows=1 width=364)
237 Output: b.bid, b.bbalance, b.filler
242 <h3 id="hint-group">ヒント句のグループ</h3>
243 <p>pg_hint_planで使えるヒント句の種類は、スキャン方式と結合方式、結合順序、見積もり件数補正、並列実行の設定、GUCパラメータの6グループです。各グループの具体的なヒント句は、<a href="hint_list-ja.html">ヒント句一覧</a>を参照してください。</p>
246 <p>あるオブジェクトでどのスキャン方式を選択するかを指定できるヒント句のグループです。「SeqScan」や「IndexScan」などを含みます。</p>
247 <p>スキャン方式を指定できるオブジェクトは、通常のテーブル、継承テーブル、UNLOGGEDテーブル、一時テーブル、システムカタログです。スキャン方式を指定できないオブジェクトは、外部テーブル、テーブル関数、VALUESコマンド結果、CTE(共通テーブル式)、ビュー、副問い合わせ結果です。</p>
248 <p>特定のオブジェクトについてあるスキャン方式を選択して欲しい場合は、そのスキャン方式のヒント句と、対象となるオブジェクトの名前を指定します。逆に、特定のオブジェクトについてあるスキャン方式を選択して欲しくない場合は、Noで始まるヒント句を指定します。同じオブジェクトに対して複数のスキャン方式のヒント句を指定した場合は、最後に指定したヒント句が適用されます。</p>
251 <p>あるオブジェクトの組み合わせでどの結合方式を選択するかを指定できるヒント句のグループです。「MergeJoin」や「NestLoop」などを含みます。</p>
252 <p>結合方式を指定できるオブジェクトは、通常のテーブル、継承テーブル、UNLOGGEDテーブル、一時テーブル、外部テーブル、システムカタログ、テーブル関数、VALUESコマンド結果、CTE(共通テーブル式)です。結合方式を指定できないオブジェクトは、ビュー、副問い合わせ結果です。</p>
253 <p>特定のオブジェクトの組み合わせについてある結合方式を選択して欲しい場合は、その結合方式のヒント句と、対象となる2つ以上のオブジェクトの名前を指定します。逆に、特定のオブジェクトの組み合わせについてある結合方式を選択して欲しくない場合は、Noで始まるヒント句を指定します。同じオブジェクトの組み合わせに対して複数の結合方式のヒント句を指定した場合は、最後に指定したヒント句が適用されます。</p>
256 <p>あるオブジェクトの組み合わせでどのような順番で結合するかを指定できるヒント句のグループです。「Leading」のみを含みます。</p>
257 <p>結合順序を指定できるオブジェクトは結合方式と同じです。</p>
258 <p>先に結合して欲しいオブジェクトから順にオブジェクト名または別名を指定します。同じ問合せブロックのオブジェクトに対して複数の結合順序のヒント句を指定した場合は、最後に指定したヒント句が適用されます。 </p>
260 <p>結合対象のテーブルが3つ以上ある場合、結合方式のヒント句を指定したとしてもコスト見積もりによっては対象のテーブルが直接結合されないことがあります。対象のテーブルが直接結合されない場合は、結合順序のヒント句を併せて指定します。</p>
261 <p>以下の例では、table1とtable2を直接結合する場合はNested Loopを、table1とtable2とtable3を結合する場合はMerge Joinを指定しています。また、コスト見積もりによってはtable1とtable2が直接結合されない場合を避けるため、table1とtable2を結合してからtable3を結合するようにLeadingヒント句を併用しています。</p>
263 <b>postgres=# /*+</b>
264 <b>postgres*# NestLoop(t1 t2)</b>
265 <b>postgres*# MergeJoin(t1 t2 t3)</b>
266 <b>postgres*# Leading(t1 t2 t3)</b>
268 <b>postgres-#</b> SELECT * FROM table1 t1
269 <b>postgres-#</b> JOIN table table2 t2 ON (t1.key = t2.key)
270 <b>postgres-#</b> JOIN table table3 t3 ON (t2.key = t3.key);
275 <p>上記の結合順序の指定を行なった際にはプランナの都合により結合方向(外部表/内部表もしくは駆動表/被駆動表の別)が期待とは異なるものになる場合があります。このような状況に対して結合方向を固定したい場合はもうひとつの書式を使う必要があります。</p>
277 <b>postgres=# /*+ Leading((t1 (t2 t3))) */</b> SELECT...
279 <p>この書式では2つの要素を丸括弧で囲ったものがネストする形になっており、一つの括弧内では1つ目の要素が外部/駆動表、2番めの要素が内部/被駆動表として結合されます。</p>
281 <p>あるオブジェクトの結合結果の件数を補正できるヒント句のグループです。「Rows」のみを含みます。</p>
282 <p>見積もり件数補正対象として指定できるオブジェクトは結合方式と同じです。補正できるのは結合結果の見積もり件数だけで、スキャンの見積もり件数を補正することはできません。</p>
283 <p>以下の例では、テーブルaとテーブルbの結合結果の件数を、ヒント句で指定した値に補正しています。</p>
285 <b>postgres=# /*+ Rows(a b #10) */</b>
286 <b>postgres-#</b> EXPLAIN SELECT *
287 <b>postgres-#</b> FROM pgbench_branches b
288 <b>postgres-#</b> JOIN pgbench_accounts a ON b.bid = a.bid
289 <b>postgres-#</b> WHERE aid < 20
290 <b>postgres-#</b> ORDER BY a.aid;
292 --------------------------------------------------------------------------------------------------------
293 Nested Loop (cost=0.29..9.92 rows=<b><u>10</u></b> width=461)
294 Join Filter: (b.bid = a.bid)
295 -> Index Scan using pgbench_accounts_pkey on pgbench_accounts a (cost=0.29. .8.62 rows=19 width=97)
296 Index Cond: (aid < 20)
297 -> Materialize (cost=0.00..1.01 rows=1 width=364)
298 -> Seq Scan on pgbench_branches b (cost=0.00..1.01 rows=1 width=364)
303 <p>スキャンの並列実行の方法を指定します。最初のパラメータは対象とするオブジェクトの指定で、2番目に指定する数だけ並列処理ワーカを起動するように指定します。
304 <p>3番目のパラメータは強制の程度を指定します。 "soft" は max_parallel_workers_per_gather のみを変更して適用はプランナに任せます。"hard" は他のプランナ変数も変更するなどしてワーカ数を強制します。</p>
305 <p>以下の例では、テーブル c1 とテーブル c2 の結合で各々異なるワーカ数を強制します。</p>
307 <b>postgres=#</b> explain <b>/*+ Parallel(c1 3 hard) Parallel(c2 5 hard) */</b>
308 select c2.a from c1 join c2 on (c1.a = c2.a);
310 -------------------------------------------------------------------------------
311 Hash Join (cost=2.86..11406.38 rows=101 width=4)
312 Hash Cond: (c1.a = c2.a)
313 -> Gather (cost=0.00..7652.13 rows=1000101 width=4)
314 <b><u>Workers Planned: 3</u></b>
315 -> Parallel Seq Scan on <b><u>c1</u></b> (cost=0.00..7652.13 rows=322613 width=4)
316 -> Hash (cost=1.59..1.59 rows=101 width=4)
317 -> Gather (cost=0.00..1.59 rows=101 width=4)
318 <b><u>Workers Planned: 5</u></b>
319 -> Parallel Seq Scan on <b><u>c2</u></b> (cost=0.00..1.59 rows=59 width=4)
323 <p>そのクエリの実行計画を作成している間だけGUCパラメータを変更できるヒント句のグループです。「Set」のみを含みます。</p>
324 <p>設定したいGUCパラメータとそのパラメータの値を指定します。指定できるGUCパラメータは<a href="http://www.postgresql.jp/document/current/html/runtime-config-query.html">問い合わせ計画</a>のGUCパラメータのみです。同じGUCパラメータのヒント句を2回以上指定した場合は、最後に指定したヒント句が適用されます。</p>
326 <span class="strong">Setヒント句の制限</span>
327 <p>Setヒント句に問い合わせ計画のGUCパラメータ以外を指定した場合の動作は保証できません。問い合わせ計画以外のGUCパラメータを指定した場合の例の一つとして、<a href="#hint-GUC">pg_hint_planのGUCパラメータ</a>を指定した場合の動作を、以下に示します。<p>
329 <li>pg_hint_plan.enable_hint、pg_hint_plan.enable_hint_tableをSetヒント句の対象に指定した場合は、無効になります。なお、pg_hint_plan.debug_printをonに設定していると、そのヒント句はログ出力で「適用されたヒント句」として出力されます。</li>
330 <li>pg_hint_plan.debug_printをSetヒント句の対象に指定した場合は、指定したとおりに適用されます。</li>
331 <li>pg_hint_plan.parse_messagesをSetヒント句の対象に指定した場合は、構文エラーと一部のSetヒント句のエラーのメッセージはクエリ開始時の設定レベルで出力され、それ以外のメッセージはSetヒント句で指定したレベルで出力されます。</li>
335 <h3 id="hint-GUC">pg_hint_planのGUCパラメータ</h3>
336 <p>pg_hint_planの動作を制御するGUCパラメータを以下に記述します。</p>
340 <tr><th>GUCパラメータ</th><th>説明</th><th>デフォルト値</th></tr>
343 <tr><td>pg_hint_plan.enable_hint</td>
344 <td>pg_hint_planの機能を有効または無効にします。</td><td>on</td></tr>
345 <tr><td>pg_hint_plan.enable_hint_table</td>
346 <td>ヒントをテーブルで指定する機能を有効または無効にします。</td><td>off</td></tr>
347 <tr><td>pg_hint_plan.parse_messages</td>
348 <td>指定したヒントを解釈できなかった場合のログメッセージのレベルを指定します。有効な値は、debug5、debug4、debug3、debug2、debug1、log、info、notice、warning、またはerrorです。</td><td>INFO</td></tr>
349 <tr><td>pg_hint_plan.debug_print</td>
350 <td>動作状況を示すログメッセージの出力を制御します。指定可能な値は off, on, verbose, detailed です。</td><td>off</td></tr>
351 <tr><td>pg_hint_plan.message_elevel</td>
352 <td>動作ログメッセージのログレベルを指定します。有効な値は、debug5、debug4、debug3、debug2、debug1、log、info、notice、warning、またはerrorです。</td><td>INFO</td></tr>
356 <h2 id="install">インストール</h2>
357 <p>pg_hint_planのインストール方法について説明します。</p>
359 <h3 id="build">ビルド</h3>
360 <p>pg_hint_planをソースコードからビルドする場合、pg_hint_planのソースを展開したディレクトリでmake → make installの順に実行します。make installはPostgreSQLをインストールしたOSユーザで実行します。なお、pg_hint_planのビルドにはpgxsを使用するので、RPM版のPostgreSQLを使用している環境では、postgresql-devel パッケージが必要です。</p>
361 <p>以下にビルドの例を示します。</p>
363 $ tar xzvf pg_hint_plan-1.0.0.tar.gz
364 $ cd pg_hint_plan-1.0.0
370 <h3 id="create-extension">データベースへの登録</h3>
371 <p>pg_hint_planはPostgreSQLの拡張(EXTENSION)を使用しているので、pg_hint_planを利用するデータベースにスーパーユーザもしくはそのデータベースの所有者で接続してCREATE EXTENSIONコマンドを実行します。</p>
372 <p>以下にデータベースへの登録の例を示します。 <i>dbname</i>は対象となるデータベース名を意味します。</p>
374 $ psql -d <i>dbname</i> -c "CREATE EXTENSION pg_hint_plan"
377 <h3 id="hint-load">pg_hint_planのロード</h3>
378 <p>特定のセッションでのみpg_hint_planを使う場合は、以下の例のようにpg_hint_planの共有ライブラリをLOADコマンドでロードします。一般ユーザで利用する場合は$libdir/pluginsにもインストールする必要があるので注意してください。
380 postgres=# LOAD 'pg_hint_plan';
382 postgres=# </pre></p>
383 <p>全てのセッションでpg_hint_planを有効にするには、shared_preload_libraries GUCパラメータに'pg_hint_plan'を追加してからサーバを再起動します。</p>
384 <p>注意: pg_hint_planを<a href="#create-extension">データベースに登録</a>せずに、ロード後にSQL文を実行すると以下に示す例のようなエラーとなります。 pg_hint_planを使うときは、データベースへの登録を忘れないように注意してください。</p>
386 postgres=# EXPLAIN SELECT * FROM pgbench_accounts a WHERE aid = 1;
387 ERROR: schema "hint_plan" does not exist
388 LINE 1: SELECT hints FROM hint_plan.hints WHERE norm_query_string...
390 QUERY: SELECT hints FROM hint_plan.hints WHERE norm_query_string = $1 AND ( application_name = $2 OR application_name = '' ) ORDER BY application_name DESC
394 <h2 id="uninstall">アンインストール</h2>
395 <p>pg_hint_planをアンインストールするときは、以下の手順を実行します。 <i>dbname</i>は対象となるデータベース名を意味します。</p>
397 <li>pg_hint_planのソースを展開したディレクトリでmake uninstallを実行します。make uninstallはPostgreSQLをインストールしたOSユーザで実行します。</li>
399 $ cd pg_hint_plan-1.0.0
403 <li>pg_hint_planを登録したデータベースにスーパーユーザもしくはそのデータベースの所有者で接続して、DROP EXTENSIONコマンドおよびDROP SCHEMAコマンドを実行します。</li>
405 $ psql -d <i>dbname</i> -c "DROP EXTENSION pg_hint_plan"
406 $ psql -d <i>dbname</i> -c "DROP SCHEMA hint_plan"
410 <h2 id="restrictions">ヒントの指定に関する詳細</h2>
411 <p>以下にいくつかのトピックに関してヒントの動作について説明します。</p>
413 <dt><h3>コメント指定でのヒント記述位置と文法</h3></dt>
414 <dd>ヒントはクエリの先頭または途中に記述できます。ただし、ヒントをクエリの途中に記述する場合、ヒント用のブロックコメントより前に以下の文字<span class="strong">以外</span>が含まれると、「/*+」で始まるコメントでもヒントと見なされず無視されます。
416 <li>空白文字(半角スペース、水平/垂直タブ、改行、フォームフィード、復帰)</li>
417 <li>アルファベット(大文字/小文字)</li>
421 <li>開き括弧(()、閉じ括弧())</li>
423 <p>指定したヒントが無視される例を以下に示します。</p>
425 <li>二重引用符(")がヒント用のブロックコメントより前に含まれている場合</li>
427 postgres=# SELECT bid AS "BID"
429 postgres*# SeqScan(b)
431 postgres-# FROM pgbench_branches b;
434 <li>演算子(>=)がヒント用のブロックコメントより前に含まれている場合</li>
436 postgres=# WITH avg_aid AS (
437 postgres(# SELECT avg(aid) FROM pgbench_history h
438 postgres(# WHERE delta >= 0
441 postgres*# SeqScan(h)
443 postgres-# SELECT * FROM avg_aid;
447 一つのクエリに複数のブロックコメントを記述する場合は、最初のブロックコメントにのみヒントを記述してください。二番目以降のブロックコメントは、ヒントと見なされず無視されます。以下の例では、HashJoin(a b)とSeqScan(a)がヒントと見なされ、IndexScan(a)とMergeJoin(a b)は無視されています。</p>
450 postgres*# <span class="strong">HashJoin(a b)</span>
451 postgres*# <span class="strong">SeqScan(a)</span>
453 postgres-# /*+ IndexScan(a) */
454 postgres-# EXPLAIN SELECT /*+ MergeJoin(a b) */ *
455 postgres-# FROM pgbench_branches b
456 postgres-# JOIN pgbench_accounts a ON b.bid = a.bid
457 postgres-# ORDER BY a.aid;
459 ---------------------------------------------------------------------------------------
460 Sort (cost=31465.84..31715.84 rows=100000 width=197)
462 -> <span class="strong">Hash Join</span> (cost=1.02..4016.02 rows=100000 width=197)
463 Hash Cond: (a.bid = b.bid)
464 -> <span class="strong">Seq Scan on pgbench_accounts a</span> (cost=0.00..2640.00 rows=100000 width=97)
465 -> Hash (cost=1.01..1.01 rows=1 width=100)
466 -> Seq Scan on pgbench_branches b (cost=0.00..1.01 rows=1 width=100)
471 <dt><h3>PL/pgSQL中の個別のクエリに対するヒント</h3></dt>
472 <dd>ヒントは、PL/pgSQLブロック中の各クエリにも指定できます。ただし、以下のような制約があります。
474 <li>指定したヒントが適用されるSQLは以下の通りです。他のSQLにヒントを指定しても適用されません。
476 <li>1行の結果を返す問い合わせ文(SELECT/INSERT/UPDATE/DELETE)</li>
477 <li>複数行の結果を返す問い合わせ文(RETURN QUERY)</li>
478 <li>動的SQL文(EXECUTE)</li>
479 <li>カーソルを開く文(OPEN)</li>
480 <li>問い合わせの結果を用いる繰り返し文(FOR)</li>
483 <li>ヒントをコメントで指定する場合は、SELECTなどのSQLキーワードより後に指定してください。ただし、動的SQL文にヒントをコメントで指定しても、そのヒントは適用されません。</li>
485 <p>以下の例では、一つ目のクエリにNoIndexScanを、二つ目のクエリにSeqScanをそれぞれ指定しています。</p>
487 postgres=# CREATE FUNCTION hints_func(integer) RETURNS integer AS $$
489 postgres$# id integer;
490 postgres$# cnt integer;
492 postgres$# SELECT <b><u>/*+ NoIndexScan(a) */</u></b> aid
493 postgres$# INTO id FROM pgbench_accounts a WHERE aid = $1;
494 postgres$# SELECT <b><u>/*+ SeqScan(a) */</u></b> count(*)
495 postgres$# INTO cnt FROM pgbench_accounts a;
496 postgres$# RETURN id + cnt;
498 postgres$# $$ LANGUAGE plpgsql;
501 <dt><h3>オブジェクト名の引用符付け</h3></dt>
502 <dd>ヒントに記述するオブジェクト名や別名が括弧((、)のいずれか)、二重引用符(")、空白(スペース、タブ、改行のいずれか)を含む場合は、通常のSQL文で使う場合と同じように二重引用符(")で囲んでください。二重引用符を含むオブジェクト名は、全体を二重引用符で括ったうえで、内部に含む二重引用符を二重引用符でエスケープしてください(例: 「quoted"table"name」→「"quoted""table""name"」)。</dd>
503 <dt><h3>同一名称テーブルの区別</h3></dt>
504 <dd>スキーマ違いや同一テーブルの複数回使用などでクエリ中に同一名称のテーブルが複数回出現する場合は、テーブルに別名をつけてそれぞれのテーブルを区別してください。以下の例の1つ目のSQL文では、HashJoin(t1 t1)をヒントに指定したとき、ヒント句対象のオブジェクトが特定できずにエラーになっています。2つ目のSQL文では、各テーブルにptやstという別名をつけているため、実行計画作成時にヒントで指定した通りにHash Joinを選択しています。</p>
506 postgres=# /*+ <span class="strong">HashJoin(t1 t1)</span>*/
507 postgres-# EXPLAIN SELECT * FROM s1.t1
508 postgres-# JOIN public.t1 ON (s1.t1.id=public.t1.id);
509 INFO: hint syntax error at or near "HashJoin(t1 t1)"
510 <span class="strong">DETAIL: Relation name "t1" is ambiguous.</span>
512 ------------------------------------------------------------------
513 Merge Join (cost=337.49..781.49 rows=28800 width=8)
514 Merge Cond: (s1.t1.id = public.t1.id)
515 -> Sort (cost=168.75..174.75 rows=2400 width=4)
517 -> Seq Scan on t1 (cost=0.00..34.00 rows=2400 width=4)
518 -> Sort (cost=168.75..174.75 rows=2400 width=4)
519 Sort Key: public.t1.id
520 -> Seq Scan on t1 (cost=0.00..34.00 rows=2400 width=4)
523 postgres=# /*+ <span class="strong">HashJoin(pt st)</span> */
524 postgres-# EXPLAIN SELECT * FROM s1.t1 st
525 postgres-# JOIN public.t1 pt ON (st.id=pt.id);
527 ---------------------------------------------------------------------
528 <span class="strong">Hash Join</span> (cost=64.00..1112.00 rows=28800 width=8)
529 Hash Cond: (st.id = pt.id)
530 -> Seq Scan on t1 st (cost=0.00..34.00 rows=2400 width=4)
531 -> Hash (cost=34.00..34.00 rows=2400 width=4)
532 -> Seq Scan on t1 pt (cost=0.00..34.00 rows=2400 width=4)
539 <dt><h3>ビューおよびルールの中でアクセスされているテーブル</h3></dt>
540 <dd>ヒント句で指定した名称と一致すれば、ビュー定義や関数内クエリなどに出現するテーブルについても、ヒントを指定したクエリ内と同じようにヒント句が適用されます。このため、ヒント句の適用有無や適用するヒント句をそれぞれのテーブルで変えたい場合は、それぞれ異なる別名を指定してください。</br>
541 以下の例では、ビュー定義で使われている「t1」という別名をSeqScanヒント句で指定したことで、表スキャンとビュー経由のスキャンの両方でSeq Scanが選択されています。ビュー定義で使用されている「t1」とは別の別名を実表に指定することで、個別にスキャン方式を制御できます。
543 postgres=# CREATE VIEW view1 AS SELECT * FROM table1 <span class="strong">t1</span>;
545 postgres=# /*+ SeqScan(<span class="strong">t1</span>) */
546 postgres=# EXPLAIN SELECT * FROM table1 <span class="strong">t1</span> JOIN view1 t2 ON (t1.key = t2.key) WHERE t2.key = 1;
548 -----------------------------------------------------------------
549 Nested Loop (cost=0.00..358.01 rows=1 width=16)
550 -> Seq Scan on table1 <span class="strong">t1</span> (cost=0.00..179.00 rows=1 width=8)
552 -> Seq Scan on table1 <span class="strong">t1</span> (cost=0.00..179.00 rows=1 width=8)
556 <dt><h3>継承テーブル</h3></dt>
557 <dd>継承テーブルにスキャン方式のヒント句を指定する場合は、オブジェクト名として親テーブルの名称または別名を指定してください。全ての子テーブルに同じスキャン方式を選択します。子テーブルごとに別のスキャン方式を指定することはできません。</dd>
559 <dt><h3>マルチステートメント</h3></dt>
560 <dd>クエリをマルチステートメントで実行する場合は、ヒントの指定方法によって以下のように注意点が異なります。
563 <dd>1つ目のクエリで指定したヒントが全てのクエリで使用されます。2つ目以降のクエリに指定したヒントは無視されます。</dd>
565 <dd>ヒントを指定したいクエリの定数部分を?に置き換えてから、全てのクエリを一つにまとめてヒント用テーブルに登録します。ただし、複数の動的クエリをマルチステートメントで実行する場合には対応していません。</dd>
568 <dt><h3>FROM句にVALUES式を使っている問い合わせ</h3></dt>
569 <dd>FROM句にVALUES式を使っている場合は、ヒント句のオブジェクト名に「*VALUES*」を指定することでヒントを与えることができる場合があります。 ただし複数のVALUESを使っている場合は内部的には区別がつかないためヒントを与えることはできません。
571 postgres=# /*+ <span class="strong">MergeJoin(a *VALUES*)</span> */
572 postgres-# EXPLAIN SELECT *
573 postgres-# FROM pgbench_accounts a
574 postgres-# JOIN (VALUES (1,1),(2,2)) <span class="strong">v</span> (vid, vbalance) ON a.aid = v.vid
575 postgres-# ORDER BY a.aid;
577 ---------------------------------------------------------------------------------------------------------------
578 <b><u>Merge Join</u></b> (cost=0.04..4497.33 rows=2 width=105)
579 Merge Cond: (a.aid = "*VALUES*".column1)
580 -> Index Scan using pgbench_accounts_pkey on pgbench_accounts a (cost=0.00..4247.26 rows=100000 width=97)
581 -> Sort (cost=0.04..0.04 rows=2 width=8)
582 Sort Key: "*VALUES*".column1
583 -> Values Scan on <b><u>"*VALUES*"</u></b> (cost=0.00..0.03 rows=2 width=8)
589 <dt><h3>副問い合わせ結果</h3></dt>
590 <dd>以下のような形式の副問い合わせでは外側の文への併合が行われず「ANY_subquery」という名前の独立した副問合せとして残される場合があります。このような副問合せが一つしかない場合は ANY_subquery という名前でこの副問合せを特定することができます。
592 <li>IN (SELECT ... { <b>LIMIT | OFFSET 等</b>} ... )</li>
593 <li>ANY (SELECT ... { <b>LIMIT | OFFSET 等</b>} ...)</li>
594 <li>SOME (SELECT ... { <b>LIMIT | OFFSET 等</b>} ...)</li>
596 以下の例では、そのことを利用して Hash Joinを強制しています。
598 postgres=# /*+HashJoin(a1 ANY_subquery)*/
599 postgres=# EXPLAIN SELECT *
600 postgres=# FROM pgbench_accounts a1
601 postgres=# WHERE aid IN (SELECT bid FROM pgbench_accounts a2 LIMIT 10);
604 ---------------------------------------------------------------------------------------------
605 Hash Semi Join (cost=0.49..2903.00 rows=1 width=97)
606 Hash Cond: (a1.aid = a2.bid)
607 -> Seq Scan on pgbench_accounts a1 (cost=0.00..2640.00 rows=100000 width=97)
608 -> Hash (cost=0.36..0.36 rows=10 width=4)
609 -> Limit (cost=0.00..0.26 rows=10 width=4)
610 -> Seq Scan on pgbench_accounts a2 (cost=0.00..2640.00 rows=100000 width=4)
616 <dt><h3>IndexOnlyScanヒント句</h3></dt>
617 <dd>ヒント句の対象となるテーブルにIndex Only Scanが可能なインデックスとIndex Only Scanが不可能なインデックスが同時に存在する場合、Index Only Scanが可能なインデックスをテーブルに対してIndexOnlyScanヒント句を追加で指定しないとIndex Scanが選択されることがあります。</dd>
619 <dt><h3>NoIndexScanヒントの挙動について</h3></dt>
620 <dd>NoIndexScanヒント句を指定した場合は、Index ScanだけでなくIndex Only Scanも選択されません。</dd>
623 <dt><h3>並列実行ヒントとスキャン方式ヒントの関係について</h3></dt>
624 <dd>スキャン方式ヒントは並列実行の部分パスを生成する際にも影響を与えます。今のところ並列実行の部分パスはシーケンシャルスキャンとなるため、シーケンシャルスキャンを禁止すると同じオブジェクトへの並列実行ヒントが無効化されます。</dd>
627 <dt><h3>UNION に対する並列実行ヒント</h3></dt>
628 <dd>UNIONは直下のサブクエリが全て並列実行可能な場合にだけ並列実行を行います。一方ですべてのサブクエリが並列実行可能な場合は、そのうちの一つで並列実行を強制するとコスト比較の結果UNION全体が並列実行されることになります。ただし並列実行ヒントによる並列実行の禁止を行った場合はそのスキャンは並列実行不可となります。
632 <dt><h3>Set ヒントでの pg_hint_plan 自身の制御変数の設定</h3></dt>
633 <dd>pg_hint_plan 自身の挙動を設定する変数を Set ヒントで変更した場合は期待通りに動作しない場合があります。</dd>
635 <li>enable_hint, enable_hint_tables の設定ヒントはログ上は「使用済み」と出ますが、実際には無視されています。</li>
636 <li>debug_print と message_level の変更は処理の途中から影響を与えます。</li>
640 <h2>ヒントのパースエラーについて</h2>
641 <dt><h3>構文エラー</h3></dt>
642 <dd>ヒント句の記述に構文上の誤りがあった場合、pg_hint_planは誤った記述より前のヒント句のみ有効とし、誤った記述以降のヒント句を無視してクエリを実行します。誤りの内容はpg_hint_plan.parse_messagesで指定したレベルでサーバログに記録されます。
644 <li>ヒント句名を間違っている。</li>
645 <li>オブジェクト指定を正しく括弧で囲っていない。</li>
646 <li>オブジェクト名を空白で区切っていない。</li>
649 <dt><h3>オブジェクト指定エラー</h3></dt>
650 <dd>pg_hint_planは、ヒント句対象のオブジェクト指定に誤りがあった場合、pg_hint_planは正常に解析できたヒントのみを適用して、それ以外を無視します。誤りの内容はpg_hint_plan.parse_messagesで指定したレベルでサーバログに記録されます。
654 <dt><h3>重複したヒント</h3></dt>
655 <dd>同じオブジェクトに対して同じグループのヒント句を重複して指定した場合は、各グループで最後に指定したヒント句を使用します。</dd>
656 <dt><h3>ネストしたブロックコメント</h3></dt>
657 <dd>pg_hint_planでは、ヒントを指定したブロックコメントにネストしたブロックコメントを含めることができません。ネストしたブロックコメントを含めた場合は、誤った記述に関する情報を出力しますがエラー終了しません。ヒントを無視してクエリを実行します。</dd>
658 <dt><h3>メッセージの出力レベル</h3></dt>
659 <dd>ヒントに誤りがあった場合に出力されるメッセージのレベルは、基本的にはpg_hint_plan.parse_messagesに指定したレベルです。ただし、ヒント句に指定したオブジェクトの長さが識別子の最大長(デフォルトでは63バイト)を超えた際に切り詰めた場合は、NOTICEで出力します。</dd>
662 <dt><h3>プランナパラメータの影響</h3></dt>
663 <dd>FROM句の要素数が from_collapse_limit 以上または join_collapse_limit を超える場合には、結合順序のヒント句は正しく動作しません。また、FROM句の要素数が geqo_threshold 以上の場合は、結合順序のヒント句、および結合方式のヒント句は動作しません。ヒント句が使われるようにするには、これらのGUCパラメータの値を十分に大きくしてください。</dd>
664 <dt><h3>適用不能なヒント</h3></dt>
665 <dd>ヒントで指定した結果正しく実行できない実行計画が生成される場合は、実行可能なプランに強制的に修正します。実行不能な実行計画例を以下に示します。
667 <li>特殊な場合を除いた FULL OUTER JOINに対する Nested Loop。</li>
668 <li>WHERE句やJOIN条件などに指定していない列のみを含むインデックス。</li>
669 <li>検索条件にctidを指定しない場合の Tid Scan。</li>
672 <dt><h3>ECPGにおける制限</h3></dt>
673 <dd>ECPGで実装したアプリケーションから発行するクエリにヒントをコメントで指定した場合、実行計画を制御できません。これは、CプリプロセッサがCコードに変換するタイミングで、全てのブロックコメントを取り除いてしまうためです。ただし、EXECUTEコマンドで実行する動的SQLに含まれるコメントは使用可能です。</dd>
674 <dt><h3>異なるヒントを与えた同一クエリの pg_stat_statements での扱い</h3></dt>
675 <dd>pg_stat_statements ではクエリIDの生成の際にコメントは無視されます。そのため与えているヒントが異なる同一のクエリは一つのクエリとして集計されます。</dd>
679 <h2 id="requirement">動作環境</h2>
680 pg_hint_plan 1.2 は PostgreSQL 9.6 のみをサポートします。
688 <h2 id="seealso">関連項目</h2>
689 <h3 id="postgresql_document">PostgreSQLドキュメント</h3>
690 <a href="http://www.postgresql.jp/document/current/html/sql-explain.html">EXPLAIN</a>
691 <a href="http://www.postgresql.jp/document/current/html/sql-set.html">SET</a>
692 <a href="http://www.postgresql.jp/document/current/html/runtime-config.html">サーバの設定</a>
693 <a href="http://www.postgresql.jp/document/current/html/parallel-plans.html">パラレルプラン</a>
696 <div class="navigation">
697 <a href="pg_hint_plan-ja.html">pg_hint_plan</a>
700 <p class="footer">Copyright (c) 2012-2017, NIPPON TELEGRAPH AND TELEPHONE CORPORATION</p>