Dav*_*dEG 12 postgresql types stored-procedures composite plpgsql
鉴于此类型:
-- Just for testing purposes:
CREATE TYPE testType as (name text)
Run Code Online (Sandbox Code Playgroud)
我可以使用此函数动态获取字段的值:
CREATE OR REPLACE FUNCTION get_field(object anyelement, field text) RETURNS text as
$BODY$
DECLARE
value text;
BEGIN
EXECUTE 'SELECT $1."' || field || '"'
USING object
INTO value;
return value;
END;
$BODY$
LANGUAGE plpgsql
Run Code Online (Sandbox Code Playgroud)
呼叫get_field('(david)'::testType, 'name')
按预期工作返回"大卫".
但是如何在复合类型中设置字段的值?我试过这些功能:
CREATE OR REPLACE FUNCTION set_field_try1(object anyelement, field text, value text)
RETURNS anyelement
as
$BODY$
DECLARE
value text;
BEGIN
EXECUTE '$1."' || field || '" := $2'
USING object, value;
return object;
END;
$BODY$
LANGUAGE plpgsql
CREATE OR REPLACE FUNCTION set_field_try2(object anyelement, field text, value text)
RETURNS anyelement
as
$BODY$
DECLARE
value text;
BEGIN
EXECUTE 'SELECT $1 INTO $2."' || field || '"'
USING value, object;
return object;
END;
$BODY$
LANGUAGE plpgsql
CREATE OR REPLACE FUNCTION set_field_try3(object anyelement, field text, value text)
RETURNS anyelement
as
$BODY$
DECLARE
value text;
BEGIN
EXECUTE 'BEGIN $1."' || field || '" := $2; SELECT $1; END;'
INTO object
USING value, object;
return object;
END;
$BODY$
LANGUAGE plpgsql
Run Code Online (Sandbox Code Playgroud)
和一些变化.通话set_field_tryX
无效.我总是得到"错误:语法错误在...附近".我怎么能做到这一点?
笔记:
anyelement
,字段可以是复合类型中的任何字段.我不能只使用object.name.Erw*_*ter 14
hstore
自Postgres 9.0以来,在您的数据库中安装了附加模块hstore
,#=
操作员可以使用非常简单快速的解决方案......
record
用匹配的值替换[s]字段hstore
.
要安装模块:
CREATE EXTENSION hstore;
Run Code Online (Sandbox Code Playgroud)
例子:
SELECT my_record #= '"field"=>"value"'::hstore; -- with string literal
SELECT my_record #= hstore(field, value); -- with values
Run Code Online (Sandbox Code Playgroud)
text
显然,值必须投射到后面.
示例plpgsql函数以及更多详细信息:
json
在Postgres中有类似的,但目前未记录的(如第9.5页)解决方案json
(pg 9.3+)或jsonb
(pg 9.4+),因此您不需要额外的模块.
hstore
和json
如果您使用的是旧版本或无法安装附加模块hstore
或无法假设已安装,则以下是我之前发布的改进版本.但仍然比hstore
运营商慢:
CREATE OR REPLACE FUNCTION f_setfield(INOUT _comp_val anyelement
, _field text, _val text)
RETURNS anyelement AS
$func$
BEGIN
EXECUTE 'SELECT ' || array_to_string(ARRAY(
SELECT CASE WHEN attname = _field
THEN '$2'
ELSE '($1).' || quote_ident(attname)
END AS fld
FROM pg_catalog.pg_attribute
WHERE attrelid = pg_typeof(_comp_val)::text::regclass
AND attnum > 0
AND attisdropped = FALSE
ORDER BY attnum
), ',')
USING _comp_val, _val
INTO _comp_val;
END
$func$ LANGUAGE plpgsql STABLE;
Run Code Online (Sandbox Code Playgroud)
呼叫:
CREATE TEMP TABLE t( a int, b text); -- Composite type for testing
SELECT f_setfield(NULL::t, 'a', '1');
Run Code Online (Sandbox Code Playgroud)
_val
不需要将值显式转换为目标数据类型,动态查询中的字符串文字将自动强制转换,从而避免使用子查询pg_type
.但我更进了一步:
quote_literal(_val)
通过该USING
子句替换为直接值插入.保存一个函数调用和两个强制转换,无论如何都更安全.text
在现代PostgreSQL中自动强制转换为目标类型.(未使用9.1之前的版本进行测试.)
array_to_string(ARRAY())
比...更快string_agg()
.
不需要变量,没有DECLARE
.分配更少.
动态SQL中没有子查询.($1).field
是比较快的.
pg_typeof(_comp_val)::text::regclass
与
(SELECT typrelid FROM pg_catalog.pg_type WHERE oid = pg_typeof($1)::oid)
有效的复合类型相同,只是更快.
最后一个修改建立在pg_type.typname
与pg_class.relname
注册复合类型的关联始终相同的假设之上,双重转换可以替换子查询.我在一个大型数据库中运行此测试以进行验证,并且它按预期显示为空:
SELECT *
FROM pg_catalog.pg_type t
JOIN pg_namespace n ON n.oid = t.typnamespace
WHERE t.typrelid > 0 -- exclude non-composite types
AND t.typrelid IS DISTINCT FROM
(quote_ident(n.nspname ) || '.' || quote_ident(typname))::regclass
Run Code Online (Sandbox Code Playgroud)INOUT
参数的使用消除了对显式的需要RETURN
.这只是一个符号捷径.帕维尔不喜欢它,他更喜欢明确的RETURN
声明......
所有东西放在一起几乎是以前版本的两倍.
结果是版本快了〜2.25倍.但是如果没有Pavel的第二个版本,我可能无法做到.
此外,这个版本通过在单个查询中执行所有操作来避免大部分转换为文本和返回,因此它应该更不容易出错.
使用PostgreSQL 9.0和9.1进行测试.
CREATE FUNCTION f_setfield(_comp_val anyelement, _field text, _val text)
RETURNS anyelement AS
$func$
DECLARE
_list text;
BEGIN
_list := (
SELECT string_agg(x.fld, ',')
FROM (
SELECT CASE WHEN a.attname = $2
THEN quote_literal($3) || '::'|| (SELECT quote_ident(typname)
FROM pg_catalog.pg_type
WHERE oid = a.atttypid)
ELSE quote_ident(a.attname)
END AS fld
FROM pg_catalog.pg_attribute a
WHERE a.attrelid = (SELECT typrelid
FROM pg_catalog.pg_type
WHERE oid = pg_typeof($1)::oid)
AND a.attnum > 0
AND a.attisdropped = false
ORDER BY a.attnum
) x
);
EXECUTE 'SELECT ' || _list || ' FROM (SELECT $1.*) x'
USING $1
INTO $1;
RETURN $1;
END
$func$ LANGUAGE plpgsql STABLE;
Run Code Online (Sandbox Code Playgroud)
我写了第二版的setfield函数.它适用于postgres 9.1我没有在旧版本上测试它.这不是一个奇迹(从性能角度来看),但它更强大,比前一个快8倍.
CREATE OR REPLACE FUNCTION public.setfield2(anyelement, text, text)
RETURNS anyelement
LANGUAGE plpgsql
AS $function$
DECLARE
_name text;
_values text[];
_value text;
_attnum int;
BEGIN
FOR _name, _attnum
IN SELECT a.attname, a.attnum
FROM pg_catalog.pg_attribute a
WHERE a.attrelid = (SELECT typrelid
FROM pg_type
WHERE oid = pg_typeof($1)::oid)
AND a.attnum > 0
LOOP
IF _name = $2 THEN
_value := $3;
ELSE
EXECUTE 'SELECT (($1).' || quote_ident(_name) || ')::text' INTO _value USING $1;
END IF;
_values[_attnum] := COALESCE('"' || replace(replace(_value, '"', '""'), '''', '''''') || '"', '');
END LOOP;
EXECUTE 'SELECT (' || quote_ident(pg_typeof($1)::text) || ' ''(' || array_to_string(_values,',') || ')'').*' INTO $1;
RETURN $1;
END;
$function$;
Run Code Online (Sandbox Code Playgroud)
更新/警告: Erwin指出,这目前是未记载的,并且手册指出,不可能以这种方式更改记录。
请改用hstore或Pavel的解决方案。
这个基于json的简单解决方案几乎与hstore一样快,并且仅需要Postgres 9.3或更高版本。如果您不能使用hstore扩展,那么这应该是一个不错的选择,并且性能差异可以忽略不计。基准:https : //stackoverflow.com/a/28673542/1914376
a)我们可以通过cast / concat内联完成。Json函数需要Postgres 9.3:
SELECT json_populate_record(
record
, ('{"'||'key'||'":"'||'new-value'||'"}')::json
);
Run Code Online (Sandbox Code Playgroud)
b)或通过使用Postgres 9.4中的函数进行内联。
SELECT json_populate_record (
record
,json_object(ARRAY['key', 'new-value'])
);
Run Code Online (Sandbox Code Playgroud)
注意:我选择json_object(ARRAY [key,value])是因为它比json_build_object(key,value)快一点:
要隐藏转换细节,您可以在函数中使用a),而开销很小。
CREATE FUNCTION x.setfield_json(in_element anyelement, key text, value text)
RETURNS anyelement AS
$BODY$
SELECT json_populate_record( in_element, ('{"'||key||'":"'||value||'"}')::json);
$BODY$ LANGUAGE sql;
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
16807 次 |
最近记录: |