1

私はplpgsqlで遊んでいて、動的クエリをアセンブルする関数をまとめました。私はそれをテストし、実行します(アセンブルされたクエリを出力するためのテストラッパーが含まれています)。

私がつまずいているのは、実行されたEXECUTEコマンドの出力をキャプチャすることです。これは、動的クエリの性質に応じて、値の一部またはすべてを返したいためです。タイプuserprofileを設定し、setProfileDynamic関数がこのタイプを返すようにしました。

パラメータを完全に補完すると、出力がチェックアウトされます(2番目のクエリを除いて、これについては後で詳しく説明します)。ただし、一部のパラメーターが欠落している場合(つまり、すべてのユーザー設定が更新されていない場合、たとえば、measuresystemの1つのみ)、出力が破損しているため、measuresystem_idが出力にユーザー名として表示される場合があります。

次に、2番目のクエリ(updateDefaultMealplan)の結果をuserprofileタイプ(mealplan_id列とmealplan_name列が辛抱強く待っている)に取り込む方法です。現在、このクエリはmp_idに戻ります(「defaultmealplan」キーが存在する場合、mp_nameは_values配列から入力されます)。

私はこれにまったく慣れておらず、1つの関数でやりすぎている可能性があり、完全に間違った方法で実行している可能性があるため、どのような修正が行われるかは気になりません。

ユーザープロファイルの種類:

DROP TYPE IF EXISTS userprofile CASCADE;
CREATE TYPE userprofile AS (
    username text,
    measuresystem_id int,
    blanksymbol_id int,
    mealplan_id int,
    mealplan_name text
);

主な機能

DROP FUNCTION IF EXISTS setProfileDynamic (int, text, text[], text[]);
CREATE OR REPLACE FUNCTION setProfileDynamic (_userid int, _token text, _keys text[], _values text[])
RETURNS userprofile AS $$
DECLARE
    _query text;
    numkeys int;
    i int;

    _update text[];
    _from text[];
    _where text[];
    _return text[];
    _into text[];
    test text[];

    up userprofile;
    mp_name text;
    mp_id int;

    u text;
    f text;
    w text;
    r text;

    c_update int := 1;
    c_from int := 1;
    c_where int := 3;
    c_return int := 1;

    runupdate boolean := false; --bc passing default mealplan through this fn too.
    changedefaultmp boolean := false;
 BEGIN
    test[1] := 'users.id';
    test[2] := 'users.token';
    test[3] := _userid;
    test[4] := _token;
    numkeys := array_length(_keys, 1);
    raise notice 'numkeys = %', numkeys;
    _where[1] := test[1] || ' = ' || quote_literal(test[3]);
    _where[2] :=  test[2] || ' = ' || quote_literal(test[4]);
    --raise notice '_where[1] = %', _where[1];
    --raise notice '_where[2] = %', _where[2];
    for i in 1..numkeys loop
        raise notice 'keys[%] = %', i, _keys[i];
        CASE _keys[i]
            WHEN 'email' THEN 
                runupdate := true;
                _update[c_update] := quote_ident(_keys[i]) || ' = ' || quote_literal(_values[i]);
                c_update := c_update + 1;
            WHEN 'password' THEN
                runupdate := true;
                _update[c_update] := quote_ident(_keys[i]) || ' = ' || quote_literal(_values[i]);
                c_update := c_update + 1;
            WHEN 'username' THEN
                runupdate := true;
                _update[c_update] := quote_ident(_keys[i]) || ' = ' || quote_literal(_values[i]);
                c_update := c_update + 1;
                _return[c_return] := quote_ident(_keys[i]);
                c_return := c_return + 1;
            WHEN 'measuresystem' THEN
                runupdate := true;
                _update[c_update] := 'measuresystem_id = ms.id';
                c_update := c_update + 1;
                _from[c_from] := 'measuresystem as ms';
                c_from := c_from + 1;
                _where[c_where] := 'ms.name = ' || quote_literal(_values[i]);
                c_where := c_where + 1;
                _return[c_return] := 'ms.id';
                c_return := c_return + 1;
            WHEN 'blanksymbol' THEN
                runupdate := true;
                _update[c_update] := 'blanksymbol_id = bs.id';
                c_update := c_update + 1;
                _from[c_from] := 'blanksymbol as bs';
                c_from := c_from + 1;
                _where[c_where] := 'bs.name = ' || quote_literal(_values[i]);
                c_where := c_where + 1;
                _return[c_return] := 'bs.id';
                c_return := c_return + 1;
            ELSE
                changedefaultmp := true;
                mp_name := _values[i];
        END CASE;
    end loop;
    u := 'UPDATE users SET ' || array_to_string(_update, ', ');
    f := 'FROM ' || array_to_string(_from, ', '); --if a_t_s is null, the whole f is null and not included so no error
    w := 'WHERE ' || array_to_string(_where, ' AND ');
    r := 'RETURNING ' || array_to_string(_return, ', ');

    raise notice 'u = %', u;
    raise notice 'f = %', f;
    raise notice 'w = %', w;
    raise notice 'r = %', r;

    _query = concat_ws(' ', u, f, w, r);
    raise notice '_query = %', _query;
    IF runupdate THEN
        if r IS NULL THEN
            EXECUTE _query;
        ELSE
            EXECUTE _query INTO up;
        END IF;
    END IF;
    IF changedefaultmp THEN
        SELECT into mp_id updateDefaultMealplan(_userid, mp_name);
    END IF;
    return up;
END
$$ LANGUAGE PLPGSQL;

これは、さまざまな入力に対して生成されたクエリを確認できるラッパー関数です。

DROP FUNCTION IF EXISTS T ();
CREATE OR REPLACE FUNCTION T ()
RETURNS setof userprofile AS $$
declare
    _keys text[];
    _values text[];

    _userid int := 1;
    _token text := 'beet';
begin
    _keys := ARRAY['email', 'password', 'username', 'measuresystem', 'blanksymbol', 'defaultmealplan'];
    _values := ARRAY['s@p.com', 'secret', 'myname', 'metric', '?', 'new'];
    --_keys := ARRAY['email', 'blanksymbol'];
    --_values := ARRAY['k@d.com', '[]'];
    return query
        SELECT * from setProfileDynamic(_userid, _token, _keys, _values);
end
$$ LANGUAGE PLPGSQL;

やり遂げるのはたくさんのコードだと思います。T関数が物事を明確にするのに役立つことを願っています。「email」および「password」パラメータが返されません。'defaultmealplan'は、2番目のクエリをトリガーします。'username'、'measuresystem'、'blanksymbol'、または'defaultmealplan'のいずれかは、userprofileタイプに値を返す必要があります。今後のフィードバックに感謝します。

4

1 に答える 1

1

基本的な問題は、動的クエリが必要なすべての列を返さないことです。しかし、いくつかの値を複合型に割り当てると、postgres は名前をチェックしません。重要なのは順序だけです。したがって、ギャップを埋めるために NULL を使用し、すべてのフィールドを返す必要があります。

配列連結でコードを簡素化できます

DECLARE _return_cols text[] = '{}';

始める
  _return_cols := _return_cols || quote_ident('some_column');
  _return_cols := _return_cols || quote_ident('some_other_column');

  ...

于 2013-01-30T09:06:44.290 に答える