sql >> データベース >  >> RDS >> PostgreSQL

SQLクエリで(func())。*構文を使用して複数の関数評価を回避するにはどうすればよいですか?

    サブクエリでまとめることはできますが、OFFSET 0がないと安全が保証されません。 ハック。 9.3では、LATERALを使用します 。この問題は、パーサーが効果的に*をマクロ拡張することによって発生します。 列リストに追加します。

    回避策

    場所:

    SELECT (my_func(x)).* FROM some_table;
    

    my_funcを評価します n nの回数 関数の結果列、この定式化:

    SELECT (mf).* FROM (
        SELECT my_func(x) AS mf FROM some_table
    ) sub;
    

    通常、実行時にスキャンを追加することはなく、追加しない傾向があります。複数の評価が実行されないことを保証するために、OFFSET 0を使用できます。 PostgreSQLがCTEの境界を越えて最適化できなかったことをハッキングまたは悪用する:

    SELECT (mf).* FROM (
        SELECT my_func(x) AS mf FROM some_table OFFSET 0
    ) sub;
    

    または:

    WITH tmp(mf) AS (
        SELECT my_func(x) FROM some_table
    )
    SELECT (mf).* FROM tmp;
    

    PostgreSQL 9.3では、LATERALを使用できます。 正気の行動をとるには:

    SELECT mf.*
    FROM some_table
    LEFT JOIN LATERAL my_func(some_table.x) AS mf ON true;
    

    LEFT JOIN LATERAL ... ON true 関数呼び出しが行を返さない場合でも、元のクエリと同様にすべての行を保持します。

    デモ

    デモンストレーションとしてインライン化できない関数を作成します:

    CREATE OR REPLACE FUNCTION my_func(integer)
    RETURNS TABLE(a integer, b integer, c integer) AS $$
    BEGIN
        RAISE NOTICE 'my_func(%)',$1;
        RETURN QUERY SELECT $1, $1, $1;
    END;
    $$ LANGUAGE plpgsql;
    

    およびダミーデータのテーブル:

    CREATE TABLE some_table AS SELECT x FROM generate_series(1,10) x;
    

    次に、上記のバージョンを試してください。最初の通知では、呼び出しごとに3つの通知が発生することがわかります。後者は1つだけ上げます。

    なぜですか?

    良い質問。ひどいです。

    次のようになります:

    (func(x)).*
    

    次のように展開されます:

    (my_func(x)).i, (func(x)).j, (func(x)).k, (func(x)).l
    

    debug_print_parseを見ると、構文解析中 、debug_print_rewritten およびdebug_print_plan 。 (トリミングされた)解析ツリーは次のようになります:

       :targetList (
          {TARGETENTRY 
          :expr 
             {FIELDSELECT 
             :arg 
                {FUNCEXPR 
                :funcid 57168 
                     ...
                }
             :fieldnum 1 
             :resulttype 23 
             :resulttypmod -1 
             :resultcollid 0
             }
          :resno 1 
          :resname i 
           ...
          }
          {TARGETENTRY 
          :expr 
             {FIELDSELECT 
             :arg 
                {FUNCEXPR 
                :funcid 57168 
                     ...
                }
             :fieldnum 2 
             :resulttype 20 
             :resulttypmod -1 
             :resultcollid 0
             }
          :resno 2 
          :resname j 
           ...
          }
          {TARGETENTRY 
          :expr 
             {FIELDSELECT 
             :arg 
                {FUNCEXPR 
                :funcid 57168 
                 ...
                }
             :fieldnum 3 
             :...
             }
          :resno 3 
          :resname k 
           ...
          }
          {TARGETENTRY 
          :expr 
             {FIELDSELECT 
             :arg 
                {FUNCEXPR 
                :funcid 57168 
                 ...
                }
             :fieldnum 4 
              ...
             }
          :resno 4 
          :resname l 
           ...
          }
       )
    

    つまり、基本的には、ダムパーサーハックを使用して、ノードのクローンを作成することでワイルドカードを拡張しています。




    1. SQLServerイベントをキャプチャして分析する方法

    2. パッチはどこにありますか?

    3. 断続的なORA-12519(TNS:適切なハンドラーが見つかりません)エラーの原因

    4. MariaDBで日時に議事録を追加する8つの方法