OSDN Git Service

156e4ea5fe867b6d0f8a128fa4a877e0836658f2
[pghintplan/pg_hint_plan.git] / doc / pg_hint_plan-ja.html
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD html 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
2 <html>
3 <head>
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">
7 -->
8 <link rel="stylesheet" type="text/css" href="style.css">
9 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
10 </head>
11
12 <body>
13 <h1 id="pg_hint_plan">pg_hint_plan 1.1.0</h1>
14 <div class="navigation">
15   <a href="pg_hint_plan-ja.html">pg_hint_plan</a>
16 </div>
17 <hr>
18
19 <div class="index">
20 <ol>
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>
30 </div>
31
32 <h2 id="name">名前</h2>
33 <p>pg_hint_plan -- 実行計画を示すヒントをクエリに指定することで、SQL文やGUCパラメータを変えずに実行計画を制御します。</p>
34
35 <h2 id="synopsis">概要</h2>
36 <p>PostgreSQLはコストベースオプティマイザを採用しており、SQL文と統計情報を元に可能な実行計画のコストを見積もり、最もコストの低い実行計画を選択します。オプティマイザは可能な限りよい実行計画を作成しようとしますが、カラム間の相関関係などは考慮しないため、複雑なクエリでは常に最適なプランを選択するとは限りません。</p>
37 <p>pg_hint_planを用いると、ヒントでスキャン方式や結合方式を指定することで、SQL文やGUCパラメータを変更することなく実行計画を制御することができます。</p>
38
39 <h2 id="description">機能説明</h2>
40 <p>pg_hint_planの機能について説明するにあたり、まず文中で使用されている用語について説明します。</p>
41 <table>
42 <thead>
43 <tr>
44 <tr><th>用語</th><th>説明</th></tr>
45 </tr></thead>
46 <tbody>
47 <tr><td>ヒント句</td>
48   <td>実行計画を制御するための情報です。</td></tr>
49   <tr><td>ヒント</td>
50   <td>実行計画を制御したいクエリに適用するヒント句を列挙したものです。</td></tr>
51 </tbody>
52 </table>
53
54 <h3 id="hint-rule">ヒントの指定方法</h3>
55 <p>ヒントは二つの方法で指定することができます。</p>
56 <ul>
57 <li>コメントでの指定</li>
58 <p>特殊なSQLブロックコメント内にヒントを記述します。</p>
59 <li>テーブルでの指定</li>
60 <p>ヒント用のテーブルにヒントを登録します。</p>
61 </ul>
62 <p>特定のアプリケーションではヒントをコメントで指定することができないため、「テーブルでの指定」でヒントを指定します。なお、「コメントでの指定」と異なり、アプリケーションのソースコードに手を入れずに指定するヒントを変更することができます。</p>
63
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>
68 <pre>
69 postgres=# EXPLAIN (VERBOSE, COSTS)
70 postgres-# /*+
71 postgres*#     SeqScan(a)
72 postgres*#     HashJoin(a b)
73 postgres*#     Set(random_page_cost 2.0)
74 postgres*#  */
75 postgres-# SELECT *
76 postgres-#   FROM pgbench_accounts a
77 postgres-#   JOIN pgbench_branches b
78 postgres-#     ON a.bid = b.bid
79 postgres-#  ORDER BY a.aid
80 postgres-#  LIMIT 10;
81                                              QUERY PLAN
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
87          Sort Key: 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
97 (14 rows)
98
99 postgres=# 
100 </pre>
101
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>
106 <table>
107 <thead>
108 <tr>
109 <tr><th>列名</th><th>説明</th></tr>
110 </tr></thead>
111 <tbody>
112 <tr><td>id</td>
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>
118 <tr><td>hints</td>
119   <td>ヒントを指定します。ヒント以外のもの(SQLコメントなど)を含めないでください。</td></tr>
120 </tbody>
121 </table>
122 <p>ヒントの登録情報を変更する場合は、変更したい登録情報のidを指定して登録情報を更新してください。</br>
123 ヒントの登録を解除する場合は、解除したい登録情報のidを指定してテーブルから削除してください。</p>
124 <p>以下の例では、ヒントの登録、ヒント登録情報の変更、ヒントの登録解除の順に、クエリの実行結果を示しています。
125 </p>
126 <pre>
127 postgres=# INSERT INTO hint_plan.hints(norm_query_string, application_name, hints)
128 postgres-#     VALUES (
129 postgres(#         'EXPLAIN (COSTS false) SELECT * FROM t1 WHERE t1.id = ?;',
130 postgres(#         '',
131 postgres(#         'SeqScan(t1)'
132 postgres(#     );
133 INSERT 0 1
134 postgres=# UPDATE hint_plan.hints
135 postgres-#    SET hints = 'IndexScan(t1)'
136 postgres-#  WHERE id = 1;
137 UPDATE 1
138 postgres=# DELETE FROM hint_plan.hints
139 postgres-#  WHERE id = 1;
140 DELETE 1
141 postgres=#
142 </pre>
143 <p>以下の例では、テーブルに登録した「コメントでの指定」の例と同じヒントと、登録したクエリの実行結果を示しています。</p>
144 <pre>
145 postgres=# SELECT * FROM hint_plan.hints;
146  id |     norm_query_string      | application_name |                      hints
147 ----+----------------------------+------------------+--------------------------------------------------
148   1 | EXPLAIN (VERBOSE, COSTS)  +| psql             | SeqScan(a)HashJoin(a b)Set(random_page_cost 2.0)
149     |  SELECT *                 +|                  |
150     |    FROM pgbench_accounts a+|                  |
151     |    JOIN pgbench_branches b+|                  |
152     |      ON a.bid = b.bid     +|                  |
153     |   ORDER BY a.aid          +|                  |
154     |   LIMIT ?;                +|                  |
155     |                            |                  |
156 ...
157
158 postgres=# SET pg_hint_plan.enable_hint_table TO on;
159 postgres=# EXPLAIN (VERBOSE, COSTS)
160 postgres-# SELECT *
161 postgres-#   FROM pgbench_accounts a
162 postgres-#   JOIN pgbench_branches b
163 postgres-#     ON a.bid = b.bid
164 postgres-#  ORDER BY a.aid
165 postgres-#  LIMIT 10;
166                                              QUERY PLAN
167 ----------------------------------------------------------------------------------------------------
168  Limit  (cost=6176.99..6177.01 rows=10 width=461)
169    Output: a.aid, a.bid, a.abalance, a.filler, b.bid, b.bbalance, b.filler, a.aid
170    ->  Sort  (cost=6176.99..6426.99 rows=100000 width=461)
171          Output: a.aid, a.bid, a.abalance, a.filler, b.bid, b.bbalance, b.filler, a.aid
172          Sort Key: a.aid
173          ->  <span class="strong">Hash Join</span>  (cost=1.02..4016.02 rows=100000 width=461)
174                Output: a.aid, a.bid, a.abalance, a.filler, b.bid, b.bbalance, b.filler, a.aid
175                Hash Cond: (a.bid = b.bid)
176                ->  <span class="strong">Seq Scan on public.pgbench_accounts a</span>  (cost=0.00..2640.00 rows=100000 width=97)
177                      Output: a.aid, a.bid, a.abalance, a.filler
178                ->  Hash  (cost=1.01..1.01 rows=1 width=364)
179                      Output: b.bid, b.bbalance, b.filler
180                      ->  Seq Scan on public.pgbench_branches b  (cost=0.00..1.01 rows=1 width=364)
181                            Output: b.bid, b.bbalance, b.filler
182 (14 rows)
183
184 postgres=# 
185 </pre>
186
187 <div class="tips">
188 <span class="strong">指定方法の優先度</span>
189 <p>ヒントをコメントとテーブルの両方で指定した場合、テーブルで指定したヒントが適用され、コメントで指定したヒントは無視されます。</p>
190 <p>以下の例では、コメントでヒントを指定して実行計画を制御しているクエリに対して、テーブルに空文字列のヒントを登録しています。テーブルで指定したヒントが優先されるので、コメントで指定したヒントを取り消すことができます。</p>
191 <pre>
192 postgres=# select * from hint_plan.hints;
193  id |             norm_query_string              | application_name | hints
194 ----+--------------------------------------------+------------------+-------
195   1 | EXPLAIN (VERBOSE, COSTS)                  +| psql             |
196     | /*+                                       +|                  |
197     |     HashJoin(a b)                         +|                  |
198     |     SeqScan(a)                            +|                  |
199     |  */                                       +|                  |
200     | SELECT *                                  +|                  |
201     |   FROM pgbench_accounts a                 +|                  |
202     |   JOIN pgbench_branches b ON a.bid = b.bid+|                  |
203     |  ORDER BY a.aid;                           |                  |
204 ...
205
206 postgres=# EXPLAIN (VERBOSE, COSTS)
207 postgres-# /*+
208 postgres*#     HashJoin(a b)
209 postgres*#     SeqScan(a)
210 postgres*#  */
211 postgres-# SELECT *
212 postgres-#   FROM pgbench_accounts a
213 postgres-#   JOIN pgbench_branches b ON a.bid = b.bid
214 postgres-#  ORDER BY a.aid;
215                                                       QUERY PLAN
216 ----------------------------------------------------------------------------------------------------------------------
217  <span class="strong">Nested Loop</span>  (cost=0.00..5750.47 rows=100000 width=461)
218    Output: a.aid, a.bid, a.abalance, a.filler, b.bid, b.bbalance, b.filler, a.aid
219    Join Filter: (a.bid = b.bid)
220    ->  <span class="strong">Index Scan using pgbench_accounts_pkey on public.pgbench_accounts a</span>  (cost=0.00..4249.45 rows=100000 width=97)
221          Output: a.aid, a.bid, a.abalance, a.filler
222    ->  Materialize  (cost=0.00..1.01 rows=1 width=364)
223          Output: b.bid, b.bbalance, b.filler
224          ->  Seq Scan on public.pgbench_branches b  (cost=0.00..1.01 rows=1 width=364)
225                Output: b.bid, b.bbalance, b.filler
226 (9 rows)
227 </pre>
228 </div>
229
230 <h3 id="hint-group">ヒント句のグループ</h3>
231 <p>pg_hint_planで使えるヒント句の種類は、スキャン方式と結合方式、結合順序、GUCパラメータの4グループです。各グループの具体的なヒント句は、<a href="hint_list-ja.html">ヒント句一覧</a>を参照してください。</p>
232
233 <h4>スキャン方式</h4>
234 <p>あるオブジェクトでどのスキャン方式を選択するかを指定できるヒント句のグループです。「SeqScan」や「IndexScan」などを含みます。</p>
235 <p>スキャン方式を指定できるオブジェクトは、通常のテーブル、継承テーブル、UNLOGGEDテーブル、一時テーブル、システムカタログです。スキャン方式を指定できないオブジェクトは、外部テーブル、テーブル関数、VALUESコマンド結果、CTE(共通テーブル式)、ビュー、副問い合わせ結果です。</p>
236 <p>特定のオブジェクトについてあるスキャン方式を選択して欲しい場合は、そのスキャン方式のヒント句と、対象となるオブジェクトの名前を指定します。逆に、特定のオブジェクトについてあるスキャン方式を選択して欲しくない場合は、Noで始まるヒント句を指定します。同じオブジェクトに対して複数のスキャン方式のヒント句を指定した場合は、最後に指定したヒント句が適用されます。</p>
237
238 <h4>結合方式</h4>
239 <p>あるオブジェクトの組み合わせでどの結合方式を選択するかを指定できるヒント句のグループです。「MergeJoin」や「NestLoop」などを含みます。</p>
240 <p>結合方式を指定できるオブジェクトは、通常のテーブル、継承テーブル、UNLOGGEDテーブル、一時テーブル、外部テーブル、システムカタログ、テーブル関数、VALUESコマンド結果、CTE(共通テーブル式)です。結合方式を指定できないオブジェクトは、ビュー、副問い合わせ結果です。</p>
241 <p>特定のオブジェクトの組み合わせについてある結合方式を選択して欲しい場合は、その結合方式のヒント句と、対象となる2つ以上のオブジェクトの名前を指定します。逆に、特定のオブジェクトの組み合わせについてある結合方式を選択して欲しくない場合は、Noで始まるヒント句を指定します。同じオブジェクトの組み合わせに対して複数の結合方式のヒント句を指定した場合は、最後に指定したヒント句が適用されます。</p>
242
243 <h4>結合順序</h4>
244 <p>あるオブジェクトの組み合わせでどのような順番で結合するかを指定できるヒント句のグループです。「Leading」のみを含みます。</p>
245 <p>結合順序を指定できるオブジェクトは結合方式と同じです。</p>
246 <p>先に結合して欲しいオブジェクトから順にオブジェクト名または別名を指定します。同じ問合せブロックのオブジェクトに対して複数の結合順序のヒント句を指定した場合は、最後に指定したヒント句が適用されます。 </p>
247 <div class="tips">
248 <span class="strong"></span>
249 <p>結合対象のテーブルが3つ以上ある場合、結合方式のヒント句を指定したとしてもコスト見積もりによっては対象のテーブルが直接結合されないことがあります。対象のテーブルが直接結合されない場合は、結合順序のヒント句を併せて指定します。</p>
250 <p>以下の例では、table1とtable2を直接結合する場合はNested Loopを、table1とtable2とtable3を結合する場合はMerge Joinを指定しています。また、コスト見積もりによってはtable1とtable2が直接結合されない場合を避けるため、table1とtable2を結合してからtable3を結合するようにLeadingヒント句を併用しています。</p>
251 <pre>
252 postgres=# /*+
253 postgres*#     NestLoop(t1 t2)
254 postgres*#     MergeJoin(t1 t2 t3)
255 postgres*#     Leading(t1 t2 t3)
256 postgres*#  */
257 postgres-# SELECT * FROM table1 t1
258 postgres-#     JOIN table table2 t2 ON (t1.key = t2.key)
259 postgres-#     JOIN table table3 t3 ON (t2.key = t3.key);
260 ...
261 </pre>
262 </div>
263
264 <h4>GUCパラメータ</h4>
265 <p>そのクエリの実行計画を作成している間だけGUCパラメータを変更できるヒント句のグループです。「Set」のみを含みます。</p>
266 <p>設定したいGUCパラメータとそのパラメータの値を指定します。指定できるGUCパラメータは<a href="http://www.postgresql.jp/document/current/html/runtime-config-query.html">問い合わせ計画</a>のGUCパラメータのみです。同じGUCパラメータのヒント句を2回以上指定した場合は、最後に指定したヒント句が適用されます。</p>
267 <div class="tips">
268 <span class="strong">Setヒント句の制限</span>
269 <p>Setヒント句に問い合わせ計画のGUCパラメータ以外を指定した場合の動作は保証できません。問い合わせ計画以外のGUCパラメータを指定した場合の例の一つとして、<a href="#hint-GUC">pg_hint_planのGUCパラメータ</a>を指定した場合の動作を、以下に示します。<p>
270 <ul>
271 <li>pg_hint_plan.enable_hint、pg_hint_plan.enable_hint_tableをSetヒント句の対象に指定した場合は、無効になります。なお、pg_hint_plan.debug_printをonに設定していると、そのヒント句はログ出力で「適用されたヒント句」として出力されます。</li>
272 <li>pg_hint_plan.debug_printをSetヒント句の対象に指定した場合は、指定したとおりに適用されます。</li>
273 <li>pg_hint_plan.parse_messagesをSetヒント句の対象に指定した場合は、構文エラーと一部のSetヒント句のエラーのメッセージはクエリ開始時の設定レベルで出力され、それ以外のメッセージはSetヒント句で指定したレベルで出力されます。</li>
274 </ul>
275 </div>
276
277 <h3 id="hint-GUC">pg_hint_planのGUCパラメータ</h3>
278 <p>pg_hint_planの動作を制御するGUCパラメータを以下に記述します。</p>
279 <table>
280 <thead>
281 <tr>
282 <tr><th>GUCパラメータ</th><th>説明</th><th>デフォルト値</th></tr>
283 </tr></thead>
284 <tbody>
285 <tr><td>pg_hint_plan.enable_hint</td>
286   <td>pg_hint_planの機能を有効または無効にします。</td><td>on</td></tr>
287 <tr><td>pg_hint_plan.enable_hint_table</td>
288   <td>ヒントをテーブルで指定する機能を有効または無効にします。</td><td>off</td></tr>
289 <tr><td>pg_hint_plan.debug_print</td>
290   <td>指定したヒントが実行計画生成にどのように影響したかを出力します。メッセージはLOGメッセージレベルで出力されますので、デフォルトではサーバログに出力され、クライアントには渡されません。</td><td>off</td></tr>
291 <tr><td>pg_hint_plan.parse_messages</td>
292   <td>指定したヒントを解釈できなかった場合に、どのメッセージ階層でログを出力するかを指定します。有効な値は、debug5、debug4、debug3、debug2、debug1、log、info、notice、warning、またはerrorです。fatalとpanicは指定できません。</td><td>info</td></tr>
293
294 </tbody>
295 </table>
296 <p>
297 PostgreSQL 9.1の環境でこれらのパラメータをpostgresql.confファイルで設定するには、<a href="http://www.postgresql.jp/document/9.1/html/runtime-config-custom.html#GUC-CUSTOM-VARIABLE-CLASSES">custom_variable_classes</a>にpg_hint_planを加える必要があります。
298 典型的な使用例は以下のようになります。
299 </p>
300 <pre>
301 # postgresql.conf
302 shared_preload_libraries = 'pg_hint_plan'
303
304 custom_variable_classes = 'pg_hint_plan'    # 9.2以降は廃止されたため記述不要
305 pg_hint_plan.parse_messages = 'debug2'
306 </pre>
307 <p>
308 PostgreSQL 9.2以降ではcustom_variable_classesは廃止されているため、pg_hint_planのGUCパラメータを標準のGUCパラメータと同様に記述することができます。
309 </p>
310
311 <h2 id="install">インストール</h2>
312 <p>pg_hint_planのインストール方法について説明します。</p>
313
314 <h3 id="build">ビルド</h3>
315 <p>pg_hint_planをソースコードからビルドする場合、pg_hint_planのソースを展開したディレクトリでmake → make installの順に実行します。make installはPostgreSQLをインストールしたOSユーザで実行します。なお、pg_hint_planのビルドにはpgxsを使用するので、RPM版のPostgreSQLを使用している環境では、postgresql-devel パッケージが必要です。</p>
316 <p>以下にビルドの例を示します。</p>
317 <pre>
318 $ tar xzvf pg_hint_plan-1.0.0.tar.gz
319 $ cd pg_hint_plan-1.0.0
320 $ make
321 $ su
322 # make install
323 </pre>
324
325 <h3 id="create-extension">データベースへの登録</h3>
326 <p>pg_hint_planはPostgreSQLの拡張(EXTENSION)を使用しているので、pg_hint_planを利用するデータベースにスーパーユーザもしくはそのデータベースの所有者で接続してCREATE EXTENSIONコマンドを実行します。</p>
327 <p>以下にデータベースへの登録の例を示します。 <i>dbname</i>は対象となるデータベース名を意味します。</p>
328 <pre>
329 $ psql -d <i>dbname</i> -c "CREATE EXTENSION pg_hint_plan"
330 </pre>
331
332 <h3 id="hint-load">pg_hint_planのロード</h3>
333 <p>特定のセッションでのみpg_hint_planを使う場合は、以下の例のようにpg_hint_planの共有ライブラリをLOADコマンドでロードします。一般ユーザで利用する場合は$libdir/pluginsにもインストールする必要があるので注意してください。
334 <pre>
335 postgres=# LOAD 'pg_hint_plan';
336 LOAD
337 postgres=# </pre></p>
338 <p>全てのセッションでpg_hint_planを有効にするには、shared_preload_libraries GUCパラメータに'pg_hint_plan'を追加してからサーバを再起動します。</p>
339 <p>注意: pg_hint_planを<a href="#create-extension">データベースに登録</a>せずに、ロード後にSQL文を実行すると以下に示す例のようなエラーとなります。 pg_hint_planを使うときは、データベースへの登録を忘れないように注意してください。</p>
340 <pre>
341 postgres=# EXPLAIN SELECT * FROM pgbench_accounts a WHERE aid = 1;
342 ERROR:  schema "hint_plan" does not exist
343 LINE 1: SELECT hints   FROM hint_plan.hints  WHERE norm_query_string...
344                             ^
345 QUERY:  SELECT hints   FROM hint_plan.hints  WHERE norm_query_string = $1    AND ( application_name = $2     OR application_name = '' )  ORDER BY application_name DESC
346 postgres=#
347 </pre>
348
349 <h2 id="uninstall">アンインストール</h2>
350 <p>pg_hint_planをアンインストールするときは、以下の手順を実行します。 <i>dbname</i>は対象となるデータベース名を意味します。</p>
351 <ol>
352 <li>pg_hint_planのソースを展開したディレクトリでmake uninstallを実行します。make uninstallはPostgreSQLをインストールしたOSユーザで実行します。</li>
353 <pre>
354 $ cd pg_hint_plan-1.0.0
355 $ su
356 # make uninstall
357 </pre>
358 <li>pg_hint_planを登録したデータベースにスーパーユーザもしくはそのデータベースの所有者で接続して、DROP EXTENSIONコマンドおよびDROP SCHEMAコマンドを実行します。</li>
359 <pre>
360 $ psql -d <i>dbname</i> -c "DROP EXTENSION pg_hint_plan"
361 $ psql -d <i>dbname</i> -c "DROP SCHEMA hint_plan"
362 </pre>
363 </ol>
364
365 <h2 id="restrictions">使用上の注意と制約</h2>
366 <p>pg_hint_planを使用する際には、以下の注意と制約があります。</p>
367 <h3>ヒントの指定方法</h3>
368 <dl>
369 <dt>コメント指定でのヒント記述位置</dt>
370 <dd>ヒントはクエリの先頭または途中に記述できます。ただし、ヒントをクエリの途中に記述する場合、ヒント用のブロックコメントより前に以下の文字<span class="strong">以外</span>が含まれると、「/*+」で始まるコメントでもヒントと見なされず無視されます。
371 <ul>
372 <li>空白文字(半角スペース、水平/垂直タブ、改行、フォームフィード、復帰)</li>
373 <li>アルファベット(大文字/小文字)</li>
374 <li>数字</li>
375 <li>アンダースコア</li>
376 <li>カンマ</li>
377 <li>開き括弧(()、閉じ括弧())</li>
378 </ul>
379 <p>指定したヒントが無視される例を以下に示します。</p>
380 <ol>
381 <li>二重引用符(")がヒント用のブロックコメントより前に含まれている場合</li>
382 <pre>
383 postgres=# SELECT bid AS "BID" 
384 postgres-# /*+
385 postgres*#     SeqScan(b)
386 postgres*#  */
387 postgres-#   FROM pgbench_branches b;
388 ...
389 </pre>
390 <li>演算子(>=)がヒント用のブロックコメントより前に含まれている場合</li>
391 <pre>
392 postgres=# WITH avg_aid AS (
393 postgres(#    SELECT avg(aid) FROM pgbench_history h
394 postgres(#     WHERE delta >= 0
395 postgres(# )
396 postgres-# /*+
397 postgres*#     SeqScan(h)
398 postgres*#  */
399 postgres-# SELECT * FROM avg_aid;
400 ...
401 </pre>
402 </ol>
403 一つのクエリに複数のブロックコメントを記述する場合は、最初のブロックコメントにのみヒントを記述してください。二番目以降のブロックコメントは、ヒントと見なされず無視されます。以下の例では、HashJoin(a b)とSeqScan(a)がヒントと見なされ、IndexScan(a)とMergeJoin(a b)は無視されています。</p>
404 <pre>
405 postgres=# /*+
406 postgres*#     <span class="strong">HashJoin(a b)</span>
407 postgres*#     <span class="strong">SeqScan(a)</span>
408 postgres*#  */
409 postgres-# /*+ IndexScan(a) */
410 postgres-# EXPLAIN SELECT /*+ MergeJoin(a b) */ *
411 postgres-#    FROM pgbench_branches b
412 postgres-#    JOIN pgbench_accounts a ON b.bid = a.bid
413 postgres-#   ORDER BY a.aid;
414                                       QUERY PLAN
415 ---------------------------------------------------------------------------------------
416  Sort  (cost=31465.84..31715.84 rows=100000 width=197)
417    Sort Key: a.aid
418    ->  <span class="strong">Hash Join</span>  (cost=1.02..4016.02 rows=100000 width=197)
419          Hash Cond: (a.bid = b.bid)
420          ->  <span class="strong">Seq Scan on pgbench_accounts a</span>  (cost=0.00..2640.00 rows=100000 width=97)
421          ->  Hash  (cost=1.01..1.01 rows=1 width=100)
422                ->  Seq Scan on pgbench_branches b  (cost=0.00..1.01 rows=1 width=100)
423 (7 rows)
424
425 postgres=# </pre>
426 </dd>
427 <dt>PL/pgSQL中の個別のクエリに対するヒント</dt>
428 <dd>ヒントは、PL/pgSQLブロック中のPERFORM文を除いた各クエリにも指定できます。ただし、コメントで指定する場合は、SELECTなどのSQLキーワードより後に指定してください。
429 <p>以下の例では、一つ目のクエリにNoIndexScanを、二つ目のクエリにSeqScanをそれぞれ指定しています。</p>
430 <pre>
431 postgres=# CREATE FUNCTION hints_func(integer) RETURNS integer AS $$
432 postgres$# DECLARE
433 postgres$#     id  integer;
434 postgres$#     cnt integer;
435 postgres$# BEGIN
436 postgres$#     SELECT /*+<span class="strong">NoIndexScan(a)</span>*/ aid
437 postgres$#         INTO id FROM pgbench_accounts a WHERE aid = $1;
438 postgres$#     SELECT /*+<span class="strong">SeqScan(a)</span>*/ count(*)
439 postgres$#         INTO cnt FROM pgbench_accounts a;
440 postgres$#     RETURN id + cnt;
441 postgres$# END;
442 postgres$# $$ LANGUAGE plpgsql;
443 </pre>
444 </dd>
445 <dt>オブジェクト名の引用符付け</dt>
446 <dd>ヒントに記述するオブジェクト名や別名が括弧((、)のいずれか)、二重引用符(")、空白(スペース、タブ、改行のいずれか)を含む場合は、通常のSQL文で使う場合と同じように二重引用符(")で囲んでください。二重引用符を含むオブジェクト名は、全体を二重引用符で括ったうえで、内部に含む二重引用符を二重引用符でエスケープしてください(例: 「quoted"table"name」→「"quoted""table""name"」)。</dd>
447 <dt>同一名称テーブルの区別</dt>
448 <dd>スキーマ違いや同一テーブルの複数回使用などでクエリ中に同一名称のテーブルが複数回出現する場合は、テーブルに別名をつけてそれぞれのテーブルを区別してください。以下の例の1つ目のSQL文では、HashJoin(t1 t1)をヒントに指定したとき、ヒント句対象のオブジェクトが特定できずにエラーになっています。2つ目のSQL文では、各テーブルにptやstという別名をつけているため、実行計画作成時にヒントで指定した通りにHash Joinを選択しています。</p>
449 <pre>
450 postgres=# /*+ <span class="strong">HashJoin(t1 t1)</span>*/
451 postgres-# EXPLAIN SELECT * FROM s1.t1
452 postgres-# JOIN public.t1 ON (s1.t1.id=public.t1.id);
453 INFO:  hint syntax error at or near "HashJoin(t1 t1)"
454 <span class="strong">DETAIL:  Relation name "t1" is ambiguous.</span>
455                             QUERY PLAN
456 ------------------------------------------------------------------
457  Merge Join  (cost=337.49..781.49 rows=28800 width=8)
458    Merge Cond: (s1.t1.id = public.t1.id)
459    ->  Sort  (cost=168.75..174.75 rows=2400 width=4)
460          Sort Key: s1.t1.id
461          ->  Seq Scan on t1  (cost=0.00..34.00 rows=2400 width=4)
462    ->  Sort  (cost=168.75..174.75 rows=2400 width=4)
463          Sort Key: public.t1.id
464          ->  Seq Scan on t1  (cost=0.00..34.00 rows=2400 width=4)
465 (8 行)
466
467 postgres=# /*+ <span class="strong">HashJoin(pt st)</span> */
468 postgres-# EXPLAIN SELECT * FROM s1.t1 st
469 postgres-# JOIN public.t1 pt ON (st.id=pt.id);
470                              QUERY PLAN
471 ---------------------------------------------------------------------
472  <span class="strong">Hash Join</span>  (cost=64.00..1112.00 rows=28800 width=8)
473    Hash Cond: (st.id = pt.id)
474    ->  Seq Scan on t1 st  (cost=0.00..34.00 rows=2400 width=4)
475    ->  Hash  (cost=34.00..34.00 rows=2400 width=4)
476          ->  Seq Scan on t1 pt  (cost=0.00..34.00 rows=2400 width=4)
477 (5 行)
478
479 postgres=#</pre>
480 </p>
481 </dd>
482 <dt>FROM句にVALUESコマンドを指定した場合の制限</dt>
483 <dd>FROM句にVALUESコマンドを指定した場合は、ヒント句のオブジェクト名に「*VALUES*」を指定してください。これは、VALUESの結果に別名を指定しても、PostgreSQL本体側で「*VALUES*」に名称が置き換えられるためです。複数のVALUESを使用する場合、この動作によりヒント句の対象を特定できません。よって、実行計画を制御できません。
484 <pre>
485 postgres=# /*+ <span class="strong">MergeJoin(a *VALUES*)</span> */
486 postgres-# EXPLAIN SELECT *
487 postgres-#    FROM pgbench_accounts a
488 postgres-#    JOIN (VALUES (1,1),(2,2)) <span class="strong">v</span> (vid, vbalance) ON a.aid = v.vid
489 postgres-#   ORDER BY a.aid;
490                                                   QUERY PLAN
491 ---------------------------------------------------------------------------------------------------------------
492  <span class="strong">Merge Join</span>  (cost=0.04..4497.33 rows=2 width=105)
493    Merge Cond: (a.aid = "*VALUES*".column1)
494    ->  Index Scan using pgbench_accounts_pkey on pgbench_accounts a  (cost=0.00..4247.26 rows=100000 width=97)
495    ->  Sort  (cost=0.04..0.04 rows=2 width=8)
496          Sort Key: "*VALUES*".column1
497          ->  Values Scan on <span class="strong">"*VALUES*"</span>  (cost=0.00..0.03 rows=2 width=8)
498 (6 行)
499
500 postgres=#
501 </pre>
502 </dd>
503 </dl>
504
505 <h3>ヒントの適用対象の指定</h3>
506 <dl>
507 <dt>クエリに明記されていないテーブルへのヒント句適用</dt>
508 <dd>ヒント句で指定した名称と一致すれば、ビュー定義や関数内クエリなどに出現するテーブルについても、ヒントを指定したクエリ内と同じようにヒント句が適用されます。このため、ヒント句の適用有無や適用するヒント句をそれぞれのテーブルで変えたい場合は、それぞれ異なる別名を指定してください。</br>
509 以下の例では、ビュー定義で使われている「t1」という別名をSeqScanヒント句で指定したことで、表スキャンとビュー経由のスキャンの両方でSeq Scanが選択されています。ビュー定義で使用されている「t1」とは別の別名を実表に指定することで、個別にスキャン方式を制御できます。
510 <pre>
511 postgres=# CREATE VIEW view1 AS SELECT * FROM table1 <span class="strong">t1</span>;
512 CREATE TABLE
513 postgres=# /*+ SeqScan(<span class="strong">t1</span>) */
514 postgres=# EXPLAIN SELECT * FROM table1 <span class="strong">t1</span> JOIN view1 t2 ON (t1.key = t2.key) WHERE t2.key = 1;
515                            QUERY PLAN
516 -----------------------------------------------------------------
517  Nested Loop  (cost=0.00..358.01 rows=1 width=16)
518    ->  Seq Scan on table1 <span class="strong">t1</span>  (cost=0.00..179.00 rows=1 width=8)
519          Filter: (key = 1)
520    ->  Seq Scan on table1 <span class="strong">t1</span>  (cost=0.00..179.00 rows=1 width=8)
521          Filter: (key = 1)
522 (5 rows)
523
524 postgres=# /*+ SeqScan(<span class="strong">t3</span>) */
525 postgres=# EXPLAIN SELECT * FROM table1 <span class="strong">t3</span> JOIN view1 t2 ON (t1.key = t2.key) WHERE t2.key = 1;
526                                    QUERY PLAN
527 --------------------------------------------------------------------------------
528  Nested Loop  (cost=0.00..187.29 rows=1 width=16)
529    ->  Seq Scan on table1 <span class="strong">t3</span>  (cost=0.00..179.00 rows=1 width=8)
530          Filter: (key = 1)
531    ->  Index Scan using foo_pkey on table1 t1  (cost=0.00..8.28 rows=1 width=8)
532          Index Cond: (key = 1)
533 (5 rows)
534
535 </pre>
536 </dd>
537 <dt>継承テーブルに対するヒント句</dt>
538 <dd>継承テーブルにスキャン方式のヒント句を指定する場合は、オブジェクト名として親テーブルの名称または別名を指定してください。全ての子テーブルに同じスキャン方式を選択します。子テーブルごとに別のスキャン方式を指定することはできません。</dd>
539 <dt>ビューやルールを使うクエリのヒント</dt>
540 <dd>ルールを定義したテーブルやビューを複数用いるときに、各ビュー内のテーブルの別名やルール書き換え後のクエリのテーブルの別名が重複した場合は、ヒント句の対象を区別できません。区別する場合は、各ビュー内のテーブルの別名やルール書き換え後のクエリのテーブルの別名を重複させないでください。</dd>
541 <dt>ルールを使うクエリでのヒントの適用範囲</dt>
542 <dd>ルールによるクエリ書き換えによってクエリが複数になる場合は、書き換え前のクエリで指定したヒントが全てのクエリで使用されます。ルール内のクエリにヒントを記述しても、そのヒントは適用されません。</dd>
543 <dt>マルチステートメントでのヒントの適用範囲</dt>
544 <dd>クエリをマルチステートメントで実行する場合は、ヒントの指定方法によって以下のように注意点が異なります。
545 <dl>
546 <dt>コメントでの指定の場合</dt>
547 <dd>1つ目のクエリで指定したヒントが全てのクエリで使用されます。2つ目以降のクエリに指定したヒントは無視されます。</dd>
548 <dt>テーブルでの指定の場合</dt>
549 <dd>ヒントを指定したいクエリの定数部分を?に置き換えてから、全てのクエリを一つにまとめてヒント用テーブルに登録します。</dd>
550 </dl>
551 psqlコマンドで-cオプションで複数のクエリを指定した場合などにマルチステートメントで実行されます。
552 </dd>
553 <dt>副問い合わせ結果をヒントに指定する方法</dt>
554 <dd>以下の副問い合わせの結果をヒントに指定する場合は、ヒント句のオブジェクト名に「ANY_subquery」を指定してください。
555 <ul>
556 <li>IN (SELECT ... { LIMIT | OFFSET 等} ... )</li>
557 <li>ANY (SELECT ... { LIMIT | OFFSET 等} ...)</li>
558 <li>SOME (SELECT ... { LIMIT | OFFSET 等} ...)</li>
559 </ul>
560 PostgreSQLは、実行計画を生成するときに副問い合わせを上位の問合せに併合することがあります。しかし、上記のような副問い合わせの場合には併合せず、これらの副問い合わせの結果に内部的に「ANY_subquery」という固定名をつけます。以下の例では、その固定名を指定したヒント句の効果でHash Joinを選択しています。
561 <pre>
562 postgres=# /*+HashJoin(a1 ANY_subquery)*/
563 postgres=# EXPLAIN SELECT *
564 postgres=#    FROM pgbench_accounts a1
565 postgres=#   WHERE aid IN (SELECT bid FROM pgbench_accounts a2 LIMIT 10);
566                                          QUERY PLAN
567
568 ---------------------------------------------------------------------------------------------
569  Hash Semi Join  (cost=0.49..2903.00 rows=1 width=97)
570    Hash Cond: (a1.aid = a2.bid)
571    ->  Seq Scan on pgbench_accounts a1  (cost=0.00..2640.00 rows=100000 width=97)
572    ->  Hash  (cost=0.36..0.36 rows=10 width=4)
573          ->  Limit  (cost=0.00..0.26 rows=10 width=4)
574                ->  Seq Scan on pgbench_accounts a2  (cost=0.00..2640.00 rows=100000 width=4)
575 (6 rows)
576
577 postgres=#
578 </pre>
579 一つのクエリで上記のような副問い合わせを複数使用している場合は、「ANY_subquery」と指定しても対象を特定できないため、ヒント句はエラーとなり無視されます。</br>
580 </dd>
581 <dt>IndexOnlyScanヒント句の指定(PostgreSQL 9.2以降)</dt>
582 <dd>ヒント句の対象となるテーブルにIndex Only Scanが可能なインデックスとIndex Only Scanが不可能なインデックスが存在する場合、Index Only Scanが可能なインデックスをテーブルと併せてIndexOnlyScanヒント句に指定しないとIndex Scanが選択されることがあります。</dd>
583 <dt>NoIndexScanヒント句の注意点(PostgreSQL 9.2以降)</dt>
584 <dd>PostgreSQL 9.2以降でNoIndexScanヒント句を指定した場合は、Index ScanだけでなくIndex Only Scanも選択されません。</dd>
585 </dl>
586
587 <h3>ヒント指定エラーの扱い</h3>
588 <dt>構文エラー</dt>
589 <dd>ヒント句の記述に構文上の誤りがあった場合、pg_hint_planは誤った記述より前のヒント句のみ有効とし、誤った記述以降のヒント句を無視してクエリを実行します。誤りの内容はpg_hint_plan.parse_messagesで指定したレベルでサーバログに記録されます。
590 <ul>
591 <li>ヒント句名を間違っている。</li>
592 <li>オブジェクト指定を正しく括弧で囲っていない。</li>
593 <li>オブジェクト名を空白で区切っていない。</li>
594 </ul>
595 </dd>
596 <dt>オブジェクト指定エラー</dt>
597 <dd>pg_hint_planは、ヒント句対象のオブジェクト指定に誤りがあった場合、pg_hint_planは不正なヒント句のみを無視し、それ以外のヒント句を使ってクエリを実行します。誤りの内容はpg_hint_plan.parse_messagesで指定したレベルでサーバログに記録されます。誤ったオブジェクト指定の例を以下に示します。
598 <ul>
599 <li>クエリ中に同じ名称のテーブル名または別名のテーブルがあり、それに対してヒント句を指定した。</li>
600 <li>結合方式や結合順序のヒント句に同じオブジェクト名を複数回指定した。</li>
601 </ul>
602 </dd>
603 <div class="tips">
604 <span class="strong">構文エラーとオブジェクト指定エラーが同時に起こった場合</span>
605 <p>構文エラーより前のヒント句の中から、オブジェクト指定エラーでないヒント句を適用します。</p>
606 </div><br>
607 <dt>指定するヒント句の種類の重複</dt>
608 <dd>同じオブジェクトに対して同じグループのヒント句を重複して指定した場合は、各グループで最後に指定したヒント句を使用します。</dd>
609 <dt>ネストしたブロックコメント</dt>
610 <dd>pg_hint_planでは、ヒントを指定したブロックコメントにネストしたブロックコメントを含めることができません。ネストしたブロックコメントを含めた場合は、誤った記述に関する情報を出力しますがエラー終了しません。ヒントを無視してクエリを実行します。</dd>
611 <dt>メッセージの出力レベル</dt>
612 <dd>ヒントに誤りがあった場合に出力されるメッセージのレベルは、基本的にはpg_hint_plan.parse_messagesに指定したレベルです。ただし、ヒント句に指定したオブジェクトの長さが識別子の最大長(デフォルトでは63バイト)を超えた際に切り詰めた場合は、NOTICEで出力します。</dd>
613
614 <h3>機能制限</h3>
615 <dt>標準のGUCパラメータの影響</dt>
616 <dd>FROMリストの数がfrom_collapse_limitの設定値以上の場合、またはFROMリストの数がjoin_collapse_limitの設定値より大きい場合は、結合順序のヒント句が無視されます。また、FROMリストの数がgeqo_thresholdの設定値以上の場合は、結合順序のヒント句、および結合方式のヒント句が無視されます。ヒント句が使われるようにするには、これらのGUCパラメータの値を大きくしてください。</dd>
617 <dt>ヒント句で制御できないケース</dt>
618 <dd>pg_hint_planでは、PostgreSQLのプランナが候補としてあげる事ができない実行計画をヒント句に指定しても、その実行計画を生成することはできません。PostgreSQLのプランナが候補としてあげる事ができない実行計画の例を以下に示します。
619 <ul>
620 <li>FULL OUTER JOINではNested Loopは、プランナが実行計画の候補として扱いません。</li>
621 <li>WHERE句やJOIN条件などに指定していない列のみを含むインデックスは、プランナが実行計画の候補として扱いません。</li>
622 <li>検索条件にctidを指定しない場合はTid Scanは、プランナが実行計画の候補として扱いません。</li>
623 </ul>
624 </dd>
625 <dt>ECPGにおける制限</dt>
626 <dd>ECPGで実装したアプリケーションから発行するクエリにヒントをコメントで指定した場合、実行計画を制御できません。これは、CプリプロセッサがCコードに変換するタイミングで、全てのブロックコメントを取り除いてしまうためです。ただし、C言語文字列に格納したクエリをEXECUTEコマンドで実行する動的SQLの場合は、コメントでヒントを指定しても実行計画を制御できます。</dd>
627 <dt>ヒントによるフィンガープリントの変化</dt>
628 <dd>SQLコメントでヒントを指定した場合、SQL文フィンガープリントベースのクエリキャッシュなどでは、ヒントが異なれば別のSQL文として扱われます。pg_stat_statementも9.1では別クエリとして集計しますが、9.2ではクエリ集約機能によりコメントが除去されるので、コメントで指定したヒントのみが異なるクエリは同じクエリとして扱われます。</dd>
629
630 </dl>
631
632 <h2 id="requirement">動作環境</h2>
633 <dl>
634 <dt>PostgreSQL</dt>
635   <dd>バージョン 9.1.4、9.2.1</dd>
636 <dt>動作検証済みOS</dt>
637   <dd>RHEL 6.1</dd>
638 </dl>
639
640 <h2 id="seealso">関連項目</h2>
641 <h3 id="postgresql_document">PostgreSQLドキュメント</h3>
642 <a href="http://www.postgresql.jp/document/current/html/sql-explain.html">EXPLAIN</a>
643 <a href="http://www.postgresql.jp/document/current/html/sql-set.html">SET</a>
644 <a href="http://www.postgresql.jp/document/current/html/runtime-config.html">サーバの設定</a>
645 <hr>
646
647 <div class="navigation">
648   <a href="pg_hint_plan-ja.html">pg_hint_plan</a>
649 </div>
650
651 <p class="footer">Copyright (c) 2012-2013, NIPPON TELEGRAPH AND TELEPHONE CORPORATION</p>
652
653 <!--
654 <script type="text/javascript">
655 var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
656 document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
657 </script><script src="pg_statsinfo-ja_files/ga.js" type="text/javascript"></script>
658 <script type="text/javascript">
659 try{
660 var pageTracker = _gat._getTracker("UA-10244036-6");
661 pageTracker._trackPageview();
662 } catch(err) {}
663 </script>
664 -->
665 </body>
666 </html>