我正在使用plpgsql并组装了一个组装动态查询的函数。我测试了它并执行(我已经包含了一个测试包装器来输出已组装的查询)。
我磕磕绊绊的是在EXECUTE命令运行后捕获它的输出,因为我想返回部分或全部值,具体取决于动态查询的性质。我已经设置了一个类型userprofile,并让setProfileDynamic函数返回此类型。
使用完整的参数补丁,输出检出(第二个查询除外,稍微多一点)。但是当缺少某些参数时(即并非所有用户首选项都被更新,只有一个例如:measureystem),则输出已损坏,因此measuresystem_id可能在输出中显示为用户名。
其次,是如何将第二个查询(updateDefaultMealplan)的结果导入userprofile类型(其中,mealplan_id和mealplan_name列耐心等待)。目前此查询返回到mp_id(如果存在'defaultmealplan'键,则从_values数组填充mp_name。)
我对此很陌生,我可能会尝试在一个函数中做太多,而且我可能完全以错误的方式完成它,所以我不介意任何修正可能会通过。
userprofile类型:
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'触发第二个查询。 “username”,“measureystem”,“blanksymbol”或“defaultmealplan”中的任何一个都应该将值返回到userprofile类型中。感谢任何即将发布的反馈。
答案 0 :(得分:1)
基本问题是你的动态查询没有返回所有必要的列,第二个问题 - 你可能期望,但它不是有效的期望,因此记录被赋予相关字段的名称。但是当你为某些复合类型指定一些值时,postgres不会检查名称 - 只有顺序很重要。因此,您必须使用NULL来填充空白并返回所有字段。
您可以使用数组连接来简化代码
DECLARE _return_cols text[] = '{}'; BEGIN _return_cols := _return_cols || quote_ident('some_column'); _return_cols := _return_cols || quote_ident('some_other_column'); ...