ヒントの詳細

構文と配置

pg_hint_planは最初のブロックコメントのみからヒントを読み、アルファベット、数字、スペース、アンダースコア、カンマ、括弧の文字以外は構文解析を停止します。以下の例では、HashJoin(a b)とSeqScan(a)はヒントとして構文解析されますが、IndexScan(a)とMergeJoin(a b)はヒントとして構文解析されません。

=# /*+
     HashJoin(a b)
     SeqScan(a)
    */
   /*+ IndexScan(a) */
   EXPLAIN SELECT /*+ MergeJoin(a b) */ *
     FROM pgbench_branches b
     JOIN pgbench_accounts a ON b.bid = a.bid
     ORDER BY a.aid;
                                      QUERY PLAN
---------------------------------------------------------------------------------------
 Sort  (cost=31465.84..31715.84 rows=100000 width=197)
   Sort Key: a.aid
   ->  Hash Join  (cost=1.02..4016.02 rows=100000 width=197)
         Hash Cond: (a.bid = b.bid)
         ->  Seq Scan on pgbench_accounts a  (cost=0.00..2640.00 rows=100000 width=97)
         ->  Hash  (cost=1.01..1.01 rows=1 width=100)
               ->  Seq Scan on pgbench_branches b  (cost=0.00..1.01 rows=1 width=100)
(7 rows)

PL/pgSQLでの使用

pg_hint_planはPL/pgSQLスクリプト内のクエリに対してはいくつかの制限付きで動作します。

  • ヒントは以下のような種類のクエリにのみ影響します。

    • ・1行を返すクエリ (SELECT、INSERT、UPDATE、DELETE)

    • ・複数行を返すクエリ (RETURN QUERY)

    • ・動的なSQL文 (EXECUTE)

    • ・カーソルを開く (OPEN)

    • ・クエリの結果をループ (FOR)

  • ヒントコメントは次のようにクエリの最初の単語の後に配置する必要があります。クエリよりも先行するコメントはクエリの一部として送信されません。

=# CREATE FUNCTION hints_func(integer) RETURNS integer AS $$
   DECLARE
     id  integer;
     cnt integer;
   BEGIN
     SELECT /*+ NoIndexScan(a) */ aid
       INTO id FROM pgbench_accounts a WHERE aid = $1;
     SELECT /*+ SeqScan(a) */ count(*)
       INTO cnt FROM pgbench_accounts a;
     RETURN id + cnt;
   END;
   $$ LANGUAGE plpgsql;

オブジェクト名の大文字と小文字の区別

PostgreSQLがオブジェクト名を扱う方法とは異なり、pg_hint_planはヒントに含まれるオブジェクト名とデータベース内部のオブジェクト名を大文字・小文字を区別して比較します。したがって、ヒント内のオブジェクト名TBLはデータベース内の "TBL "にのみマッチし、TBL、tbl、Tblのような引用符のない名前にはマッチしません。

オブジェクト名の特殊文字のエスケープ

ヒントのパラメータに指定されたオブジェクト名に括弧、二重引用符、空白を含む場合、二重引用符で囲む必要があります。エスケープのルールはPostgreSQLと同じです。

複数出現するテーブルの区別

pg_hint_planは別名が存在する場合、それを使用して対象オブジェクトを特定します。この動作は、1つのテーブルが複数出現する中から特定のものを指定するのに便利です。

=# /*+ HashJoin(t1 t1) */
   EXPLAIN SELECT * FROM s1.t1
     JOIN public.t1 ON (s1.t1.id=public.t1.id);
INFO:  hint syntax error at or near "HashJoin(t1 t1)"
DETAIL:  Relation name "t1" is ambiguous.
...
=# /*+ HashJoin(pt st) */
   EXPLAIN SELECT * FROM s1.t1 st
     JOIN public.t1 pt ON (st.id=pt.id);
                             QUERY PLAN
---------------------------------------------------------------------
 Hash Join  (cost=64.00..1112.00 rows=28800 width=8)
   Hash Cond: (st.id = pt.id)
   ->  Seq Scan on t1 st  (cost=0.00..34.00 rows=2400 width=4)
   ->  Hash  (cost=34.00..34.00 rows=2400 width=4)
         ->  Seq Scan on t1 pt  (cost=0.00..34.00 rows=2400 width=4)

ビューまたはルールの根底にあるテーブル

ヒントはビュー自体には適用されませんが、オブジェクト名がビュー上に展開されたクエリ内のオブジェクト名と一致する場合、ビュー内のクエリに影響を与えることができます。ビュー内のテーブルに別名を割り当てると、ビューの外からそれらを操作することができます。

=# CREATE VIEW v1 AS SELECT * FROM t2;
=# EXPLAIN /*+ HashJoin(t1 v1) */
          SELECT * FROM t1 JOIN v1 ON (c1.a = v1.a);
                            QUERY PLAN
------------------------------------------------------------------
 Hash Join  (cost=3.27..18181.67 rows=101 width=8)
   Hash Cond: (t1.a = t2.a)
   ->  Seq Scan on t1  (cost=0.00..14427.01 rows=1000101 width=4)
   ->  Hash  (cost=2.01..2.01 rows=101 width=4)
         ->  Seq Scan on t2  (cost=0.00..2.01 rows=101 width=4)

