1

提供されたパラメーターに基づいて条件付きでデータを削除する関数を作成しようとしています。デフォルトのパラメーターを使用できるようにしたいので、明示的に指定されていない場合、関数はフィルタリングを実行しません。2つのフィルタリングタイプの最初のタイプは時間範囲であり、2番目のタイプは主キーの配列です。

時間範囲については、デフォルトのセンチネル変数を使用できます+/- infinity。これはすべてのデータと一致し、本質的にフィルターを無効にします。これらはpostgresによって提供されます。配列ベースのフィルターを無効にするために使用できるもの(ある種の番兵)はありますか?

具体的には、関数に整数の配列を渡しますが、デフォルトでこのフィルターを無効にします(つまり、配列を渡さない場合)。明らかな選択は、デフォルトのパラメータとして配列を空の配列に設定することです。ただし、where in空の配列では何にも一致しません(空の配列が有効なplpgqlでさえある場合)。したがって、明らかな選択は、IFステートメントまたは動的SQLを使用することです。しかし、postgresを知っていると、私が見逃したかもしれないトリックや構文が必要です。

編集:

私の最初の計画は、私が望む動作をエミュレートするブールUDFを作成することでした。の線に沿った何か:

CREATE OR REPLACE FUNCTION filter_category(
    category_array  INTEGER[],
    current_category INTEGER
)
RETURNS 
BOOLEAN AS $$
BEGIN
    IF category_array = '{}' THEN
        RETURN TRUE;
    ELSE
        --  if in set return true, else false. 
        RETURN FALSE;
    END IF;
END;
$$ LANGUAGE plpgsql;

ただし、この問題についてさらに検討した後、クエリはより複雑になる必要があると思います。節、または上記のwhere in array関数は、おそらくあまり効率的ではありません。したがって、存在する可能性のあるインデックスを使用することになるには、主キーの配列をアンネストして結合を実行する必要があります。

そして、配列がnullでない場合にのみこれを行う必要があります。したがって、クエリには少なくとも1つの `IF ... ELSE ... ENDIF'ステートメントが必要です。さらにフィルタリングを行う必要がある場合は、中間のローカルデータセットを実装する必要があるかもしれません。それでも私はアドバイスに興味があります:D

編集2:

私の動的クエリは次のようになります。

CREATE OR REPLACE FUNCTION get_paginated_search_results(
    forum_id_ INTEGER,
    query_    CHARACTER VARYING,
    from_date_ TIMESTAMP WITHOUT TIME ZONE DEFAULT NULL,
    to_date_ TIMESTAMP WITHOUT TIME ZONE DEFAULT NULL,
    in_categories_ INTEGER[] DEFAULT '{}')
RETURNS SETOF post_result_entry AS $$
DECLARE
    join_string CHARACTER VARYING := ' ';
    from_where_date CHARACTER VARYING := ' ';
    to_where_date CHARACTER VARYING := ' ';
    query_string_ CHARACTER VARYING := ' ';
BEGIN
    IF NOT from_date_ IS NULL THEN
        from_where_date := ' AND fp.posted_at > ''' || from_date_ || '''';
    END IF;

    IF NOT to_date_ IS NULL THEN
        to_where_date := ' AND fp.posted_at < ''' || to_date_ || '''';
    END IF;

    CREATE LOCAL TEMP TABLE un_cat(id) ON COMMIT DROP AS (select * from unnest(in_categories_)) ;

    if in_categories_ != '{}' THEN
        join_string := ' INNER JOIN forum_topics ft ON fp.topic_id = ft.id ' ||
        ' INNER JOIN un_cat uc ON uc.id = ft.category_id ' ;
    END IF;

    query_string_ := '
    SELECT index,posted_at,post_text,name,join_date,quotes
    FROM forum_posts fp
    INNER JOIN forum_user fu ON
    fu.forum_id = fp.forum_id AND fu.id = fp.user_id' ||
        join_string
    ||
    'WHERE fu.forum_id = ' || forum_id_ || ' AND
    to_tsvector(''english'',fp.post_text) @@ to_tsquery(''english'','''|| query_||''')' || 
        from_where_date || 
        to_where_date
    ||';';

    RAISE NOTICE '%', query_string_ ;

    RETURN QUERY
    EXECUTE query_string_;
END;
$$ LANGUAGE plpgsql;
4

1 に答える 1

2

空の配列を使用して、次のようにWHERE句にロールインできます。

where ...
  and ($1 = array[]::int[] or id = any ($1))

パラメータ$1が何であれ、どこにありますか。int[]または、NULLを使用して、同じ方法で実行することもできます。

where ...
  and ($1 is null or id = any ($1))

「-infinityと+infinityの間」のクエリでEXPLAIN出力を確認しますが、オプティマイザはそれをトートロジーとして認識しない可能性があります(NULLがないため、実際にはトートロジーであると想定しています)。あなたの手で無意味なテーブルスキャン。動的SQLまたはIFの醜い山は、より良いサーバーになる可能性があります。

于 2012-08-29T04:39:22.120 に答える