継承

ヒントは継承ツリーの親だけを指すことができ、そのヒントはすべての継承ツリーに影響を与えます。継承された子を直接指定するヒントは無効です。

マルチステートメントにおけるヒント

マルチステートメントには1つのヒントコメントを指定することができ、そのヒントはマルチステートメント内のすべてのステートメントに影響を与えます。

VALUES式

FROM句のVALUES式は内部的には*VALUES*と名付けられ、それがクエリ内の唯一のVALUESであればヒントを使用することができます。2つ以上のVALUES式が含まれるクエリでは、EXPLAINの結果を見て区別することが出来ず、曖昧な結果になります。

=# /*+ MergeJoin(*VALUES*_1 *VALUES*) */
   EXPLAIN SELECT * FROM (VALUES (1, 1), (2, 2)) v (a, b)
     JOIN (VALUES (1, 5), (2, 8), (3, 4)) w (a, c) ON v.a = w.a;
INFO:  pg_hint_plan: hint syntax error at or near "MergeJoin(*VALUES*_1 *VALUES*) "
DETAIL:  Relation name "*VALUES*" is ambiguous.
                               QUERY PLAN
-------------------------------------------------------------------------
 Hash Join  (cost=0.05..0.12 rows=2 width=16)
   Hash Cond: ("*VALUES*_1".column1 = "*VALUES*".column1)
   ->  Values Scan on "*VALUES*_1"  (cost=0.00..0.04 rows=3 width=8)
   ->  Hash  (cost=0.03..0.03 rows=2 width=8)
         ->  Values Scan on "*VALUES*"  (cost=0.00..0.03 rows=2 width=8)

副問い合わせ

サブクエリのコンテキストはANY_subqueryという名前を使用しヒントにすることができる場合があります。

IN (SELECT ... {LIMIT | OFFSET ...} ...)
= ANY (SELECT ... {LIMIT | OFFSET ...} ...)
= SOME (SELECT ... {LIMIT | OFFSET ...} ...)

これらの構文では副問い合わせを含むテーブルの結合を計画する際に、プランナは副問い合わせに対し内部的に名前を割り当てます。そのため、結合方法ヒントは暗黙の名前を使用している結合に適用することができます。以下は例です。

=# /*+HashJoin(a1 ANY_subquery)*/
   EXPLAIN SELECT *
     FROM pgbench_accounts a1
   WHERE aid IN (SELECT bid FROM pgbench_accounts a2 LIMIT 10);
                                         QUERY PLAN

---------------------------------------------------------------------------------------------
 Hash Semi Join  (cost=0.49..2903.00 rows=1 width=97)
   Hash Cond: (a1.aid = a2.bid)
   ->  Seq Scan on pgbench_accounts a1  (cost=0.00..2640.00 rows=100000 width=97)
   ->  Hash  (cost=0.36..0.36 rows=10 width=4)
         ->  Limit  (cost=0.00..0.26 rows=10 width=4)
               ->  Seq Scan on pgbench_accounts a2  (cost=0.00..2640.00 rows=100000 width=4)

IndexOnlyScanヒントの使用

IndexOnlyScanヒントで指定されたインデックスでインデックスオンリースキャンを実行できない場合、インデックススキャンは予期せず別のインデックスで実行されることがあります。

NoIndexScanについて

NoIndexScanヒントはNoIndexOnlyScanを含んでいます。

ParallelヒントとUNION

UNIONが並列に実行できるのは、その下にあるすべてのサブクエリの並列実行が安全である場合のみです。したがってサブクエリのいずれかに並列実行を強制することで、並列実行可能なUNIONが並列で実行されます。一方、ワーカーがゼロのPARALLELヒントはスキャンの並列実行を禁止します。

Set ヒントによるpg_hint_planのパラメータ設定

・pg_hint_planのパラメータはそれ自体の動作を変更するため、一部のパラメータは期待通りに動作しません。

  • ・enable_hint, enable_hint_tableを変更するヒントは、デバッグログに"used hints"として報告されても無視されます。

  • ・debug_printとmessage_levelの設定は、対象クエリの処理の途中から動作します。

DisableIndexヒントの使用

DisableIndexヒントは、指定されたインデックスがクエリ計画中に考慮されないように排除します。他のヒントよりも優先されます。無効化されたインデックスは、明示的にIndexScan で要求された場合でも使用されません。

=# /*+DisableIndex(t t_c1) IndexScan(t t_c1) */
   EXPLAIN SELECT * FROM t WHERE c1 = 1;
LOG:  indexes disabled for DisableIndex(t): t_c1
LOG:  available indexes for IndexScan(t):
LOG:  pg_hint_plan:
used hint:
DisableIndex(t t_c1)
not used hint:
IndexScan(t t_c1)
duplication hint:
error hint:

                           QUERY PLAN
-----------------------------------------------------------------
 Index Scan using t_pkey on t  (cost=0.15..8.17 rows=1 width=12)
   Index Cond: (c1 = 1)
(2 rows)